1128 lines
44 KiB
C
1128 lines
44 KiB
C
//
|
|
// This program is free software; you can redistribute it and/or modify it
|
|
// under the term of the GNU Lesser General Public License as published by the
|
|
// Free Software Foundation; either version 2 of the License, or (at your
|
|
// option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
|
// for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with this program; if not, write to the Free Software Foundation,
|
|
// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
// _________________
|
|
|
|
// @(#) $Revision$ $Source$
|
|
//
|
|
// JUDY FUNCTIONS FOR STRING INDEXES, where associated values are longs. One
|
|
// JudySL*() corresponds to each JudyL*() function (with exceptions).
|
|
//
|
|
// See the manual entry for details.
|
|
//
|
|
// METHOD: Break up each null-terminated Index (string) into chunks of W
|
|
// bytes, where W is the machines word size, with null-padding in the last
|
|
// word if necessary. Store strings as a tree of JudyL arrays, that is, array
|
|
// of array of array... where each level consumes W bytes (one word) as an
|
|
// index to the JudyL array at that level. Since strings can begin on
|
|
// arbitrary byte boundaries, copy each chunk of W bytes from Index into a
|
|
// word-aligned object before using it as a Judy index.
|
|
//
|
|
// The JudySL tree also supports "single-index shortcut leaves". A simple
|
|
// JudySL array (tree of JudyL arrays) would go as many levels deep as the
|
|
// Index (string) is long, which wastes time and memory when an Index is unique
|
|
// beyond a certain point. When theres just one Index under a pointer, given
|
|
// a reliable way to tell that the pointer is not a root pointer to another
|
|
// JudyL array, it should save a lot of time to instead point to a "leaf"
|
|
// object, similar to leaves in JudyL arrays.
|
|
//
|
|
// TBD: Multi-index leaves, like those in JudyL, are also worth considering,
|
|
// but their payback for JudySL is less certain. Likewise, shortcut branches
|
|
// are worth considering too.
|
|
//
|
|
// This code uses the Judy.h definitions and Doug Baskins convention of a "P"
|
|
// prefix for pointers, except no "P" for the first level of char * (strings).
|
|
|
|
// IMPORTS:
|
|
|
|
#include <string.h> // for strcmp(), strlen(), strcpy()
|
|
#include <Judy.h>
|
|
|
|
#ifndef NDEDUG
|
|
#define NDEBUG 1
|
|
#endif
|
|
#include <assert.h>
|
|
|
|
//=======================================================================
|
|
// Compile:
|
|
//
|
|
// cc -O JudyHS.c -c
|
|
//
|
|
// Notes:
|
|
// 1) use -DJU_64BIT for 64 bit compiles (HP, Sun, IPF, Motorola/IBM? etc..)
|
|
// 2) In gcc version 3.3.1 for a Centrino, -O2 is faster than -O
|
|
// 3) In gcc version 3.3.2 for a Centrino, -O3 is faster than -O2
|
|
//=======================================================================
|
|
|
|
#define JU_SET_ERRNO(PJERROR, JERRNO) \
|
|
{ \
|
|
if (PJERROR != (PJError_t)NULL) \
|
|
{ \
|
|
JU_ERRNO(PJERROR) = (JERRNO); \
|
|
JU_ERRID(PJERROR) = __LINE__; \
|
|
} \
|
|
}
|
|
|
|
#define JU_SET_ERRNO_NONNULL(PJERROR, JERRNO) \
|
|
{ \
|
|
JU_ERRNO(PJERROR) = (JERRNO); \
|
|
JU_ERRID(PJERROR) = __LINE__; \
|
|
}
|
|
|
|
// SUPPORT FOR HANDLING WORDS:
|
|
|
|
#define WORDSIZE (sizeof (Word_t)) // bytes in word = JudyL index.
|
|
#define WORDS(BYTES) (((BYTES) + WORDSIZE - 1) / WORDSIZE) // round up.
|
|
|
|
// To mark a pointer is to a "short cut leaf", set least bit
|
|
|
|
#define IS_PSCL(PSCL) (((Word_t) (PSCL)) & JLAP_INVALID)
|
|
#define CLEAR_PSCL(PSCL) ((Pscl_t)(((Word_t) (PSCL)) & (~JLAP_INVALID)))
|
|
#define SET_PSCL(PSCL) (((Word_t) (PSCL)) | JLAP_INVALID)
|
|
|
|
// MISCELLANEOUS GLOBALS:
|
|
|
|
// Get the Index (string) length in bytes, including the trailing \0, which
|
|
// is an integral part of the string:
|
|
|
|
// A string is "in the last word" if a previously-set byte count is at or below
|
|
// the system word size, or in some cases if the last byte in the (null-padded)
|
|
// word is null (assume big-endian, including in a register on a little-endian
|
|
// machine):
|
|
|
|
#define LASTWORD_BY_VALUE(WORD) (! ((WORD) & 0xffL))
|
|
|
|
#ifdef JU_64BIT
|
|
|
|
// copy from 1..7 bytes from string to Word_t and test if \0 bytes
|
|
//
|
|
#define COPYSTRINGtoWORD(WORD,STR) \
|
|
{ \
|
|
do \
|
|
{ \
|
|
uint8_t chr; \
|
|
WORD = (Word_t)(STR)[0] << 56; \
|
|
if (!(WORD)) break; \
|
|
if (!(chr = (STR)[1])) break; \
|
|
WORD += ((Word_t)(chr) << 48); \
|
|
if (!(chr = (STR)[2])) break; \
|
|
WORD += ((Word_t)(chr) << 40); \
|
|
if (!(chr = (STR)[3])) break; \
|
|
WORD += ((Word_t)(chr) << 32); \
|
|
if (!(chr = (STR)[4])) break; \
|
|
WORD += ((Word_t)(chr) << 24); \
|
|
if (!(chr = (STR)[5])) break; \
|
|
WORD += ((Word_t)(chr) << 16); \
|
|
if (!(chr = (STR)[6])) break; \
|
|
WORD += ((Word_t)(chr) << 8) + (STR)[7]; \
|
|
} while(0); \
|
|
}
|
|
|
|
// copy Word_t from 1..8 bytes to string and test of \0 bytes
|
|
//
|
|
#define COPYWORDtoSTRING(STR,WORD) \
|
|
{ \
|
|
do \
|
|
{ \
|
|
if (!((STR)[0] = (uint8_t)((WORD) >> 56))) break; \
|
|
if (!((STR)[1] = (uint8_t)((WORD) >> 48))) break; \
|
|
if (!((STR)[2] = (uint8_t)((WORD) >> 40))) break; \
|
|
if (!((STR)[3] = (uint8_t)((WORD) >> 32))) break; \
|
|
if (!((STR)[4] = (uint8_t)((WORD) >> 24))) break; \
|
|
if (!((STR)[5] = (uint8_t)((WORD) >> 16))) break; \
|
|
if (!((STR)[6] = (uint8_t)((WORD) >> 8))) break; \
|
|
(STR)[7] = (uint8_t)(WORD); \
|
|
} while(0); \
|
|
}
|
|
|
|
#else // JU_32BIT
|
|
|
|
// copy from 1..4 bytes from string to Word_t and test if \0 bytes
|
|
|
|
#define COPYSTRINGtoWORD(WORD,STR) \
|
|
{ \
|
|
do \
|
|
{ \
|
|
uint8_t chr; \
|
|
WORD = (STR)[0] << 24; \
|
|
if (WORD == 0) break; \
|
|
if (!(chr = (STR)[1])) break; \
|
|
WORD += (Word_t)(chr << 16); \
|
|
if (!(chr = (STR)[2])) break; \
|
|
WORD += (Word_t)(chr << 8) + (STR)[3]; \
|
|
} while(0); \
|
|
}
|
|
|
|
// copy Word_t from 1..4 bytes to string and test of \0 bytes
|
|
|
|
#define COPYWORDtoSTRING(STR,WORD) \
|
|
{ \
|
|
do \
|
|
{ \
|
|
if (!((STR)[0] = (uint8_t)((WORD) >> 24))) break; \
|
|
if (!((STR)[1] = (uint8_t)((WORD) >> 16))) break; \
|
|
if (!((STR)[2] = (uint8_t)((WORD) >> 8))) break; \
|
|
(STR)[3] = (uint8_t)(WORD); \
|
|
} while(0); \
|
|
}
|
|
#endif // JU_32BIT
|
|
|
|
|
|
// SUPPORT FOR SINGLE-INDEX SHORTCUT LEAVES:
|
|
|
|
typedef struct SHORCUTLEAF
|
|
{
|
|
Pvoid_t scl_Pvalue; // callers value area.
|
|
uint8_t scl_Index[WORDSIZE]; // base Index string.
|
|
} scl_t , *Pscl_t;
|
|
|
|
// overhead of the scl_Pvalue only, the scl_Index is calculate elsewhere
|
|
|
|
#define STRUCTOVD (sizeof(scl_t) - WORDSIZE)
|
|
|
|
// How big to malloc a shortcut leaf; stringlen should already include the
|
|
// trailing null char:
|
|
|
|
#define SCLSIZE(LEN) (((LEN) + STRUCTOVD + WORDSIZE - 1) / WORDSIZE)
|
|
|
|
// string routines, may replace with your own
|
|
//
|
|
#define STRCMP(S1,S2) strcmp((void *)(S1), (void *)(S2))
|
|
#define STRCPY(S1,S2) strcpy((void *)(S1), (void *)(S2))
|
|
#define STRLEN(S1) (strlen((void *)(S1)) + 1)
|
|
|
|
|
|
// Index and value area for a shortcut leaf, depending on how it matches the
|
|
// undecoded remainder of the Index, given a Pscl_t that includes type bits
|
|
// that must be cleared:
|
|
//
|
|
// PSCLINDEX() and PSCLVALUE() are also useful when Pscl contains uncleared
|
|
// TYPE bits.
|
|
//
|
|
// Note: SCLCMP() cannot take advantage of knowing the Index length because
|
|
// the scl_Index length is not pre-known when these macros are used.
|
|
|
|
#define PSCLINDEX(PSCL) ((CLEAR_PSCL(PSCL))->scl_Index)
|
|
#define PSCLVALUE(PSCL) ((CLEAR_PSCL(PSCL))->scl_Pvalue)
|
|
|
|
#define SCLCMP(INDEX,PSCL) STRCMP(INDEX, PSCLINDEX(PSCL))
|
|
|
|
#define PPSCLVALUE_EQ(INDEX,PSCL) \
|
|
((SCLCMP(INDEX, PSCL) == 0) ? &PSCLVALUE(PSCL) : (PPvoid_t)NULL)
|
|
|
|
#define PPSCLVALUE_LT(INDEX,PSCL) \
|
|
((SCLCMP(INDEX, PSCL) < 0) ? &PSCLVALUE(PSCL) : (PPvoid_t)NULL)
|
|
|
|
#define PPSCLVALUE_GT(INDEX,PSCL) \
|
|
((SCLCMP(INDEX, PSCL) > 0) ? &PSCLVALUE(PSCL) : (PPvoid_t)NULL)
|
|
|
|
// Common in-lined code to append or free a shortcut leaf:
|
|
//
|
|
// See header comments about premature return(). Note that malloc() does not
|
|
// pre-zero the memory, so ensure scl_Pvalue is zeroed, just like a value area
|
|
// in a JudyL array. Hope strcpy() is fast enough in this context.
|
|
|
|
#define APPEND_SCL(PSCL,PPARRAY,INDEX,LEN,PJERROR) \
|
|
{ \
|
|
if (((PSCL) = (Pscl_t) JudyMalloc(SCLSIZE(LEN))) == (Pscl_t)NULL) \
|
|
{ \
|
|
JU_SET_ERRNO(PJERROR, JU_ERRNO_NOMEM); \
|
|
return (PPJERR); \
|
|
} \
|
|
*(PPARRAY) = (Pvoid_t)SET_PSCL(PSCL); \
|
|
((PSCL)->scl_Pvalue) = (Pvoid_t)NULL; \
|
|
(void)STRCPY((PSCL)->scl_Index, INDEX); \
|
|
}
|
|
|
|
// "FORWARD" DECLARATIONS:
|
|
|
|
static void JudySLModifyErrno(PJError_t PJError,
|
|
Pcvoid_t PArray, Pcvoid_t PArrayOrig);
|
|
static int JudySLDelSub(PPvoid_t PPArray, PPvoid_t PPArrayOrig,
|
|
const uint8_t * Index, Word_t len, PJError_t PJError);
|
|
static PPvoid_t JudySLPrevSub(Pcvoid_t PArray, uint8_t * Index, int orig,
|
|
Word_t len, PJError_t PJError);
|
|
static PPvoid_t JudySLNextSub(Pcvoid_t PArray, uint8_t * Index, int orig,
|
|
Word_t len, PJError_t PJError);
|
|
|
|
// ****************************************************************************
|
|
// J U D Y S L M O D I F Y E R R N O
|
|
//
|
|
// Common code for error translation: When a caller passes an invalid JAP
|
|
// ("not a JudyL pointer"), OR if the JudySL array is corrupted at a lower
|
|
// level, various JudyL*() calls return JU_ERRNO_NOTJUDYL. If the caller wants
|
|
// detailed error info, convert this particular error to JU_ERRNO_NOTJUDYSL if
|
|
// at the top of the tree, otherwise convert it to JU_ERRNO_CORRUPT, meaning
|
|
// there was a corruption (the only one even detectable outside JudyL) in the
|
|
// JudySL tree; but pass through any other errors unaltered.
|
|
|
|
static void
|
|
JudySLModifyErrno(PJError_t PJError, // to modify if non-null.
|
|
Pcvoid_t PArray, // current JudyL array.
|
|
Pcvoid_t PArrayOrig // top-of-tree JudyL array.
|
|
)
|
|
{ // map this Judy errno.
|
|
if ((PJError != PJE0) && (JU_ERRNO(PJError) == JU_ERRNO_NOTJUDYL))
|
|
{
|
|
if (PArray == PArrayOrig) // callers fault.
|
|
{
|
|
JU_SET_ERRNO_NONNULL(PJError, JU_ERRNO_NOTJUDYSL);
|
|
}
|
|
else // lower level.
|
|
{
|
|
JU_SET_ERRNO_NONNULL(PJError, JU_ERRNO_CORRUPT);
|
|
}
|
|
}
|
|
} // JudySLModifyErrno()
|
|
|
|
// ****************************************************************************
|
|
// J U D Y S L G E T
|
|
//
|
|
// See comments in file header and below.
|
|
|
|
PPvoid_t
|
|
JudySLGet(Pcvoid_t PArray, const uint8_t * Index, PJError_t PJError)
|
|
{
|
|
const uint8_t *pos = Index; // place in Index.
|
|
Word_t indexword; // buffer for aligned copy.
|
|
PPvoid_t PPValue; // from JudyL array.
|
|
|
|
// CHECK FOR CALLER ERROR (NULL POINTER):
|
|
|
|
if (Index == (uint8_t *) NULL)
|
|
{
|
|
JU_SET_ERRNO(PJError, JU_ERRNO_NULLPINDEX);
|
|
return (PPJERR);
|
|
}
|
|
|
|
// SEARCH NEXT LEVEL JUDYL ARRAY IN TREE:
|
|
//
|
|
// Use or copy each word from the Index string and check for it in the next
|
|
// level JudyL array in the array tree, but first watch for shortcut leaves.
|
|
// Upon invalid Index or end of Index (string) in current word, return.
|
|
|
|
do // until return.
|
|
{
|
|
if (IS_PSCL(PArray)) // a shortcut leaf.
|
|
return (PPSCLVALUE_EQ(pos, PArray));
|
|
|
|
COPYSTRINGtoWORD(indexword, pos); // copy next 4[8] bytes.
|
|
|
|
JLG(PPValue, PArray, indexword);
|
|
|
|
if ((PPValue == (PPvoid_t) NULL) || LASTWORD_BY_VALUE(indexword))
|
|
return (PPValue);
|
|
|
|
// CONTINUE TO NEXT LEVEL DOWN JUDYL ARRAY TREE:
|
|
//
|
|
// If a previous JudySLIns() ran out of memory partway down the tree, it left a
|
|
// null *PPValue; this is automatically treated here as a dead-end (not a core
|
|
// dump or assertion; see version 1.25).
|
|
|
|
pos += WORDSIZE;
|
|
PArray = *PPValue; // each value -> next array.
|
|
} while(1); // forever
|
|
// NOTREACHED JudySLGet()
|
|
}
|
|
|
|
// ****************************************************************************
|
|
// J U D Y S L I N S
|
|
//
|
|
// See also the comments in JudySLGet(), which is somewhat similar, though
|
|
// simpler.
|
|
//
|
|
// Theory of operation:
|
|
//
|
|
// Upon encountering a null pointer in the tree of JudyL arrays, insert a
|
|
// shortcut leaf -- including directly under a null root pointer for the first
|
|
// Index in the JudySL array.
|
|
//
|
|
// Upon encountering a pre-existing shortcut leaf, if the old Index is equal to
|
|
// the new one, return the old value area. Otherwise, "carry down" the old
|
|
// Index until the old and new Indexes diverge, at which point each Index
|
|
// either terminates in the last JudyL array or a new shortcut leaf is inserted
|
|
// under it for the Indexs remainder.
|
|
//
|
|
// TBD: Running out of memory below the starting point causes a premature
|
|
// return below (in several places) and leaves a dead-end in the JudySL tree.
|
|
// Ideally the code here would back this out rather than wasting a little
|
|
// memory, but in lieu of that, the get, delete, and search functions
|
|
// understand dead-ends and handle them appropriately.
|
|
|
|
PPvoid_t
|
|
JudySLIns(PPvoid_t PPArray, const uint8_t * Index, PJError_t PJError)
|
|
{
|
|
PPvoid_t PPArrayOrig = PPArray; // for error reporting.
|
|
const uint8_t *pos = Index; // place in Index.
|
|
const uint8_t *pos2 = (uint8_t *) NULL; // old Index (SCL being moved).
|
|
Word_t len; // bytes remaining.
|
|
|
|
// Note: len2 is set when needed and only used when valid, but this is not
|
|
// clear to gcc -Wall, so initialize it here to avoid a warning:
|
|
|
|
Word_t len2 = 0; // for old Index (SCL being moved).
|
|
Word_t scl2 = 0; // size in words of SCL
|
|
Word_t indexword; // buffer for aligned copy.
|
|
Word_t indexword2; // for old Index (SCL being moved).
|
|
PPvoid_t PPValue; // from JudyL array.
|
|
PPvoid_t PPValue2; // for old Index (SCL being moved).
|
|
Pscl_t Pscl = (Pscl_t) NULL; // shortcut leaf.
|
|
Pscl_t Pscl2; // for old Index (SCL being moved).
|
|
|
|
// CHECK FOR CALLER ERROR (NULL POINTERS):
|
|
|
|
if (PPArray == (PPvoid_t) NULL)
|
|
{
|
|
JU_SET_ERRNO(PJError, JU_ERRNO_NULLPPARRAY);
|
|
return (PPJERR);
|
|
}
|
|
if (Index == (uint8_t *) NULL)
|
|
{
|
|
JU_SET_ERRNO(PJError, JU_ERRNO_NULLPINDEX);
|
|
return (PPJERR);
|
|
}
|
|
|
|
len = STRLEN(Index); // bytes remaining.
|
|
|
|
// APPEND SHORTCUT LEAF:
|
|
//
|
|
// If PPArray, which is the root pointer to the first or next JudyL array in
|
|
// the tree, points to null (no next JudyL array), AND there is no shortcut
|
|
// leaf being carried down, append a shortcut leaf here for the new Index, no
|
|
// matter how much of the Index string remains (one or more bytes, including
|
|
// the trailing \0).
|
|
|
|
while (1) // until return.
|
|
{
|
|
if (*PPArray == (Pvoid_t)NULL) // no next JudyL array.
|
|
{
|
|
if (Pscl == (Pscl_t) NULL) // no SCL being carried down.
|
|
{
|
|
APPEND_SCL(Pscl, PPArray, pos, len, PJError); // returns if error.
|
|
return (&(Pscl->scl_Pvalue));
|
|
}
|
|
// else do nothing here; see below.
|
|
}
|
|
|
|
// CARRY DOWN PRE-EXISTING SHORTCUT LEAF:
|
|
//
|
|
// When PPArray points to a pre-existing shortcut leaf, if its Index is equal
|
|
// to the Index to be inserted, meaning no insertion is required, return its
|
|
// value area; otherwise, "move it aside" and "carry it down" -- replace it
|
|
// (see below) with one or more levels of JudyL arrays. Moving it aside
|
|
// initially just means setting Pscl non-null, both as a flag and for later
|
|
// use, and clearing the pointer to the SCL in the JudyL array.
|
|
|
|
else if (IS_PSCL(*PPArray))
|
|
{
|
|
assert(Pscl == (Pscl_t) NULL); // no nested SCLs.
|
|
|
|
Pscl = CLEAR_PSCL(*PPArray);
|
|
|
|
pos2 = Pscl->scl_Index; // note: pos2 is always word-aligned.
|
|
len2 = STRLEN(pos2); // bytes remaining.
|
|
|
|
// first check if string is already inserted
|
|
|
|
if ((len == len2) && (STRCMP(pos, pos2) == 0))
|
|
return (&(Pscl->scl_Pvalue));
|
|
|
|
*PPArray = (Pvoid_t)NULL; // disconnect SCL.
|
|
|
|
scl2 = SCLSIZE(len2); // save for JudyFree
|
|
|
|
// continue with *PPArray now clear, and Pscl, pos2, len2 set.
|
|
}
|
|
|
|
// CHECK IF OLD AND NEW INDEXES DIVERGE IN THE CURRENT INDEX WORD:
|
|
//
|
|
// If a shortcut leaf is being carried down and its remaining Index chars now
|
|
// diverge from the remaining chars of the Index being inserted, that is, if
|
|
// the next words of each Index differ, "plug in" the old Index here, in a new
|
|
// JudyL array, before proceeding.
|
|
//
|
|
// Note: Call JudyLIns() for the SCL Index word before calling it for the new
|
|
// Index word, so PPValue remains correct for the latter. (JudyLIns() return
|
|
// values are not stable across multiple calls.)
|
|
//
|
|
// Note: Although pos2 is word-aligned, and a Pscl_t is a whole number of
|
|
// words in size, pos2 is not certain to be null-padded through a whole word,
|
|
// so copy it first to an index word for later use.
|
|
//
|
|
// See header comments about premature return().
|
|
|
|
COPYSTRINGtoWORD(indexword, pos); // copy next 4[8] bytes.
|
|
|
|
if (Pscl != (Pscl_t) NULL)
|
|
{
|
|
COPYSTRINGtoWORD(indexword2, pos2); // copy next 4[8] bytes.
|
|
|
|
if (indexword != indexword2) // SCL and new Indexes diverge.
|
|
{
|
|
assert(*PPArray == (Pvoid_t)NULL); // should be new JudyL array.
|
|
|
|
// Note: If JudyLIns() returns JU_ERRNO_NOTJUDYL here, *PPArray should not be
|
|
// modified, so JudySLModifyErrno() can do the right thing.
|
|
|
|
if ((PPValue2 = JudyLIns(PPArray, indexword2, PJError))
|
|
== PPJERR)
|
|
{
|
|
JudySLModifyErrno(PJError, *PPArray, *PPArrayOrig);
|
|
return (PPJERR);
|
|
}
|
|
|
|
assert(PPValue2 != (PPvoid_t) NULL);
|
|
|
|
// If the old (SCL) Index terminates here, copy its value directly into the
|
|
// JudyL value area; otherwise create a new shortcut leaf for it, under
|
|
// *PPValue2 (skipping the word just inserted), and copy its value to the new
|
|
// SCL:
|
|
|
|
if (len2 <= WORDSIZE)
|
|
{
|
|
*((PWord_t)PPValue2) = (Word_t)(Pscl->scl_Pvalue);
|
|
}
|
|
else
|
|
{
|
|
APPEND_SCL(Pscl2, PPValue2, pos2 + WORDSIZE,
|
|
len2 - WORDSIZE, PJError);
|
|
(Pscl2->scl_Pvalue) = Pscl->scl_Pvalue;
|
|
}
|
|
// old SCL no longer needed.
|
|
|
|
JudyFree((void *)Pscl, scl2);
|
|
|
|
Pscl = (Pscl_t) NULL;
|
|
}
|
|
}
|
|
|
|
// APPEND NEXT LEVEL JUDYL ARRAY TO TREE:
|
|
//
|
|
// If a shortcut leaf was carried down and diverged at this level, the code
|
|
// above already appended the new JudyL array, but the next word of the new
|
|
// Index still must be inserted in it.
|
|
//
|
|
// See header comments about premature return().
|
|
//
|
|
// Note: If JudyLIns() returns JU_ERRNO_NOTJUDYL here, *PPArray should not be
|
|
// modified, so JudySLModifyErrno() can do the right thing.
|
|
|
|
if ((PPValue = JudyLIns(PPArray, indexword, PJError)) == PPJERR)
|
|
{
|
|
JudySLModifyErrno(PJError, *PPArray, *PPArrayOrig);
|
|
return (PPJERR);
|
|
}
|
|
|
|
assert(PPValue != (PPvoid_t) NULL);
|
|
|
|
// CHECK IF NEW INDEX TERMINATES:
|
|
//
|
|
// Note that if it does, and an old SCL was being carried down, it must have
|
|
// diverged by this point, and is already handled.
|
|
|
|
if (len <= WORDSIZE)
|
|
{
|
|
assert(Pscl == (Pscl_t) NULL);
|
|
return (PPValue); // is value for whole Index string.
|
|
}
|
|
|
|
pos += WORDSIZE;
|
|
len -= WORDSIZE;
|
|
pos2 += WORDSIZE; // useless unless Pscl is set.
|
|
len2 -= WORDSIZE;
|
|
|
|
PPArray = PPValue; // each value -> next array.
|
|
} // while.
|
|
} // NOTREACHED, JudySLIns()
|
|
|
|
// ****************************************************************************
|
|
// J U D Y S L D E L
|
|
//
|
|
// See the comments in JudySLGet(), which is somewhat similar.
|
|
//
|
|
// Unlike JudySLGet() and JudySLIns(), recurse downward through the tree of
|
|
// JudyL arrays to find and delete the given Index, if present, and then on the
|
|
// way back up, any of its parent arrays which ends up empty.
|
|
//
|
|
// TECHNICAL NOTES:
|
|
//
|
|
// Recursion seems bad, but this allows for an arbitrary-length Index. Also, a
|
|
// more clever iterative solution that used JudyLCount() (see below) would
|
|
// still require a function call per tree level, so why not just recurse?
|
|
//
|
|
// An earlier version (1.20) used a fixed-size stack, which limited the Index
|
|
// size. We were going to replace this with using JudyLCount(), in order to
|
|
// note and return to (read this carefully) the highest level JudyL array with
|
|
// a count of 1, all of whose descendant JudyL arrays also have a count of 1,
|
|
// and delete from that point downwards. This solution would traverse the
|
|
// array tree downward looking to see if the given Index is in the tree, then
|
|
// if so, delete layers downwards starting below the last one that contains
|
|
// other Indexes than the one being deleted.
|
|
//
|
|
// TBD: To save time coding, and to very likely save time overall during
|
|
// execution, this function does "lazy deletions", or putting it more nicely,
|
|
// it allows "hysteresis" in the JudySL tree, when shortcut leafs are present.
|
|
// It only removes the specified Index, and recursively any empty JudyL arrays
|
|
// above it, without fully reversing the effects of JudySLIns(). This is
|
|
// probably OK because any application that calls JudySLDel() is likely to call
|
|
// JudySLIns() again with the same or a neighbor Index.
|
|
|
|
int
|
|
JudySLDel(PPvoid_t PPArray, const uint8_t * Index, PJError_t PJError) // optional, for returning error info.
|
|
{
|
|
|
|
// Check for caller error (null pointer):
|
|
|
|
if (PPArray == (PPvoid_t) NULL)
|
|
{
|
|
JU_SET_ERRNO(PJError, JU_ERRNO_NULLPPARRAY);
|
|
return (JERR);
|
|
}
|
|
if (Index == (uint8_t *) NULL)
|
|
{
|
|
JU_SET_ERRNO(PJError, JU_ERRNO_NULLPINDEX);
|
|
return (JERR);
|
|
}
|
|
|
|
// Do the deletion:
|
|
|
|
return (JudySLDelSub(PPArray, PPArray, Index, STRLEN(Index), PJError));
|
|
|
|
} // JudySLDel()
|
|
|
|
// ****************************************************************************
|
|
// J U D Y S L D E L S U B
|
|
//
|
|
// This is the "engine" for JudySLDel() that expects aligned and len to already
|
|
// be computed (only once). See the header comments for JudySLDel().
|
|
|
|
static int
|
|
JudySLDelSub(PPvoid_t PPArray, // in which to delete.
|
|
PPvoid_t PPArrayOrig, // for error reporting.
|
|
const uint8_t * Index, // to delete.
|
|
Word_t len, // bytes remaining.
|
|
PJError_t PJError) // optional, for returning error info.
|
|
{
|
|
Word_t indexword; // next word to find.
|
|
PPvoid_t PPValue; // from JudyL array.
|
|
int retcode; // from lower-level call.
|
|
|
|
assert(PPArray != (PPvoid_t) NULL);
|
|
assert(Index != (uint8_t *) NULL);
|
|
|
|
// DELETE SHORTCUT LEAF:
|
|
//
|
|
// As described above, this can leave an empty JudyL array, or one containing
|
|
// only a single other Index word -- which could be, but is not, condensed into
|
|
// a higher-level shortcut leaf. More precisely, at this level it leaves a
|
|
// temporary "dead end" in the JudySL tree, similar to when running out of
|
|
// memory during JudySLIns(), and this is somewhat cleaned up by higher
|
|
// recursions of the same function (see below); but remaining shortcut leaves
|
|
// for other Indexes are not coalesced.
|
|
|
|
if (IS_PSCL(*PPArray))
|
|
{
|
|
Pscl_t Pscll = CLEAR_PSCL(*PPArray);
|
|
Word_t words;
|
|
|
|
if (STRCMP(Index, Pscll->scl_Index))
|
|
return (0); // incorrect index.
|
|
|
|
words = SCLSIZE(STRLEN(Pscll->scl_Index));
|
|
JudyFree((void *)Pscll, words);
|
|
|
|
*PPArray = (Pvoid_t)NULL;
|
|
return (1); // correct index deleted.
|
|
}
|
|
|
|
// DELETE LAST INDEX WORD, FROM CURRENT JUDYL ARRAY:
|
|
//
|
|
// When at the end of the full Index, delete the last word, if present, from
|
|
// the current JudyL array, and return the result all the way up.
|
|
|
|
COPYSTRINGtoWORD(indexword, Index); // copy next 4[8] bytes.
|
|
|
|
if (len <= WORDSIZE)
|
|
{
|
|
if ((retcode = JudyLDel(PPArray, indexword, PJError)) == JERR)
|
|
{
|
|
JudySLModifyErrno(PJError, *PPArray, *PPArrayOrig);
|
|
return (JERR);
|
|
}
|
|
return (retcode);
|
|
}
|
|
|
|
// DELETE BELOW NON-LAST INDEX WORD IN CURRENT JUDYL ARRAY:
|
|
//
|
|
// If a word before the end of the full Index is present in the current JudyL
|
|
// array, recurse through its value, which must be a pointer to another JudyL
|
|
// array, to continue the deletion at the next level. Return the JudyLGet()
|
|
// return if the Indexs current word is not in the JudyL array, or if no
|
|
// delete occurs below this level, both of which mean the whole Index is not
|
|
// currently valid.
|
|
//
|
|
|
|
JLG(PPValue, *PPArray, indexword);
|
|
if (PPValue == (PPvoid_t) NULL)
|
|
return (0); // Index not in JudySL array.
|
|
// If a previous JudySLIns() ran out of memory partway down the tree, it left a
|
|
// null *PPValue; this is automatically treated as a dead-end (not a core dump
|
|
// or assertion; see version 1.25).
|
|
if ((retcode =
|
|
JudySLDelSub(PPValue, PPArrayOrig, Index + WORDSIZE,
|
|
len - WORDSIZE, PJError)) != 1)
|
|
{
|
|
return (retcode); // no lower-level delete, or error.
|
|
}
|
|
|
|
// DELETE EMPTY JUDYL ARRAY:
|
|
//
|
|
// A delete occurred below in the tree. If the child JudyL array became empty,
|
|
// delete the current Index word from the current JudyL array, which could
|
|
// empty the current array and null out *PPArray in turn (or pass through an
|
|
// error). Otherwise simply indicate that a deletion did occur.
|
|
|
|
if (*PPValue == (Pvoid_t)NULL)
|
|
{
|
|
if ((retcode = JudyLDel(PPArray, indexword, PJError)) == JERR)
|
|
{
|
|
JudySLModifyErrno(PJError, *PPArray, *PPArrayOrig);
|
|
return (JERR);
|
|
}
|
|
|
|
return (retcode);
|
|
}
|
|
|
|
return (1);
|
|
} // JudySLDelSub()
|
|
|
|
// ****************************************************************************
|
|
// J U D Y S L P R E V
|
|
//
|
|
// Recursively traverse the JudySL tree downward using JudyLGet() to look for
|
|
// each successive index word from Index in the JudyL array at each level. At
|
|
// the last level for the Index (LASTWORD_BY_LEN()), use JudyLPrev() instead of
|
|
// JudyLGet(), to exclude the initial Index. If this doesnt result in finding
|
|
// a previous Index, work back up the tree using JudyLPrev() at each higher
|
|
// level to search for a previous index word. Upon finding a previous index
|
|
// word, descend again if/as necessary, this time inclusively, to find and
|
|
// return the full previous Index.
|
|
//
|
|
// Also support shortcut leaves.
|
|
//
|
|
// Since this function is recursive and it also needs to know if its still
|
|
// looking for the original Index (to exclude it at the LASTWORD_BY_LEN()
|
|
// level) or for the remaining words of the previous Index (inclusive),
|
|
// actually call a subroutine that takes an additional parameter.
|
|
//
|
|
// See also the technical notes in JudySLDel() regarding the use of recursion
|
|
// rather than iteration.
|
|
|
|
PPvoid_t
|
|
JudySLPrev(Pcvoid_t PArray, uint8_t * Index, PJError_t PJError) // optional, for returning error info.
|
|
{
|
|
// Check for caller error (null pointer), or empty JudySL array:
|
|
|
|
if (Index == (uint8_t *) NULL)
|
|
{
|
|
JU_SET_ERRNO(PJError, JU_ERRNO_NULLPINDEX);
|
|
return (PPJERR);
|
|
}
|
|
|
|
if (PArray == (Pvoid_t)NULL)
|
|
return ((PPvoid_t) NULL);
|
|
// Do the search:
|
|
return (JudySLPrevSub(PArray, Index, /* original = */ 1,
|
|
STRLEN(Index), PJError));
|
|
} // JudySLPrev()
|
|
|
|
// ****************************************************************************
|
|
// J U D Y S L P R E V S U B
|
|
//
|
|
// This is the "engine" for JudySLPrev() that knows whether its still looking
|
|
// for the original Index (exclusive) or a neighbor index (inclusive), and that
|
|
// expects aligned and len to already be computed (only once). See the header
|
|
// comments for JudySLPrev().
|
|
|
|
static PPvoid_t
|
|
JudySLPrevSub(Pcvoid_t PArray, uint8_t * Index, int orig,
|
|
Word_t len, // bytes remaining.
|
|
PJError_t PJError) // optional, for returning error info.
|
|
{
|
|
Word_t indexword; // next word to find.
|
|
PPvoid_t PPValue; // from JudyL array.
|
|
// ORIGINAL SEARCH:
|
|
//
|
|
// When at a shortcut leaf, copy its remaining Index (string) chars into Index
|
|
// and return its value area if the current Index is after (greater than) the
|
|
// SCLs index; otherwise return null.
|
|
if (orig)
|
|
{
|
|
if (IS_PSCL(PArray))
|
|
{
|
|
if ((PPValue = PPSCLVALUE_GT(Index, PArray)) != (PPvoid_t) NULL)
|
|
(void)STRCPY(Index, PSCLINDEX(PArray));
|
|
return (PPValue);
|
|
}
|
|
|
|
// If the current Index word:
|
|
// - is not the last word in Index (end of string),
|
|
// - exists in the current JudyL array, and,
|
|
// - a previous Index is found below it, return that Indexs value area.
|
|
|
|
COPYSTRINGtoWORD(indexword, Index); // copy next 4[8] bytes.
|
|
if (len > WORDSIZE) // not at end of Index.
|
|
{
|
|
JLG(PPValue, PArray, indexword);
|
|
if (PPValue != (PPvoid_t) NULL)
|
|
{
|
|
|
|
// If a previous JudySLIns() ran out of memory partway down the tree, it left a
|
|
// null *PPValue; this is automatically treated as a dead-end (not a core dump
|
|
// or assertion; see version 1.25):
|
|
|
|
PPValue = JudySLPrevSub(*PPValue, Index + WORDSIZE,
|
|
/* original = */ 1,
|
|
len - WORDSIZE, PJError);
|
|
if (PPValue == PPJERR)
|
|
return (PPJERR); // propagate error.
|
|
if (PPValue != (PPvoid_t) NULL)
|
|
return (PPValue); // see above.
|
|
}
|
|
}
|
|
|
|
// Search for previous index word:
|
|
//
|
|
// One of the above conditions is false. Search the current JudyL array for
|
|
// the Index word, if any, prior to the current index word. If none is found,
|
|
// return null; otherwise fall through to common later code.
|
|
|
|
if ((PPValue = JudyLPrev(PArray, &indexword, PJError)) == PPJERR)
|
|
{
|
|
JudySLModifyErrno(PJError, PArray, orig ? PArray : (Pvoid_t)NULL);
|
|
return (PPJERR);
|
|
}
|
|
|
|
if (PPValue == (PPvoid_t) NULL)
|
|
return ((PPvoid_t) NULL); // no previous index word.
|
|
} // if.
|
|
|
|
// SUBSEQUENT SEARCH:
|
|
//
|
|
// A higher level search already excluded the initial Index, then found a
|
|
// previous index word, and is now traversing down to determine the rest of the
|
|
// Index and to obtain its value area. If at a shortcut leaf, return its value
|
|
// area. Otherwise search the current JudyL array backward from the upper
|
|
// limit for its last index word. If no index word is found, return null --
|
|
// should never happen unless the JudySL tree is corrupt; otherwise fall
|
|
// through to common later code.
|
|
|
|
else
|
|
{
|
|
if (IS_PSCL(PArray)) // at shortcut leaf.
|
|
{
|
|
(void)STRCPY(Index, PSCLINDEX(PArray));
|
|
return (&PSCLVALUE(PArray));
|
|
}
|
|
|
|
indexword = ~0UL;
|
|
if ((PPValue = JudyLLast(PArray, &indexword, PJError)) == PPJERR)
|
|
{
|
|
JudySLModifyErrno(PJError, PArray, orig ? PArray : (Pvoid_t)NULL);
|
|
return (PPJERR);
|
|
}
|
|
|
|
// If a previous JudySLIns() ran out of memory partway down the tree, it left a
|
|
// null *PPValue; this is automatically treated as a dead-end (not a core dump
|
|
// or assertion; see version 1.25):
|
|
|
|
if (PPValue == (PPvoid_t) NULL)
|
|
return ((PPvoid_t) NULL); // no previous index word.
|
|
}
|
|
|
|
// FOUND PREVIOUS INDEX WORD:
|
|
//
|
|
// A previous (if original) or last (if subsequent) index word was located in
|
|
// the current JudyL array. Store it into the callers Index (string). Then
|
|
// if the found (previous) Index ends here, return its value area; otherwise do
|
|
// a subsequent search below this point, which should never fail unless the
|
|
// JudySL tree is corrupt, but this is detected at a lower level by the above
|
|
// assertion.
|
|
//
|
|
// Note: Treat Index as unaligned, even if it is aligned, to avoid writing
|
|
// past the end of allocated memory (in case its less than a whole number of
|
|
// words).
|
|
|
|
COPYWORDtoSTRING(Index, indexword); // copy next 4[8] bytes.
|
|
if (LASTWORD_BY_VALUE(indexword))
|
|
return (PPValue);
|
|
// If a previous JudySLIns() ran out of memory partway down the tree, it left a
|
|
// null *PPValue; this is automatically treated as a dead-end (not a core dump
|
|
// or assertion; see version 1.25):
|
|
return (JudySLPrevSub(*PPValue, Index + WORDSIZE, /* original = */ 0,
|
|
len - WORDSIZE, PJError));
|
|
} // JudySLPrevSub()
|
|
|
|
// ****************************************************************************
|
|
// J U D Y S L N E X T
|
|
//
|
|
// See the comments in JudySLPrev(), which is very similar.
|
|
//
|
|
// TBD: Could the two functions call a common engine function with various
|
|
// subfunctions and other constants specified?
|
|
|
|
PPvoid_t
|
|
JudySLNext(Pcvoid_t PArray, uint8_t * Index, PJError_t PJError) // optional, for returning error info.
|
|
{
|
|
// Check for caller error (null pointer), or empty JudySL array:
|
|
|
|
if (Index == (uint8_t *) NULL)
|
|
{
|
|
JU_SET_ERRNO(PJError, JU_ERRNO_NULLPINDEX);
|
|
return (PPJERR);
|
|
}
|
|
|
|
if (PArray == (Pvoid_t)NULL)
|
|
return ((PPvoid_t) NULL);
|
|
// Do the search:
|
|
return (JudySLNextSub(PArray, Index, /* original = */ 1,
|
|
STRLEN(Index), PJError));
|
|
} // JudySLNext()
|
|
|
|
// ****************************************************************************
|
|
// J U D Y S L N E X T S U B
|
|
//
|
|
// See the comments in JudySLPrevSub(), which is very similar.
|
|
|
|
static PPvoid_t
|
|
JudySLNextSub(Pcvoid_t PArray, uint8_t * Index, int orig,
|
|
Word_t len, // bytes remaining.
|
|
PJError_t PJError) // optional, for returning error info.
|
|
{
|
|
Word_t indexword; // next word to find.
|
|
PPvoid_t PPValue; // from JudyL array.
|
|
if (orig)
|
|
{
|
|
if (IS_PSCL(PArray))
|
|
{
|
|
if ((PPValue = PPSCLVALUE_LT(Index, PArray)) != (PPvoid_t) NULL)
|
|
(void)STRCPY(Index, PSCLINDEX(PArray));
|
|
return (PPValue);
|
|
}
|
|
|
|
COPYSTRINGtoWORD(indexword, Index); // copy next 4[8] bytes.
|
|
|
|
if (len > WORDSIZE) // not at end of Index.
|
|
{
|
|
JLG(PPValue, PArray, indexword);
|
|
if (PPValue != (PPvoid_t) NULL)
|
|
{
|
|
// If a previous JudySLIns() ran out of memory partway down the tree, it left a
|
|
// null *PPValue; this is automatically treated as a dead-end (not a core dump
|
|
// or assertion; see version 1.25):
|
|
|
|
PPValue = JudySLNextSub(*PPValue, Index + WORDSIZE,
|
|
/* original = */ 1,
|
|
len - WORDSIZE, PJError);
|
|
if (PPValue == PPJERR)
|
|
return (PPJERR); // propagate error.
|
|
if (PPValue != (PPvoid_t) NULL)
|
|
return (PPValue); // see above.
|
|
}
|
|
}
|
|
|
|
if ((PPValue = JudyLNext(PArray, &indexword, PJError)) == PPJERR)
|
|
{
|
|
JudySLModifyErrno(PJError, PArray, orig ? PArray : (Pvoid_t)NULL);
|
|
return (PPJERR);
|
|
}
|
|
|
|
if (PPValue == (PPvoid_t) NULL)
|
|
return ((PPvoid_t) NULL); // no next index word.
|
|
}
|
|
else
|
|
{
|
|
if (IS_PSCL(PArray)) // at shortcut leaf.
|
|
{
|
|
(void)STRCPY(Index, PSCLINDEX(PArray));
|
|
return (&PSCLVALUE(PArray));
|
|
}
|
|
|
|
indexword = 0;
|
|
if ((PPValue = JudyLFirst(PArray, &indexword, PJError)) == PPJERR)
|
|
{
|
|
JudySLModifyErrno(PJError, PArray, orig ? PArray : (Pvoid_t)NULL);
|
|
return (PPJERR);
|
|
}
|
|
|
|
// If a previous JudySLIns() ran out of memory partway down the tree, it left a
|
|
// null *PPValue; this is automatically treated as a dead-end (not a core dump
|
|
// or assertion; see version 1.25):
|
|
|
|
if (PPValue == (PPvoid_t) NULL)
|
|
return ((PPvoid_t) NULL); // no next index word.
|
|
}
|
|
|
|
COPYWORDtoSTRING(Index, indexword); // copy next 4[8] bytes
|
|
if (LASTWORD_BY_VALUE(indexword))
|
|
return (PPValue);
|
|
// If a previous JudySLIns() ran out of memory partway down the tree, it left a
|
|
// null *PPValue; this is automatically treated as a dead-end (not a core dump
|
|
// or assertion; see version 1.25):
|
|
return (JudySLNextSub(*PPValue, Index + WORDSIZE, /* original = */ 0,
|
|
len - WORDSIZE, PJError));
|
|
} // JudySLNextSub()
|
|
|
|
// ****************************************************************************
|
|
// J U D Y S L F I R S T
|
|
//
|
|
// Like JudyLFirst(), do a JudySLGet(), then if necessary a JudySLNext().
|
|
|
|
PPvoid_t
|
|
JudySLFirst(Pcvoid_t PArray, uint8_t * Index, PJError_t PJError) // optional, for returning error info.
|
|
{
|
|
PPvoid_t PPValue; // from JudyL array.
|
|
if (Index == (uint8_t *) NULL)
|
|
{
|
|
JU_SET_ERRNO(PJError, JU_ERRNO_NULLPINDEX);
|
|
return (PPJERR);
|
|
}
|
|
|
|
if ((PPValue = JudySLGet(PArray, Index, PJError)) == PPJERR)
|
|
return (PPJERR); // propagate serious error.
|
|
if ((PPValue == (PPvoid_t) NULL) // first try failed.
|
|
&& ((PPValue = JudySLNext(PArray, Index, PJError)) == PPJERR))
|
|
{
|
|
return (PPJERR); // propagate serious error.
|
|
}
|
|
|
|
return (PPValue);
|
|
} // JudySLFirst()
|
|
|
|
// ****************************************************************************
|
|
// J U D Y S L L A S T
|
|
//
|
|
// Like JudyLLast(), do a JudySLGet(), then if necessary a JudySLPrev().
|
|
|
|
PPvoid_t
|
|
JudySLLast(Pcvoid_t PArray, uint8_t * Index, PJError_t PJError) // optional, for returning error info.
|
|
{
|
|
PPvoid_t PPValue; // from JudyL array.
|
|
if (Index == (uint8_t *) NULL)
|
|
{
|
|
JU_SET_ERRNO(PJError, JU_ERRNO_NULLPINDEX);
|
|
return (PPJERR);
|
|
}
|
|
|
|
if ((PPValue = JudySLGet(PArray, Index, PJError)) == PPJERR)
|
|
return (PPJERR); // propagate serious error.
|
|
if ((PPValue == (PPvoid_t) NULL) // first try failed.
|
|
&& ((PPValue = JudySLPrev(PArray, Index, PJError)) == PPJERR))
|
|
{
|
|
return (PPJERR); // propagate serious error.
|
|
}
|
|
|
|
return (PPValue);
|
|
} // JudySLLast()
|
|
|
|
// ****************************************************************************
|
|
// J U D Y S L F R E E A R R A Y
|
|
//
|
|
// Walk the JudySL tree of JudyL arrays to free each JudyL array, depth-first.
|
|
// During the walk, ignore indexes (strings) that end in the current JudyL
|
|
// array to be freed. Just recurse through those indexes which do not end,
|
|
// that is, those whose associated value areas point to subsidiary JudyL
|
|
// arrays, except for those which point to shortcut leaves. Return the total
|
|
// bytes freed in all of the JudyL arrays at or below the current level.
|
|
//
|
|
// Like the JudyLFreeArray() and Judy1FreeArray() code, this is written
|
|
// recursively, which is probably fast enough, to allow indexes (strings) of
|
|
// arbitrary size. If recursion turns out to be a problem, consider instead
|
|
// doing some large, fixed number of iterative descents (like 100) using a
|
|
// fixed-size "stack" (array), then recursing upon overflow (relatively
|
|
// rarely).
|
|
|
|
Word_t
|
|
JudySLFreeArray(PPvoid_t PPArray, PJError_t PJError) // optional, for returning error info.
|
|
{
|
|
PPvoid_t PPArrayOrig = PPArray; // for error reporting.
|
|
Word_t indexword = 0; // word just found.
|
|
PPvoid_t PPValue; // from Judy array.
|
|
Word_t bytes_freed = 0; // bytes freed at this level.
|
|
Word_t bytes_total = 0; // bytes freed at all levels.
|
|
if (PPArray == (PPvoid_t) NULL)
|
|
{
|
|
JU_SET_ERRNO(PJError, JU_ERRNO_NULLPPARRAY);
|
|
return (JERR);
|
|
}
|
|
|
|
// FREE SHORTCUT LEAF:
|
|
|
|
if (IS_PSCL(*PPArray))
|
|
{
|
|
Word_t freewords;
|
|
Pscl_t Pscl;
|
|
|
|
Pscl = CLEAR_PSCL(*PPArray);
|
|
|
|
freewords = SCLSIZE(STRLEN(Pscl->scl_Index));
|
|
|
|
JudyFree((void *)Pscl, freewords);
|
|
|
|
*PPArray = (Pvoid_t)NULL;
|
|
|
|
return (freewords * WORDSIZE);
|
|
}
|
|
|
|
// FREE EACH SUB-ARRAY (DEPTH-FIRST):
|
|
//
|
|
// If a previous JudySLIns() ran out of memory partway down the tree, it left a
|
|
// null *PPValue. This is automatically treated correctly here as a dead-end.
|
|
//
|
|
// An Index (string) ends in the current word iff the last byte of the
|
|
// (null-padded) word is null.
|
|
|
|
for (PPValue = JudyLFirst(*PPArray, &indexword, PJError);
|
|
(PPValue != (PPvoid_t) NULL) && (PPValue != PPJERR);
|
|
PPValue = JudyLNext(*PPArray, &indexword, PJError))
|
|
{
|
|
if (!LASTWORD_BY_VALUE(indexword))
|
|
{
|
|
if ((bytes_freed = JudySLFreeArray(PPValue, PJError)) == JERR)
|
|
return (JERR); // propagate serious error.
|
|
bytes_total += bytes_freed;
|
|
}
|
|
}
|
|
|
|
// Check for a serious error in a JudyL*() call:
|
|
|
|
if (PPValue == PPJERR)
|
|
{
|
|
JudySLModifyErrno(PJError, *PPArray, *PPArrayOrig);
|
|
return (JERR);
|
|
}
|
|
|
|
// Now free the current array, which also nulls the pointer:
|
|
//
|
|
// Note: *PPArray can be null here for a totally null JudySL array =>
|
|
// JudyLFreeArray() returns zero.
|
|
|
|
if ((bytes_freed = JudyLFreeArray(PPArray, PJError)) == JERR)
|
|
{
|
|
JudySLModifyErrno(PJError, *PPArray, *PPArrayOrig);
|
|
return (JERR);
|
|
}
|
|
return (bytes_total + bytes_freed);
|
|
} // JudySLFreeArray()
|