|
|
/*
* rhizome.c * * author: John R. Douceur * date: 28 April 1997 * * This source file provides functions that implement insertion, removal, and * search operations on the rhizome database. The code is object-oriented C, * transliterated from a C++ implementation. * * The rhizome is a database that stores patterns containing wildcards. * Each pattern defines a set of keys that it matches; if a pattern contains * N wildcards, then it matches 2^N keys. Since each pattern can match * multiple keys, it is possible for a given key to match multiple patterns * in the database. The rhizome requires that all patterns stored therein * have a strict hierarchical interrelationship. Two patterns may match no * common keys (in which case the patterns are said to be independent), or * one pattern may match all the keys matched by a second pattern as well as * additonal keys (in which case the second pattern is said to be more general * than the first, and the first more specific than the second). The database * will not accept two patterns which match some keys in common but each of * which also matches additional keys that the other does not. * * The database can be searched for patterns that match a given search key. * When the database is searched for a given key, the most specifically * matching pattern is found. If no patterns in the database match the key, * an appropriate indication is returned. * * None of the code or comments in this file needs to be understood by writers * of client code; all explanatory information for clients is found in the * associated header file, rhizome.h. * */
#include "gpcpre.h"
// The fields of the RhizomeNode structure are accessed through the following
// macros. The first three are obvious; the subsequent three rely on an agreed
// usage of the cdata array in the RhizomeNode. The first keybytes locations
// of the cdata array are used to store the value field of the node; the second
// keybytes locations store the mask field; and the third keybytes locations
// store the imask field.
//
#define CHILDREN udata.branch.children
#define REFERENCE udata.leaf.reference
#define GODPARENT udata.leaf.godparent
#define VALUE(pointer) (pointer->cdata)
#define MASK(pointer) (pointer->cdata + rhizome->keybytes)
#define IMASK(pointer) (pointer->cdata + 2 * rhizome->keybytes)
// This macro allocates a new rhizome node structure. The size of the structure
// is a function of the value of keybytes, since three bytes of information
// need to be stored in the structure for each byte of pattern length. The
// cdata array, which is the last field in the structure, is declared as a
// having a single element, but this array will actually extend beyond the
// defined end of the structure into additional space that is allocated for it
// by the following macro.
//
#define NEW_RhizomeNode(_pa) \
GpcAllocMem(_pa,\ sizeof(RhizomeNode) + 3 * rhizome->keybytes - 1,\ RhizomeTag);\ TRACE(RHIZOME, *_pa, sizeof(RhizomeNode) + 3 * rhizome->keybytes - 1, "NEW_RhizomeNode")
// This macro gets the indexed bit of the value, where the most-significant bit
// is defined as bit 0.
//
#define BIT_OF(value, index) \
(((value)[(index) >> 3] >> (7 - ((index) & 0x7))) & 0x1)
// Following are prototypes for static functions that are used internally by
// the implementation of the rhizome routines.
static int node_insert( Rhizome *rhizome, RhizomeNode *new_leaf, RhizomeNode **ppoint, int prev_bit);
static void node_remove( Rhizome *rhizome, RhizomeNode *leaf, RhizomeNode **ppoint);
static RhizomeNode * replicate( Rhizome *rhizome, RhizomeNode *source, int pivot_bit);
static void eliminate( Rhizome *rhizome, RhizomeNode *point);
static void coalesce( Rhizome *rhizome, RhizomeNode **leaf_list, RhizomeNode *point);
// Since this is not C++, the Rhizome structure is not self-constructing;
// therefore, the following constructor code must be called on the Rhizome
// structure after it is allocated. The argument keybits specifies the size
// (in bits) of each pattern that will be stored in the database.
//
void constructRhizome( Rhizome *rhizome, int keybits) { rhizome->keybits = keybits; rhizome->keybytes = (keybits - 1) / 8 + 1; rhizome->root = 0; }
// Since this is not C++, the Rhizome structure is not self-destructing;
// therefore, the following destructor code must be called on the Rhizome
// structure before it is deallocated.
//
// If the structure is non-empty, call coalesce() to eliminate
// all branch nodes and to string leaf nodes into a list; then delete list.
//
void destructRhizome( Rhizome *rhizome) { RhizomeNode *leaf_list, *next; if (rhizome->root != 0) { leaf_list = 0; coalesce(rhizome, &leaf_list, rhizome->root); while (leaf_list != 0) { next = leaf_list->GODPARENT; GpcFreeMem(leaf_list, RhizomeTag); leaf_list = next; } } }
// This function searches the database for the pattern that most specifically
// matches the given key. The key is passed as an array of bytes. When the
// most specific match is found, the PatternHandle of that matching pattern is
// returned. From the PatternHandle can be gotten the reference value via the
// macro GetReferenceFromPatternHandle. If no pattern in the database is found
// to match the key, then a value of 0 is returned as the PatternHandle.
//
PatternHandle searchRhizome( Rhizome *rhizome, char *key) { int index; RhizomeNode *point; // If tree is empty, search fails.
if (rhizome->root == 0) { return 0; } // Otherwise, start at rhizome->root and navigate tree until reaching a leaf.
point = rhizome->root; while (point->pivot_bit < rhizome->keybits) { point = point->CHILDREN[BIT_OF(key, point->pivot_bit)]; } // Check value for match, one byte at a time. If any byte fails to match,
// continue checking godparent with same byte; since previous bytes matched
// godchild, they are guaranteed to match godparent also.
index = 0; while (index < rhizome->keybytes) { if ((((key)[index]) & MASK(point)[index]) != VALUE(point)[index]) { if (point->GODPARENT != 0) { point = point->GODPARENT; } else { return 0; } } else { index++; } } return point; }
// This function inserts a new pattern into the database. The pattern is
// specified by a value and a mask. Each bit of the mask determines whether
// the bit position is specified or is a wildcard: A 1 in a mask bit indicates
// that the value of that bit is specified by the pattern; a 0 indicates that
// the value of that bit is a wildcard. If a mask bit is 1, then the
// corresponding bit in the value field indicates the specified value of that
// bit. Value and mask fields are passed as arrays of bytes.
//
// The client specifies a void pointer reference value to associate with the
// pattern. When the pattern is installed, the insertRhizome function returns
// a pointer to a PatternHandle.
//
// If the new pattern conflicts with a pattern already installed in the
// database, meaning that the two patterns match some keys in common but each
// also matches additional keys that the other does not, then the new pattern
// is not inserted, and a value of 0 is returned as the PatternHandle.
//
PatternHandle insertRhizome( Rhizome *rhizome, char *value, char *mask, void *reference, ulong *status) { RhizomeNode *new_leaf; int index0, insert_status; *status = GPC_STATUS_SUCCESS;
// Create new leaf and copy data into it; restrict set bits of value to
// those set in mask, since later code assumes this is the case. Add new
// leaf to reference table.
NEW_RhizomeNode(&new_leaf); if (new_leaf == 0) { // Memory could not be allocated for this new node. Therefore, we
// return an indication of failure to the client.
*status = GPC_STATUS_RESOURCES; return 0; } for (index0 = 0; index0 < rhizome->keybytes; index0++) { VALUE(new_leaf)[index0] = value[index0] & mask[index0]; MASK(new_leaf)[index0] = mask[index0]; IMASK(new_leaf)[index0] = mask[index0]; } new_leaf->REFERENCE = reference; new_leaf->pivot_bit = rhizome->keybits; new_leaf->GODPARENT = 0; // If tree is empty, leaf becomes first node; otherwise, attempt to insert
// using recursive node_insert() routine. If new leaf conflicts with
// existing leaf, node_insert() throws exception; then remove new leaf and
// return failure code.
if (rhizome->root == 0) { rhizome->root = new_leaf; } else { insert_status = node_insert(rhizome, new_leaf, &rhizome->root, -1); if (insert_status != GPC_STATUS_SUCCESS) { removeRhizome(rhizome, new_leaf); *status = GPC_STATUS_CONFLICT; return 0; // return null pointer
}; } return new_leaf; }
// This function removes a pattern from the rhizome. The pattern is specified
// by the PatternHandle that was returned by the insertRhizome function. No
// checks are performed to insure that this is a valid handle.
//
void removeRhizome( Rhizome *rhizome, PatternHandle phandle) { // Call recursive node_remove() routine to remove all references to leaf;
// then delete leaf.
node_remove(rhizome, phandle, &rhizome->root); TRACE(RHIZOME, rhizome, phandle, "removeRhizome") GpcFreeMem(phandle, RhizomeTag); }
// Insert new_leaf into subtree pointed to by *ppoint. Update *ppoint to point
// to newly created nodes if necessary. Index of most recently examined bit
// is given by prev_bit. The return value is a status code: Normally, it
// returns GPC_STATUS_SUCCESS; if there is a conflict, then it returns NDIS_STATUS_CONFLICT;
// if there is insufficient memory available to perform the insertion, then it
// returns GPC_STATUS_RESOURCES.
//
static int node_insert( Rhizome *rhizome, RhizomeNode *new_leaf, RhizomeNode **ppoint, int prev_bit) { int index, index0, bit_value, insert_status; char sub, super; RhizomeNode *point, *child, *new_branch; // This routine has a recursive structure, but unnecessary recursions have
// been replaced by iteration, in order to improve performance. This
// recursion removal has introduced a forever loop which encloses the
// entirety of the routine; looping back to the beginning of this loop is
// thus the equivalent of recursing.
while (1) { point = *ppoint; // Examine each bit index beginnig with that following last bit index
// examined previously. Continue examining bits until pivot bit of
// current node is reached (unless loop is terminated prematurely).
for (index = prev_bit + 1; index < point->pivot_bit; index++) { // If some leaf in the current subtree cares about the value of the
// current bit, and if the new leaf cares about the value of the
// current bit, and these two leaves disagree about the value of
// this bit, then a new branch node should be inserted here.
if (BIT_OF(MASK(new_leaf), index) == 1 && BIT_OF(MASK(point), index) == 1 && BIT_OF(VALUE(new_leaf), index) != BIT_OF(VALUE(point), index)) { // Create new branch node; insert into tree; and set fields.
bit_value = BIT_OF(VALUE(new_leaf), index); NEW_RhizomeNode(&new_branch); if (new_branch == 0) { // Memory could not be allocated for this new node.
// Therefore, we pass an indication of failure up the stack.
return GPC_STATUS_RESOURCES; } *ppoint = new_branch; for (index0 = 0; index0 < rhizome->keybytes; index0++) { VALUE(new_branch)[index0] = VALUE(point)[index0] | VALUE(new_leaf)[index0]; MASK(new_branch)[index0] = MASK(point)[index0] | MASK(new_leaf)[index0]; IMASK(new_branch)[index0] = IMASK(point)[index0] & IMASK(new_leaf)[index0]; } // Pivot bit of new branch node is the bit that inspired the
// creation of this branch.
new_branch->pivot_bit = index; // The earlier subtree becomes the child whose bit disagreed
// with that of the new leaf.
new_branch->CHILDREN[1 - bit_value] = point; // If every leaf in the subtree cares about the value of this
// bit, then we can insert the new leaf as the other child of
// this branch.
if (BIT_OF(IMASK(point), index) == 1) { // Insert new leaf here and return.
new_branch->CHILDREN[bit_value] = new_leaf; return GPC_STATUS_SUCCESS; } // Otherwise, at least one leaf in the earlier subtree does not
// care about the value of this bit. Copy all such leaves
// (and necessary branches) to the other child of the new
// branch node.
child = replicate(rhizome, point, index); if (child == 0) { // Memory could not be allocated for the replica.
// Therefore, we remove the new node from the structure,
// delete the new node, and pass an indication of failure
// up the stack.
*ppoint = point; GpcFreeMem(new_branch, RhizomeTag); return GPC_STATUS_RESOURCES; } new_branch->CHILDREN[bit_value] = child; // Continue search on newly copied subtree.
ppoint = &new_branch->CHILDREN[bit_value]; point = *ppoint; } } // All bits have been examined up to the pivot bit of the current node.
// If this node is a leaf, then we have found a leaf with which the new
// leaf has no disagreements over bit values.
if (point->pivot_bit >= rhizome->keybits) { // Loop up the chain of godparents until one of the four cases
// below causes an exit from the subroutine.
while (1) { // Case 1: We have reached the end of the godparent chain.
if (point == 0) { // Insert new leaf at this point and return.
*ppoint = new_leaf; return GPC_STATUS_SUCCESS; } // Case 2: We discover that we have already inserted this leaf
// at the appropriate location. This can happen because two
// leaves in separate parts of the tree may have a common god-
// ancestor, and a leaf which is a further god-ancestor of that
// leaf will be reached more than once. Since the first
// occasion inserted the leaf, the second one can return without
// performing any action.
if (point == new_leaf) { return GPC_STATUS_SUCCESS; } // Compare mask bits of the new leaf to the current leaf.
sub = 0; super = 0; for (index = 0; index < rhizome->keybytes; index++) { sub |= MASK(new_leaf)[index] & ~MASK(point)[index]; super |= ~MASK(new_leaf)[index] & MASK(point)[index]; } // Case 3: The new leaf cares about at least one bit that the
// current leaf does not; and the current leaf does not care
// about any bits that the new leaf does not; thus, the new leaf
// should be a godchild of the current leaf.
if (sub != 0 && super == 0) { // Update imask field of new leaf; insert into chain;
// and return.
for (index0 = 0; index0 < rhizome->keybytes; index0++) { IMASK(new_leaf)[index0] &= IMASK(point)[index0]; } new_leaf->GODPARENT = point; *ppoint = new_leaf; return GPC_STATUS_SUCCESS; } // Case 4: Either the new leaf has the same value and mask as
// the current leaf, or there is a hierarchy conflict between
// the two leaves. In either case, terminate the insertion
// process and clean up (in insert() routine) anything done
// already.
if (sub != 0 || super == 0) { return GPC_STATUS_CONFLICT; } // None of the above cases occurred; thus, the new leaf should
// be a god-ancestor of the current leaf. Update the imask
// field of the current leaf, and continue with godparent of
// current leaf.
for (index0 = 0; index0 < rhizome->keybytes; index0++) { IMASK(point)[index0] &= IMASK(new_leaf)[index0]; } ppoint = &point->GODPARENT; point = *ppoint; } } // The current node is not a leaf node. Thus, we recurse on one or both
// of the child nodes of the current node. First, update the fields of
// the current node to reflect the insertion of the new leaf into the
// subtree.
for (index0 = 0; index0 < rhizome->keybytes; index0++) { VALUE(point)[index0] |= VALUE(new_leaf)[index0]; MASK(point)[index0] |= MASK(new_leaf)[index0]; IMASK(point)[index0] &= IMASK(new_leaf)[index0]; } // If the new leaf doesn't care about the value of the pivot bit of the
// current leaf, then we must recurse on both children. We can only
// replace a single recursive call with iteration, so we perform a true
// recursion in this case, and we recurse on child 1.
if (BIT_OF(MASK(new_leaf), point->pivot_bit) == 0) { insert_status = node_insert(rhizome, new_leaf, &point->CHILDREN[1], point->pivot_bit); if (insert_status != GPC_STATUS_SUCCESS) { return insert_status; } } // Update the values of prev_bit and ppoint to reflect the same
// conditions that would hold in a recursive call. The pseudo-recursion
// is performed on the bit indicated by the value of the pivot bit of
// the new leaf. If the new leaf does not care about this bit, then
// this value will be a 0, and we recursed on child 1 above. If the new
// leaf does care about the value of this bit, then we continue down the
// appropriate path.
prev_bit = point->pivot_bit; ppoint = &point->CHILDREN[BIT_OF(VALUE(new_leaf), point->pivot_bit)]; } }
// Remove references to leaf from subtree pointed to by *ppoint. Update *ppoint
// if necessary due to removal of branch nodes.
//
static void node_remove( Rhizome *rhizome, RhizomeNode *leaf, RhizomeNode **ppoint) { int pivot_bit, bit_value, index0; RhizomeNode *point, *child, *child0, *child1; point = *ppoint; pivot_bit = point->pivot_bit; if (pivot_bit < rhizome->keybits) { // The current node is a branch node.
if (BIT_OF(MASK(leaf), pivot_bit) == 1) { // The leaf to be removed cares about this node's pivot bit;
// therefore, we need only recurse on one of the current node's
// children.
bit_value = BIT_OF(VALUE(leaf), pivot_bit); node_remove(rhizome, leaf, &point->CHILDREN[bit_value]); child = point->CHILDREN[bit_value]; if (child != 0 && BIT_OF(MASK(child), pivot_bit) == 1) { // Some leaf in the same subtree as the removed leaf cares about
// the value of this node's pivot bit; therefore, this node
// still has reason to exist. Update its fields to reflect the
// change in one of its subtrees.
child0 = point->CHILDREN[0]; child1 = point->CHILDREN[1]; for (index0 = 0; index0 < rhizome->keybytes; index0++) { VALUE(point)[index0] = VALUE(child0)[index0] | VALUE(child1)[index0]; MASK(point)[index0] = MASK(child0)[index0] | MASK(child1)[index0]; IMASK(point)[index0] = IMASK(child0)[index0] & IMASK(child1)[index0]; } } else { // No leaf in the same subtree as the removed leaf cares about
// the value of this node's pivot bit; therefore, there is no
// longer any reason for this node to exist. Have the other
// subtree take the current node's place in the tree; call
// remove() to remove the unneeded subtree; and delete the
// current node.
*ppoint = point->CHILDREN[1 - bit_value]; if (child != 0) { eliminate(rhizome, child); } GpcFreeMem(point, RhizomeTag); } } else { // The leaf to be removed does not care about this node's pivot bit;
// therefore, we must recurse on both of the current node's
// children. This node must still be necessary, since we have not
// removed any leaf which cares about this node's value. So we
// update its fields to reflect the change in its two subtrees.
node_remove(rhizome, leaf, &point->CHILDREN[0]); node_remove(rhizome, leaf, &point->CHILDREN[1]); child0 = point->CHILDREN[0]; child1 = point->CHILDREN[1]; for (index0 = 0; index0 < rhizome->keybytes; index0++) { VALUE(point)[index0] = VALUE(child0)[index0] | VALUE(child1)[index0]; MASK(point)[index0] = MASK(child0)[index0] | MASK(child1)[index0]; IMASK(point)[index0] = IMASK(child0)[index0] & IMASK(child1)[index0]; } } } else { // The current node is a leaf node.
if (point == leaf) { // The current node is the leaf to be removed; therefore, remove it
// from chain of godparents.
*ppoint = leaf->GODPARENT; } else { // The current node is not leaf to be removed. Therefore, if this
// node has a godparent, then recurse on that godparent. If this
// node does not have a godparent, then the to-be-removed leaf
// either already was removed by a different path, or it was never
// inserted to begin with. The latter might be the case if remove()
// was called from the catch clause of insert().
if (point->GODPARENT != 0) { node_remove(rhizome, leaf, &point->GODPARENT); } // We are now popping back up the recursion stack. If this node
// does not have a godparent, or if it did but it does not anymore,
// then initialize imask to mask; otherwise, copy the godparent's
// value of imask. Since the godparent chain follows a strict
// hierarchy, and since imask is formed by successive conjunction,
// all leaves in any given godparent chain will have the same value
// of imask, namely the mask value of the highest god-ancestor.
if (point->GODPARENT == 0) { for (index0 = 0; index0 < rhizome->keybytes; index0++) { IMASK(point)[index0] = MASK(point)[index0]; } } else { for (index0 = 0; index0 < rhizome->keybytes; index0++) { IMASK(point)[index0] = IMASK(point->GODPARENT)[index0]; } } } } }
// Replicate all nodes in a subtree which do not care about the value of
// pivot_bit.
//
static RhizomeNode * replicate( Rhizome *rhizome, RhizomeNode *source, int pivot_bit) { int index0, current_bit; RhizomeNode *new_node, *child0, *child1; // If this routine were fully recursive, the following while statement
// would be an if statement. However, recursion has been replaced by
// iteration where possible, so the following code loops until bottoming
// out when a leaf node is reached.
while (source->pivot_bit < rhizome->keybits) { if (BIT_OF(IMASK(source->CHILDREN[0]), pivot_bit) == 0) { if (BIT_OF(IMASK(source->CHILDREN[1]), pivot_bit) == 0) { // Both subtrees contain leaves which do not care about the
// pivot bit; therefore, we may need to make a copy of the
// current node. It is not guaranteed that we need to make
// a copy, since it may be a common leaf in both subtrees
// that does not care about the pivot bit. This may happen
// for a leaf which is a godparent of two leaves, one in each
// subtree. Recurse on each child and examine results.
child0 = replicate(rhizome, source->CHILDREN[0], pivot_bit); if (child0 == 0) { // Memory could not be allocated for the child replica.
// Therefore, we abort the replication process and pass an
// indication of failure op the stack.
return 0; } child1 = replicate(rhizome, source->CHILDREN[1], pivot_bit); if (child1 == 0) { // Memory could not be allocated for the child replica.
// Therefore, we abort the replication process, eliminate
// the other child replica, and pass an indication of
// failure op the stack.
eliminate(rhizome, child0); return 0; // return null pointer
} current_bit = source->pivot_bit; if (BIT_OF(MASK(child0), current_bit) == 1) { if (BIT_OF(MASK(child1), current_bit) == 1) { // Both replicated child subtrees contain leaves which
// care about the current node's bit. Since any node
// which is a godparent of nodes in both subtrees could
// not possibly care about the current node's bit, we
// know that we need to make a copy of the current node.
NEW_RhizomeNode(&new_node); if (new_node == 0) { // Memory could not be allocated for this new node.
// Therefore, we have to eliminate both children
// and pass an indication of failure up the stack.
eliminate(rhizome, child0); eliminate(rhizome, child1); return 0; // return null pointer
} for (index0 = 0; index0 < rhizome->keybytes; index0++) { VALUE(new_node)[index0] = VALUE(child0)[index0] | VALUE(child1)[index0]; MASK(new_node)[index0] = MASK(child0)[index0] | MASK(child1)[index0]; IMASK(new_node)[index0] = IMASK(child0)[index0] & IMASK(child1)[index0]; } new_node->pivot_bit = current_bit; new_node->CHILDREN[0] = child0; new_node->CHILDREN[1] = child1; return new_node; } // Child 0's subtree contains a leaf that cares about the
// current bit; however, child 1's subtree does not. Thus,
// all leaves which are in child 1's subtree are also in
// child 0's subtree, so we only need to keep the latter.
// We therefore eliminate child 1's subtree, and we return
// child 0 as the new subtree at this location, since we
// do not need to create a new branch node here.
eliminate(rhizome, child1); return child0; } // Child 0's subtree does not contain a leaf that cares about
// the current node's bit. Thus, all leaves which are in child
// 0's subtree are also in child 1's subtree, so we only need to
// keep the latter. We therefore eliminate child 0's subtree,
// and we return child 1 as the new subtree at this location,
// since we do not need to create a new branch node here.
eliminate(rhizome, child0); return child1; } // Child 0's subtree contains a leaf which does not care about the
// pivot bit; however, child 1's subtree does not. Therefore, we
// recurse on child 0. Rather than truly recursing, we update the
// value of source and iterate once through the while loop.
source = source->CHILDREN[0]; } else { // Child 0's subtree does not contain a leaf which does not care
// about the pivot bit. Child 1's subtree must contain such a leaf,
// since the current node's subtree contains such a leaf. Thus, we
// recurse on child 1. Rather than truly recursing, we update the
// value of source and iterate once through the while loop.
source = source->CHILDREN[1]; } } // A leaf node has been reached. We now iterate through the godparents of
// the leaf until we find one which does not care about the pivot bit.
// Once we find it, we know that all godparents of that leaf also do not
// care about the pivot bit, since the godparents are arranged in a strict
// hierarchy. We thus return the first leaf found which does not care about
// the value of the pivot bit.
while (BIT_OF(MASK(source), pivot_bit) == 1) { source = source->GODPARENT; } return source; }
// Eliminate an entire subtree.
//
static void eliminate( Rhizome *rhizome, RhizomeNode *point) { RhizomeNode *child; // Partial recursion removal. The while loop takes the place of one of the
// recursive calls to eliminate(). We eliminate each node and recursively
// eleminate each subtree under the node. We do not eliminate leaves, since
// there is only one copy of each leaf stored in the entire structure.
while (point->pivot_bit < rhizome->keybits) { eliminate(rhizome, point->CHILDREN[0]); child = point->CHILDREN[1]; GpcFreeMem(point, RhizomeTag); point = child; } }
// Coalesce leaves of subtree into a linked list and eliminate subtree. This
// routine is called by the destructor so that it can deallocate the leaf nodes
// after the branch nodes are eliminated.
//
static void coalesce( Rhizome *rhizome, RhizomeNode **leaf_list, RhizomeNode *point) { RhizomeNode *child, *godparent; // Partial recursion removal. This while loop takes the place of one of
// the recursive calls to coalesce(). This performs an inorder traversal.
// We delete each branch node after we have visited it, just as in the
// eliminate() routine.
while (point->pivot_bit < rhizome->keybits && point->pivot_bit >= 0) { coalesce(rhizome, leaf_list, point->CHILDREN[0]); child = point->CHILDREN[1]; GpcFreeMem(point, RhizomeTag); point = child; } // Once we have found a leaf, we search through the chain of godparents,
// adding to the list each leaf node that is not already in the list.
// A pivot_bit of -1 indicates that the leaf is already in the list.
// If a leaf is in the list, then so are all of its godparents.
while (point != 0 && point->pivot_bit >= 0) { godparent = point->GODPARENT; point->pivot_bit = -1; point->GODPARENT = *leaf_list; *leaf_list = point; point = godparent; } }
|