Alien-Judy
view release on metacpan or search on metacpan
src/judy-1.0.5/src/JudyCommon/JudyPrevNextEmpty.c view on Meta::CPAN
#ifdef JUDY1
#include "Judy1.h"
#else
#include "JudyL.h"
#endif
#include "JudyPrivate1L.h"
#ifdef TRACEJPSE
#include "JudyPrintJP.c"
#endif
// ****************************************************************************
// J U D Y 1 P R E V E M P T Y
// J U D Y 1 N E X T E M P T Y
// J U D Y L P R E V E M P T Y
// J U D Y L N E X T E M P T Y
//
// See the manual entry for the API.
//
// OVERVIEW OF Judy*PrevEmpty() / Judy*NextEmpty():
//
// See also for comparison the equivalent comments in JudyPrevNext.c.
//
// Take the callers *PIndex and subtract/add 1, but watch out for
// underflow/overflow, which means "no previous/next empty index found." Use a
// reentrant switch statement (state machine, see SMGetRestart and
// SMGetContinue) to decode Index, starting with the JRP (PArray), through a
// JPM and branches, if any, down to an immediate or a leaf. Look for Index in
// that immediate or leaf, and if not found (invalid index), return success
// (Index is empty).
//
// This search can result in a dead end where taking a different path is
// required. There are four kinds of dead ends:
//
// BRANCH PRIMARY dead end: Encountering a fully-populated JP for the
// appropriate digit in Index. Search sideways in the branch for the
// previous/next absent/null/non-full JP, and if one is found, set Index to the
// highest/lowest index possible in that JPs expanse. Then if the JP is an
// absent or null JP, return success; otherwise for a non-full JP, traverse
// through the partially populated JP.
//
// BRANCH SECONDARY dead end: Reaching the end of a branch during a sideways
// search after a branch primary dead end. Set Index to the lowest/highest
// index possible in the whole branchs expanse (one higher/lower than the
// previous/next branchs expanse), then restart at the top of the tree, which
// includes pre-decrementing/incrementing Index (again) and watching for
// underflow/overflow (again).
//
// LEAF PRIMARY dead end: Finding a valid (non-empty) index in an immediate or
// leaf matching Index. Search sideways in the immediate/leaf for the
// previous/next empty index; if found, set *PIndex to match and return success.
//
// LEAF SECONDARY dead end: Reaching the end of an immediate or leaf during a
// sideways search after a leaf primary dead end. Just as for a branch
// secondary dead end, restart at the top of the tree with Index set to the
// lowest/highest index possible in the whole immediate/leafs expanse.
// TBD: If leaf secondary dead end occurs, could shortcut and treat it as a
// branch primary dead end; but this would require remembering the parent
// branchs type and offset (a "one-deep stack"), and also wrestling with
// narrow pointers, at least for leaves (but not for immediates).
//
// Note some ASYMMETRIES between SearchValid and SearchEmpty:
//
// - The SearchValid code, upon descending through a narrow pointer, if Index
// is outside the expanse of the subsidiary node (effectively a secondary
// dead end), must decide whether to backtrack or findlimit. But the
// SearchEmpty code simply returns success (Index is empty).
//
// - Similarly, the SearchValid code, upon finding no previous/next index in
// the expanse of a narrow pointer (again, a secondary dead end), can simply
// start to backtrack at the parent JP. But the SearchEmpty code would have
// to first determine whether or not the parent JPs narrow expanse contains
// a previous/next empty index outside the subexpanse. Rather than keeping a
// parent state stack and backtracking this way, upon a secondary dead end,
// the SearchEmpty code simply restarts at the top of the tree, whether or
// not a narrow pointer is involved. Again, see the equivalent comments in
// JudyPrevNext.c for comparison.
//
// This function is written iteratively for speed, rather than recursively.
//
// TBD: Wed like to enhance this function to make successive searches faster.
// This would require saving some previous state, including the previous Index
// returned, and in which leaf it was found. If the next call is for the same
// Index and the array has not been modified, start at the same leaf. This
// should be much easier to implement since this is iterative rather than
// recursive code.
#ifdef JUDY1
#ifdef JUDYPREV
FUNCTION int Judy1PrevEmpty
#else
FUNCTION int Judy1NextEmpty
#endif
#else
#ifdef JUDYPREV
FUNCTION int JudyLPrevEmpty
#else
FUNCTION int JudyLNextEmpty
#endif
#endif
(
Pcvoid_t PArray, // Judy array to search.
Word_t * PIndex, // starting point and result.
PJError_t PJError // optional, for returning error info.
)
{
Word_t Index; // fast copy, in a register.
Pjp_t Pjp; // current JP.
Pjbl_t Pjbl; // Pjp->jp_Addr masked and cast to types:
Pjbb_t Pjbb;
Pjbu_t Pjbu;
Pjlb_t Pjlb;
PWord_t Pword; // alternate name for use by GET* macros.
Word_t digit; // next digit to decode from Index.
Word_t digits; // current state in SM = digits left to decode.
Word_t pop0; // in a leaf.
Word_t pop0mask; // precalculated to avoid variable shifts.
long offset; // within a branch or leaf (can be large).
int subexp; // subexpanse in a bitmap branch.
BITMAPB_t bitposmaskB; // bit in bitmap for bitmap branch.
BITMAPL_t bitposmaskL; // bit in bitmap for bitmap leaf.
Word_t possfullJP1; // JP types for possibly full subexpanses:
Word_t possfullJP2;
Word_t possfullJP3;
// ----------------------------------------------------------------------------
// M A C R O S
//
// These are intended to make the code a bit more readable and less redundant.
// CHECK FOR NULL JP:
//
// TBD: In principle this can be reduced (here and in other *.c files) to just
// the latter clause since no Type should ever be below cJU_JPNULL1, but in
// fact some root pointer types can be lower, so for safety do both checks.
#define JPNULL(Type) (((Type) >= cJU_JPNULL1) && ((Type) <= cJU_JPNULLMAX))
// CHECK FOR A FULL JP:
//
// Given a JP, indicate if it is fully populated. Use digits, pop0mask, and
// possfullJP1..3 in the context.
//
// This is a difficult problem because it requires checking the Pop0 bits for
// all-ones, but the number of bytes depends on the JP type, which is not
// directly related to the parent branchs type or level -- the JPs child
// could be under a narrow pointer (hence not full). The simple answer
// requires switching on or otherwise calculating the JP type, which could be
// slow. Instead, in SMPREPB* precalculate pop0mask and also record in
// possfullJP1..3 the child JP (branch) types that could possibly be full (one
// level down), and use them here. For level-2 branches (with digits == 2),
// the test for a full child depends on Judy1/JudyL.
//
// Note: This cannot be applied to the JP in a JPM because it doesnt have
// enough pop0 digits.
//
// TBD: JPFULL_BRANCH diligently checks for BranchL or BranchB, where neither
// of those can ever be full as it turns out. Could just check for a BranchU
// at the right level. Also, pop0mask might be overkill, its not used much,
// so perhaps just call cJU_POP0MASK(digits - 1) here?
//
// First, JPFULL_BRANCH checks for a full expanse for a JP whose child can be a
// branch, that is, a JP in a branch at level 3 or higher:
#define JPFULL_BRANCH(Pjp) \
((((JU_JPDCDPOP0(Pjp) ^ cJU_ALLONES) & pop0mask) == 0) \
&& ((JU_JPTYPE(Pjp) == possfullJP1) \
|| (JU_JPTYPE(Pjp) == possfullJP2) \
|| (JU_JPTYPE(Pjp) == possfullJP3)))
#ifdef JUDY1
#define JPFULL(Pjp) \
((digits == 2) ? \
(JU_JPTYPE(Pjp) == cJ1_JPFULLPOPU1) : JPFULL_BRANCH(Pjp))
#else
#define JPFULL(Pjp) \
((digits == 2) ? \
(JU_JPTYPE(Pjp) == cJU_JPLEAF_B1) \
&& (((JU_JPDCDPOP0(Pjp) & cJU_POP0MASK(1)) == cJU_POP0MASK(1))) : \
JPFULL_BRANCH(Pjp))
#endif
// RETURN SUCCESS:
//
// This hides the need to set *PIndex back to the local value of Index -- use a
// local value for faster operation. Note that the callers *PIndex is ALWAYS
// modified upon success, at least decremented/incremented.
#define RET_SUCCESS { *PIndex = Index; return(1); }
// RETURN A CORRUPTION:
#define RET_CORRUPT { JU_SET_ERRNO(PJError, JU_ERRNO_CORRUPT); return(JERRI); }
// SEARCH A BITMAP BRANCH:
//
// This is a weak analog of j__udySearchLeaf*() for bitmap branches. Return
// the actual or next-left position, base 0, of Digit in a BITMAPB_t bitmap
// (subexpanse of a full bitmap), also given a Bitposmask for Digit. The
// position is the offset within the set bits.
//
// Unlike j__udySearchLeaf*(), the offset is not returned bit-complemented if
// Digits bit is unset, because the caller can check the bitmap themselves to
( run in 0.920 second using v1.01-cache-2.11-cpan-2398b32b56e )