795 lines
28 KiB
C
795 lines
28 KiB
C
/*
|
|
* 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 "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
// 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,\
|
|
NAT_TAG_RHIZOME)/*;\
|
|
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, NAT_TAG_RHIZOME);
|
|
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, NAT_TAG_RHIZOME);
|
|
}
|
|
|
|
// 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, NAT_TAG_RHIZOME);
|
|
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, NAT_TAG_RHIZOME);
|
|
}
|
|
}
|
|
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, NAT_TAG_RHIZOME);
|
|
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, NAT_TAG_RHIZOME);
|
|
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;
|
|
}
|
|
}
|