diff options
Diffstat (limited to 'private/newsam/server/gentab2.c')
-rw-r--r-- | private/newsam/server/gentab2.c | 3561 |
1 files changed, 3561 insertions, 0 deletions
diff --git a/private/newsam/server/gentab2.c b/private/newsam/server/gentab2.c new file mode 100644 index 000000000..fea707673 --- /dev/null +++ b/private/newsam/server/gentab2.c @@ -0,0 +1,3561 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + gentab2.c + +Abstract: + + GenericTable2 package + + Generic table services for maintaining data sets. The primary + characteristic of this generic table package is that it maintains + a relatively balanced tree, which provides for good (O(log(N)) + performance. + + The GenericTable2 routines are similar to the original + GenericTable routines provided by Gary Kimure except that the + GenericTable2 routines use a 2-3-tree rather than a splay tree. + 2-3-trees are described in "Data Structures And Algorithms", by + Aho, Hopcroft, and Ullman, published by Addison Wesley Publishing + Company. + + Another difference between this package and the original Generic + Table package is that this one references element buffers that are + inserted rather than copying the data (as in the orignal package). + This characteristic is nice if you have to sort large numbers of + records by multiple keys + + 2-3-trees have better characteristics than splay-trees when the + data being maintained is not random. For example, maintaining a + dictionary, in which the data quite often is provided in an orderly + manner, is an ideal application for 2-3-trees. + + This package does not support the retrieval of elements in inserted + order that is supported in the original Generic Table package. + + Differences between the algorithm outlined in Aho, et al and what + is coded here are: + + 1) I provide an additional means of obtaining the elements + in the tree in sorted order (for enumeration performance). + I keep a linked list of elements in addition to the tree + structure. + + 1) Aho et al point directly to elements in the tree from + nodes in the tree. In order to allow me to keep the linked + list mentioned in (1), I have a separate leaf element pointed + to from nodes which point to the element values. This leaf + component has the LIST_ENTRY structures used to link the + elements together. + + 3) Aho et al's algorithms ignore the fact that they may fail + to allocate memory (that is, they assume the Pascal "new" + function always succeeds). This package assumes that + any memory allocation may fail and will always leave the + tree in a valid form (although an insertion may fail in + this case). + + +Author: + + Jim Kelly (JimK) 20-Jan-1994 + +Environment: + + Run time library, user or kernel mode. + +Revision History: + + +--*/ + +/////////////////////////////////////////////////////////////////////////////// +// // +// Includes // +// // +/////////////////////////////////////////////////////////////////////////////// + + +#include <nt.h> +#include <ntrtl.h> +#include <samsrvp.h> + + + + +////////////////////////////////////////////////////////////////////////// +// // +// defines ... // +// // +////////////////////////////////////////////////////////////////////////// + +// +// The following define controls the diagnostic capabilities that +// are built into this package. +// + +#if DBG +#define GTBP_DIAGNOSTICS 1 +#endif // DBG + + +// +// These definitions are useful diagnostics aids +// + +#if GTBP_DIAGNOSTICS + +// +// defining the following symbol causes significant amounts of +// development assistance code to be built +// + +//#define GTBP_DEVELOPER_BUILD 1 + +// +// Global Diagnostics Flags +// + +ULONG GtbpGlobalFlag; + +// +// Test for diagnostics enabled +// + +#define IF_GTBP_GLOBAL( FlagName ) \ + if (GtbpGlobalFlag & (GTBP_DIAG_##FlagName)) + +// +// Diagnostics print statement +// + +#define GtbpDiagPrint( FlagName, _Text_ ) \ + IF_GTBP_GLOBAL( FlagName ) \ + DbgPrint _Text_ + + +#else + +// +// No diagnostics included in build +// + +// +// Test for diagnostics enabled +// + +#define IF_GTBP_GLOBAL( FlagName ) if (FALSE) + + +// +// Diagnostics print statement (nothing) +// + +#define GtbpDiagPrint( FlagName, Text ) ; + + +#endif // GTBP_DIAGNOSTICS + +// +// The following flags enable or disable various diagnostic +// capabilities within SAM. These flags are set in +// GtbpGlobalFlag. +// +// INSERT - print diagnostic messages related to insertion +// operations. +// +// DELETION - print diagnostic messages related to deletion +// operations. +// +// LEAF_AND_NODE_ALLOC - print diagnostic messages related +// to allocation of leaf and node objects for insertion +// operations. +// +// ENUMERATE - print diagnostic messages related to enumeration +// operations. This includes getting restart keys. +// +// LOOKUP - print diagnostic messages related to element lookup +// operations. +// +// COLLISIONS - print diagnostic messages indicating when collisions +// occur on insert. +// +// VALIDATE - print diagnostic messages to be printed during table +// validations. +// + +#define GTBP_DIAG_INSERT ((ULONG) 0x00000001L) +#define GTBP_DIAG_DELETION ((ULONG) 0x00000002L) +#define GTBP_DIAG_LEAF_AND_NODE_ALLOC ((ULONG) 0x00000004L) +#define GTBP_DIAG_ENUMERATE ((ULONG) 0X00000008L) +#define GTBP_DIAG_LOOKUP ((ULONG) 0X00000010L) +#define GTBP_DIAG_COLLISIONS ((ULONG) 0X00000020L) +#define GTBP_DIAG_VALIDATE ((ULONG) 0X00000040L) + + +////////////////////////////////////////////////////////////////////////// +// // +// Macros ... // +// // +////////////////////////////////////////////////////////////////////////// + +// +// GtbpChildrenAreLeaves( +// IN GTB_TWO_THREE_NODE N +// ) +// Returns TRUE if children of N are leaves. +// Otherwise returns FALSE. +// + +#define GtbpChildrenAreLeaves( N ) ((((N)->Control) & GTB_CHILDREN_ARE_LEAVES) != 0) + + +/////////////////////////////////////////////////////////////////////////////// +// // +// Private structures and definitions // +// // +/////////////////////////////////////////////////////////////////////////////// + +// +// GTB_TWO_THREE_NODE.Control field values +// + +#define GTB_CHILDREN_ARE_LEAVES (0x00000001) + + + +////////////////////////////////////////////////////////////////////////// +// // +// Internal Routine Definitions ... // +// // +////////////////////////////////////////////////////////////////////////// + +VOID +GtbpDeleteFromSubTree ( + IN PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN PVOID Element, + OUT PGTB_TWO_THREE_LEAF *LowOfNode, + OUT BOOLEAN *ElementDeleted, + OUT BOOLEAN *OnlyOneChildLeft + ); + +BOOLEAN +GtbpInsertIntoSubTree ( + PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN BOOLEAN NodeIsLeaf, + IN PVOID Element, + IN ULONG SplitCount, + IN PVOID *FoundElement, + OUT PGTB_TWO_THREE_NODE *ExtraNode, + OUT PGTB_TWO_THREE_LEAF *LowLeaf, + OUT PLIST_ENTRY *AllocatedNodes + ); + +ULONG +GtbpNumberOfChildren( + IN PGTB_TWO_THREE_NODE Node + ); + +VOID +GtbpGetSubTreeOfElement( + IN PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN PVOID Element, + OUT PGTB_TWO_THREE_NODE *SubTreeNode, + OUT ULONG *SubTree + ); + +VOID +GtbpCoalesceChildren( + IN PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN ULONG SubTree, + OUT BOOLEAN *OnlyOneChildLeft + ); + +VOID +GtbpSplitNode( + IN PGTB_TWO_THREE_NODE Node, + IN PGTB_TWO_THREE_NODE NodePassedBack, + IN PGTB_TWO_THREE_LEAF LowPassedBack, + IN ULONG SubTree, + IN PLIST_ENTRY AllocatedNodes, + OUT PGTB_TWO_THREE_NODE *NewNode, + OUT PGTB_TWO_THREE_LEAF *LowLeaf + ); + +PGTB_TWO_THREE_LEAF +GtbpAllocateLeafAndNodes( + IN PRTL_GENERIC_TABLE2 Table, + IN ULONG SplitCount, + OUT PLIST_ENTRY *AllocatedNodes + ); + +PGTB_TWO_THREE_NODE +GtbpGetNextAllocatedNode( + IN PLIST_ENTRY AllocatedNodes + ); + + + + +////////////////////////////////////////////////////////////////////////// +// // +// Exported Services ... // +// // +////////////////////////////////////////////////////////////////////////// + + +VOID +RtlInitializeGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PRTL_GENERIC_2_COMPARE_ROUTINE CompareRoutine, + PRTL_GENERIC_2_ALLOCATE_ROUTINE AllocateRoutine, + PRTL_GENERIC_2_FREE_ROUTINE FreeRoutine + ) + +/*++ + +Routine Description: + + Initialize the table by initializing the corresponding + (empty) two-three tree and the extra linked-list we have + going through the tree. + + Two-three trees are described in "Data Structures And Algorithms" + by Alfred Aho, John Hopcroft, and Jeffrey Ullman (Addison Wesley + publishing). + +Arguments: + + Table - Pointer to the generic table to be initialized. This gets + typecast internally, but we export this so that we don't have to + invent another type of generic table for users to worry about. + + CompareRoutine - User routine to be used to compare to keys in the + table. + + AllocateRoutine - Used by the table package to allocate memory + when necessary. + + FreeRoutine - Used by the table package to free memory previously + allocated using the AllocateRoutine. + +Return Value: + + None. + +--*/ +{ + + + // + // Tree is empty. + // + + Table->Root = NULL; + Table->ElementCount = 0; + + Table->Compare = CompareRoutine; + Table->Allocate = AllocateRoutine; + Table->Free = FreeRoutine; + + InitializeListHead(&Table->SortOrderHead); + + return; +} + + +PVOID +RtlInsertElementGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID Element, + PBOOLEAN NewElement + ) + +/*++ + +Routine Description: + + + This function inserts an element into the table. + + If the element is successfully inserted into the table + then NewElement will be returned as TRUE and the function will + return the value passed via the Element parameter. + + If the element already exists in the table, then NewElement + is returned as FALSE and the function will return the value + of the element already found in the table. + + + The caller is responsible for ensuring that an element referenced by + the table is not modified or deallocated while it is still in the + table. + +Arguments: + + Table - Pointer to the generic table to which the Element is to + be inserted. + + Element - Pointer to the element to be entered into the table. + + NewElement - Receives TRUE if the element was added to the table. + Receives FALSE if the element collided with an element already + in the table (that is, an element with the same comparison + value already exists in the table). + + +Return Value: + + Pointer to the element inserted, or the element that was already + in the table with the same value as the one being inserted. + + If NULL is returned, then memory could not be allocated to add + the new element. + +--*/ +{ + + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + + PGTB_TWO_THREE_NODE + NodePassedBack, + NewNode, + SubTreeNode, + Node; + + PGTB_TWO_THREE_LEAF + Leaf, + LowLeaf, + LowPassedBack; + + ULONG + SplitCount, + SubTree; + + PVOID + FoundElement; + + PLIST_ENTRY + AllocatedNodes; + + BOOLEAN + NodeIsLeaf; + + + GtbpDiagPrint( INSERT, + ("GTB: Inserting Element 0x%lx into table 0x%lx\n", Element, Table)); + + // + // Except for errors, one of the following will occur: + // + // o There is no root ==> + // 1) Allocate a root and leaf + // 2) put the element in the leaf and make it the + // 3) first child of the root + // + // o There is a root with only one child ==> + // 1) If the elements are equal, return without new entry + // 2) If the new element is less, move child 1 to 2 and + // make new leaf child 1. + // 3) Otherwise element is greater, allocate it a leaf + // and make it child 2. + // + // o There is a root with at least two children ==> + // 1) If there are already 3 children, then set split + // count = 2, otherwise set it to 1. + // 2) Call normal insertion routine to insert into + // appropriate SubTree. + // 3) If there is a split needed, then establish + // a newly allocated node as the root, and make it the + // parent of the current node. Then use the normal + // split routine. + // + + + + + + // + // If empty, then create a root node and add the element. + // + + if (Table->ElementCount == 0) { + + GtbpDiagPrint( INSERT, + ("GTB: Table empty. Creating root node.\n")); + + NewNode = (PGTB_TWO_THREE_NODE) + ((*Table->Allocate)( sizeof(GTB_TWO_THREE_NODE) )); + if (NewNode == NULL) { + GtbpDiagPrint(INSERT, + ("GTB: Couldn't allocate memory for root node.\n")); + (*NewElement) = FALSE; + return( NULL ); + } + GtbpDiagPrint( INSERT, + ("GTB: New root node is: 0x%lx\n", NewNode)); + + + NewNode->ParentNode = NULL; // Doesn't have a parent. Special case. + NewNode->Control = GTB_CHILDREN_ARE_LEAVES; + NewNode->SecondChild = NULL; + NewNode->ThirdChild = NULL; + + // + // Allocate a leaf and put the element in it. + // + + Leaf = (PGTB_TWO_THREE_LEAF) + ((*Table->Allocate)( sizeof(GTB_TWO_THREE_LEAF) )); + + if (Leaf == NULL) { + GtbpDiagPrint(INSERT, + ("GTB: Couldn't allocate memory for leaf.\n")); + ((*Table->Free)( NewNode )); + (*NewElement) = FALSE; + return( NULL ); + } + + + InsertHeadList( &Table->SortOrderHead, &Leaf->SortOrderEntry ); + Leaf->Element = Element; + NewNode->FirstChild = (PGTB_TWO_THREE_NODE)Leaf; + + Table->Root = NewNode; + Table->ElementCount++; + ASSERT(Table->ElementCount == 1); + (*NewElement) = TRUE; + return(Element); + } + + + // + // We have a root with at least one child in it. + // + + if (Table->Root->SecondChild == NULL) { + + // + // The root doesn't have two children. + // If it didn't have any children it would have been + // deallocated. So, it must have a degenerate case of + // only one child. + // + + Leaf = (PGTB_TWO_THREE_LEAF)Table->Root->FirstChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + (*NewElement) = FALSE; + + GtbpDiagPrint( COLLISIONS, + ("GTB: Insertion attempt resulted in collision.\n" + " Element NOT being inserted.\n" + " Elements in table: %d\n", + Table->ElementCount)); + return( Leaf->Element ); + } + + + // + // Need a new leaf + // + + Leaf = (PGTB_TWO_THREE_LEAF) + ((*Table->Allocate)( sizeof(GTB_TWO_THREE_LEAF) )); + + if (Leaf == NULL) { + GtbpDiagPrint(INSERT, + ("GTB: Couldn't allocate memory for leaf.\n")); + (*NewElement) = FALSE; + return( NULL ); + } + Leaf->Element = Element; + + // + // it is either the first child or second + // + + if (CompareResult == GenericLessThan) { + + // + // Move the first child to be the second child and make + // a new first child leaf for the new element. + // + + InsertHeadList( &Table->SortOrderHead, &Leaf->SortOrderEntry ); + + + + Table->Root->SecondChild = Table->Root->FirstChild; + Table->Root->LowOfSecond = (PGTB_TWO_THREE_LEAF) + Table->Root->SecondChild; //it is the leaf + + Table->Root->FirstChild = (PGTB_TWO_THREE_NODE)Leaf; + + + } else { + + // + // new element is greater than existing element. + // make it the second child. + // + + InsertTailList( &Table->SortOrderHead, &Leaf->SortOrderEntry ); + + Table->Root->SecondChild = (PGTB_TWO_THREE_NODE)Leaf; + Table->Root->LowOfSecond = Leaf; + + } + + Table->ElementCount++; + ASSERT(Table->ElementCount == 2); + + (*NewElement) = TRUE; //Set return value + return(Element); + + } + + // + // Normal insertion. + // If we get an ExtraNode coming back, then we may have to + // split the root. Normally for a node with three children + // you would need to allow for one node in a split. However, + // we will need a new root as well, so allow for two new nodes. + // + + if (Table->Root->ThirdChild != NULL) { + SplitCount = 2; + } else { + SplitCount = 0; + } + + GtbpGetSubTreeOfElement( Table, Table->Root, Element, &SubTreeNode, &SubTree); + NodeIsLeaf = GtbpChildrenAreLeaves(Table->Root); + + (*NewElement) = GtbpInsertIntoSubTree ( Table, + SubTreeNode, + NodeIsLeaf, + Element, + SplitCount, + &FoundElement, + &NodePassedBack, + &LowPassedBack, + &AllocatedNodes + ); + + // + // One of several things could have happened: + // + // 1) We didn't have enough memory to add the new element. + // In this case we are done and simply return. + // + // 2) The element was added, and no-rearrangement to this + // node is needed. In this case we are done and simply + // return. + // + // 3) The element was added and caused a node to be pushed + // out of the SubTree. We have some work to do. + // + + + if ( (FoundElement == NULL) || // Insufficient memory, or + (NodePassedBack == NULL) ) { // no work for this node + + return(FoundElement); + } + + + Node = Table->Root; + if (Node->ThirdChild == NULL) { + + // + // Root doesn't yet have a third child, so use it. + // This might require shuffling the second child to the + // be the third child. + // + + if (SubTree == 2) { + + // + // NodePassedBack fell out of second SubTree and root does't + // have a third SubTree. Make that node the third SubTree. + // + + Node->ThirdChild = NodePassedBack; + Node->LowOfThird = LowPassedBack; + + } else { + + // + // Node fell out of first SubTree. + // Make the second SubTree the third SubTree and + // then make the passed back node the second SubTree. + // + + ASSERT(SubTree == 1); + + Node->ThirdChild = Node->SecondChild; + Node->LowOfThird = Node->LowOfSecond; + Node->SecondChild = NodePassedBack; + Node->LowOfSecond = LowPassedBack; + + } + } else { + + // + // Node already has three children - split it. + // Do this by setting a new parent first. + // + + NewNode = GtbpGetNextAllocatedNode( AllocatedNodes ); + ASSERT(NewNode != NULL); + + Table->Root = NewNode; + NewNode->ParentNode = NULL; + NewNode->Control = 0; + NewNode->FirstChild = Node; + NewNode->SecondChild = NULL; + NewNode->ThirdChild = NULL; + + Node->ParentNode = NewNode; + + + GtbpSplitNode( Node, + NodePassedBack, + LowPassedBack, + SubTree, + AllocatedNodes, + &NewNode, + &LowLeaf + ); + + Table->Root->SecondChild = NewNode; + Table->Root->LowOfSecond = LowLeaf; + } + + return(FoundElement); +} + + +BOOLEAN +RtlDeleteElementGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID Element + ) + +/*++ + +Routine Description: + + The function DeleteElementGenericTable2 will find and delete an element + from a generic table. If the element is located and deleted the return + value is TRUE, otherwise if the element is not located the return value + is FALSE. The user supplied input buffer is only used as a key in + locating the element in the table. + + The value of the passed element is compared to elements in the table + to determine whether or not the element is in the table. Therefore, + the Element passed in may be the address of the element in the table + to delete, or it may be an element with the same value that is not + in the table. + +Arguments: + + Table - Pointer to the table in which to (possibly) delete the + element referenced by the buffer. + + Element - Passed to the user comparasion routine. Its contents are + up to the user but one could imagine that it contains some + sort of key value. + +Return Value: + + BOOLEAN - If the table contained the Element then TRUE, otherwise FALSE. + +--*/ +{ + + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + PGTB_TWO_THREE_NODE + Node, + SubTreeNode; + + PGTB_TWO_THREE_LEAF + Leaf, + LowOfNode; + + BOOLEAN + ElementDeleted, + OnlyOneChildLeft; + + ULONG + SubTree; + + GtbpDiagPrint( DELETION, + ("GTB: Request received to delete element 0x%lx\n", Element)); + + + // + // There are the following special cases: + // + // 1) The table is empty. + // 2) The table has only one leaf + // + // Otherwise, all operations work the same. + // + + if (Table->ElementCount == 0) { + GtbpDiagPrint( DELETION, + ("GTB: No elements in table to delete.\n")); + return(FALSE); + } + + if (GtbpChildrenAreLeaves(Table->Root)) { + + + // + // See if any of the elements match the one passed in. + // If so, delete the element and shift larger elements + // to take up the free'd child's spot (unless it is the + // third child). + // + + if (Table->Root->ThirdChild != NULL) { + Leaf = (PGTB_TWO_THREE_LEAF)Table->Root->ThirdChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + + GtbpDiagPrint( DELETION, + ("GTB: Deleting child 3 (0x%lx) from root node.\n" + " Element count before deletion: %d\n", + Leaf, Table->ElementCount)); + + RemoveEntryList( &Leaf->SortOrderEntry ); + (*Table->Free)(Leaf); + Table->Root->ThirdChild = NULL; + + Table->ElementCount--; + ASSERT(Table->ElementCount == 2); + + + return(TRUE); + } + } + + // + // Try second child + // + + if (Table->Root->SecondChild != NULL) { + Leaf = (PGTB_TWO_THREE_LEAF)Table->Root->SecondChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + + GtbpDiagPrint( DELETION, + ("GTB: Deleting child 2 (0x%lx) from root node.\n" + " Element count before deletion: %d\n", + Leaf, Table->ElementCount)); + + RemoveEntryList( &Leaf->SortOrderEntry ); + (*Table->Free)(Leaf); + Table->Root->SecondChild = Table->Root->ThirdChild; + Table->Root->ThirdChild = NULL; + + Table->Root->LowOfSecond = Table->Root->LowOfThird; + + Table->ElementCount--; + ASSERT(Table->ElementCount <= 2); + + return(TRUE); + } + } + + // + // Try first child + // + + ASSERT(Table->Root->FirstChild != NULL); + Leaf = (PGTB_TWO_THREE_LEAF)Table->Root->FirstChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + + GtbpDiagPrint( DELETION, + ("GTB: Deleting child 1 (0x%lx) from root node.\n" + " Element count before deletion: %d\n", + Leaf, Table->ElementCount)); + + RemoveEntryList( &Leaf->SortOrderEntry ); + (*Table->Free)(Leaf); + Table->Root->FirstChild = Table->Root->SecondChild; + Table->Root->SecondChild = Table->Root->ThirdChild; + Table->Root->LowOfSecond = Table->Root->LowOfThird; + Table->Root->ThirdChild = NULL; + + + Table->ElementCount--; + ASSERT(Table->ElementCount <= 2); + + // + // If that was the last element, then free the root as well. + // + + if (Table->ElementCount == 0) { + (*Table->Free)(Table->Root); + Table->Root = NULL; + + GtbpDiagPrint( DELETION, + ("GTB: Deleted last element. Deleting Root node.\n")); + + } + + return(TRUE); + } + + // + // Didn't match any of the leaves + // + + GtbpDiagPrint( DELETION, + ("GTB: No matching element found on DELETE attempt.\n")); + return(FALSE); + + } + + + + + + // + // We have: + // + // - Root with at least two children + // - Root's children are not leaves. + // + + // + // Find which sub-tree the element might be in. + // + + Node = Table->Root; + GtbpGetSubTreeOfElement( Table, Node, Element, &SubTreeNode, &SubTree ); + + GtbpDeleteFromSubTree( Table, + SubTreeNode, + Element, + &LowOfNode, + &ElementDeleted, + &OnlyOneChildLeft + ); + + + // + // If we deleted an entry from either the second or third + // subtree, then we may need to set a new LowOfXxx value. + // If it was the first subtree, then we simply have to return + // the LowLeaf value we received. + // + + if (LowOfNode != 0) { + if (SubTree == 2) { + Node->LowOfSecond = LowOfNode; + } else if (SubTree == 3) { + Node->LowOfThird = LowOfNode; + } + + } + + + // + // If the SubTreeNode has only one child left, then some + // adjustments are going to be necessary. Otherwise, + // we are done. + // + + if (OnlyOneChildLeft) { + + GtbpDiagPrint( DELETION, + ("GTB: Only one child left in 0x%lx\n", SubTreeNode)); + + // + // We are at the root and one of our children has only one + // child. Re-shuffle our kid's kids. + // + + GtbpCoalesceChildren( Table, + Node, + SubTree, + &OnlyOneChildLeft + ); + + // + // After coellescing our children, the root may have only one child + // left. Since we are the root node, we can't pass responsibility + // for fixing this problem to our caller. + // + + if (OnlyOneChildLeft) { + + GtbpDiagPrint( DELETION, + ("GTB: Root has only one child. \n" + " Replacing root with child: 0x%lx\n", Node->FirstChild)); + Table->Root = Table->Root->FirstChild; + Table->Root->ParentNode = NULL; + + (*Table->Free)((PVOID)Node); + } + } + + return(ElementDeleted); + +} + + +PVOID +RtlLookupElementGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID Element + ) + +/*++ + +Routine Description: + + + The function LookupElementGenericTable2 will find an element in a + generic table. If the element is located the return value is a + pointer to the user defined structure associated with the element, + otherwise if the element is not located the return value is NULL. + The user supplied input buffer is only used as a key in locating + the element in the table. + + +Arguments: + + Table - Pointer to the users generic table. + + Element - Used for the comparison. + +Return Value: + + PVOID - returns a pointer to the user data if found, otherwise NULL. + +--*/ + +{ + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + PGTB_TWO_THREE_NODE + Node; + + PGTB_TWO_THREE_LEAF + Leaf; + + ULONG + SubTree; + + + GtbpDiagPrint( LOOKUP, + ("GTB: Looking up element 0x%lx in table 0x%lx\n", + Element, Table)); + // + // If the table is empty, then no possible match. + // + + if (Table->ElementCount == 0) { + GtbpDiagPrint( LOOKUP, + ("GTB: Element not found. No elements in table.\n")); + return(NULL); + } + + Node = Table->Root; + + // + // traverse the tree until we reach a node that has leaves as + // children. + // + // We don't need to use recursion here because there + // is no tree re-structuring necessary. That is, there + // is no need to perform any operations back up the tree + // once we find the element, so it is much more efficient + // not to use recursion (which uses lots of push, call, + // pop, and ret instructions rather than short loop + // termination tests). + // + + while (!GtbpChildrenAreLeaves(Node)) { + GtbpGetSubTreeOfElement( Table, Node, Element, &Node, &SubTree ); + } + + // + // We are at the node which "might" contain the element. + // See if any of the children match. + // + + // + // Try first child + // + + Leaf = (PGTB_TWO_THREE_LEAF)Node->FirstChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + GtbpDiagPrint( LOOKUP, + ("GTB: Element found: 2nd child (0x%lx) of node 0x%lx\n", + Leaf, Node)); + return(Leaf->Element); + } + + // + // Try second child + // + + if (Node->SecondChild != NULL) { // Must allow for Root node case + Leaf = (PGTB_TWO_THREE_LEAF)Node->SecondChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + GtbpDiagPrint( LOOKUP, + ("GTB: Element found: 2nd child (0x%lx) of node 0x%lx\n", + Leaf, Node)); + return(Leaf->Element); + } + } + // + // Try third child + // + + if (Node->ThirdChild != NULL) { + Leaf = (PGTB_TWO_THREE_LEAF)Node->ThirdChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + GtbpDiagPrint( LOOKUP, + ("GTB: Element found: 3rd child (0x%lx) of node 0x%lx\n", + Leaf, Node)); + return(Leaf->Element); + } + } + + + GtbpDiagPrint( LOOKUP, + ("GTB: Element NOT found in node 0x%lx\n", Node)); + + return(NULL); + +} + + +PVOID +RtlEnumerateGenericTable2 ( + PRTL_GENERIC_TABLE2 Table, + PVOID *RestartKey + ) + +/*++ + +Routine Description: + + + The function EnumerateGenericTable2 will return to the + caller, one-by-one, the elements of a table (in sorted order). + The return value is a pointer to the user defined structure + associated with the element. + + The input parameter RestartKey indicates where the enumeration should + proceed from. If there are no more new elements to return the return + value is NULL. + + A RestartKey value of NULL will cause the enumeration to proceed + from the beginning of the list. + + As an example of its use, to enumerate all of the elements in a table + the user would write: + + RestartKey = NULL; + for (ptr = EnumerateGenericTable2(Table, &RestartKey); + ptr != NULL; + ptr = EnumerateGenericTable2(Table, &RestartKey)) { + : + } + + + If you wish to initiate an enumeration at a point other than the first + entry, you may use RestartKeyByIndexGenericTable2() or + RestartKeyByValueGenericTable2(). If a RestartKey + for the I'th entry was obtained via RestartKeyByIndexGenericTable2(), + then passing that RestartKey to this routine will cause the (I+1)th + element to be returned. If a RestartKey was obtained matching + a value passed to RestartKeyByValueGenericTable2(), then passing + that RestartKey to this routine will cause the entry with the + next higher value to be returned. + + ! WARNING ! + A RestartKey value may become invalid and cause an access violation + if any entries have been removed from the table. If enumeration + is to be carried out and it is unknown whether or not the table + contents have changed, it is best to obtain a RestartKey using + RestartKeyByIndexGenericTable2() or + RestartKeyByValueGenericTable2(). + + +Arguments: + + Table - Pointer to the generic table to enumerate. + + RestartKey - Upon call, indicates where the enumeration is to + begin. Upon return, receives context that may be used to + continue enumeration in a successive call. NULL indicates + enumeration should start at the beginning of the table. + A return value of NULL indicates the last entry has been + returned. + +Return Value: + + PVOID - Pointer to the next enumerated element or NULL. + NULL is returned if the entire table has already been + enumerated. + +--*/ + +{ + PLIST_ENTRY + ListEntry; + + PGTB_TWO_THREE_LEAF + Leaf; + + ListEntry = (PLIST_ENTRY)(*RestartKey); + + // + // The restart key is a pointer to our leaf element. + // Since all leaves are linked together in the SortOrderList, + // this makes it really trivial to return the next element. + // + + if (ListEntry == NULL) { + ListEntry = &Table->SortOrderHead; //Point to previous element + } + + // + // RestartKey pointed to the last enumerated leaf. + // Advance to the new one. + // + + ListEntry = ListEntry->Flink; + + // + // See if we have reached the end of the list + // + + if (ListEntry == &Table->SortOrderHead) { + (*RestartKey) = NULL; + return(NULL); + } + + // + // Otherwise, return the address of the element from this leaf. + // + + Leaf = (PGTB_TWO_THREE_LEAF)((PVOID)ListEntry); + + (*RestartKey) = (PVOID)Leaf; + return(Leaf->Element); + +} + + + +PVOID +RtlRestartKeyByIndexGenericTable2( + PRTL_GENERIC_TABLE2 Table, + ULONG I, + PVOID *RestartKey + ) + +/*++ + +Routine Description: + + + The function RestartKeyByIndexGenericTable2 will return a RestartKey + which may then be passed to EnumerateGenericTable2() to perform + an enumeration of sorted elements following the I'th sorted element + (zero based). + + This routine also returns a pointer to the I'th element. + + I = 0 implies restart at the second sorted element. + + I = (RtlNumberGenericTable2Elements(Table)-1) will return the last + sorted element in the generic table. + + Values of I greater than (NumberGenericTableElements(Table)-1) + will return NULL and the returned RestartKey will cause an + enumeration to be performed from the beginning of the sorted list + if passed to EnumerateGenericTable2(). + + WARNING - You may be tempted to use this routine, passing + first 0, then 1, then 2, et cetera, to perform + enumerations. DON'T. This is a very expensive + operation compared to the enumeration call. + +Arguments: + + Table - Pointer to the generic table. + + I - Indicates the point following which you wish to be able + to resume enumeration. For example, if you pass 7 for I, + then a RestartKey will be returned that continues enumeration + at the 8th element (skipping elements 0 through 6). + + RestartKey - Receives context that may be used to continue + enumeration in a successive call. If there is no I'th + element, then NULL is returned. + + Return Value: + + PVOID - Pointer to the I'th Element. If there is no I'th element, + then returns NULL. + +--*/ + +{ + PLIST_ENTRY + ListEntry; + + PGTB_TWO_THREE_LEAF + Leaf; + + ULONG + i; + + if (I >= Table->ElementCount) { + (*RestartKey) = NULL; + return(NULL); + } + + // + // Point to the first entry on the list. + // + + ListEntry = Table->SortOrderHead.Flink; + + // + // Move to the desired index + // + + for (i=0; i<I; i++) { + ListEntry = ListEntry->Flink; + } + + + // + // Found the I'th element . + // + + (*RestartKey) = (PVOID)ListEntry; + Leaf = (PGTB_TWO_THREE_LEAF)((PVOID)ListEntry); + return(Leaf->Element); + +} + + +PVOID +RtlRestartKeyByValueGenericTable2( + PRTL_GENERIC_TABLE2 Table, + PVOID Element, + PVOID *RestartKey + ) + +/*++ + +Routine Description: + + + The function RestartKeyByValueGenericTable2 will return a RestartKey + which may then be passed to EnumerateGenericTable2() to perform + an enumeration of sorted elements. The RestartKey will have a + value that will cause the enumeration to begin starting with + the first element whose value is greater than the passed in element + value. + + There does not have to be an element in the tree whose value + exactly matches the passed in value. A pointer to the element + with the largest value that is less than or equal to the passed + in value will be returned and serve as the continuation point + for the enumeration. + + + +Arguments: + + Table - Pointer to the generic table. + + Value - points to an element whose value indicates where you + wish enumeration to continue. + + RestartKey - Receives context that may be used to continue + enumeration in a successive call. + + Return Value: + + PVOID - Pointer to the element with the largest value less than + or equal to the element value passed in. If there are no + elements in the table less than or equal to the passed value, + then a value of NULL will be returned. + +--*/ + +{ + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + PGTB_TWO_THREE_NODE + Node; + + PGTB_TWO_THREE_LEAF + Leaf; + + ULONG + Children, + SubTree; + + BOOLEAN + LargestElementPath; + + // + // This routine is real similar to LookupElement + // + + // + // handle the special "table is empty" case. + // + + if (Table->ElementCount == 0) { + (*RestartKey) = NULL; + return(NULL); + } + + + Node = Table->Root; + + // + // traverse the tree until we reach a node that has leaves as + // children. + // + // We don't need to use recursion here because there + // is no tree re-structuring necessary. That is, there + // is no need to perform any operations back up the tree + // once we find the element, so it is much more efficient + // not to use recursion (which uses lots of push, call, + // pop, and ret instructions rather than short loop + // termination tests). + // + + LargestElementPath = TRUE; + while (!GtbpChildrenAreLeaves(Node)) { + Children = GtbpNumberOfChildren( Node ); + GtbpGetSubTreeOfElement( Table, Node, Element, &Node, &SubTree ); + if (Children > SubTree) { //did we take the highest value path? + LargestElementPath = FALSE; + } + } + + Children = GtbpNumberOfChildren(Node); + + // + // We are at the node which "might" contain the element. + // See if any of the children match. + // + // MUST compare 3rd, then 2nd, then 1st child !! + // + + // + // Try third child... + // If we are evaluating the largest element in the + // table, then the RestartKey will be set to continue + // at the beginning of the table. Otherwise, it is + // set to continue from here. + // + + if (Children == 3) { + Leaf = (PGTB_TWO_THREE_LEAF)Node->ThirdChild; + CompareResult = (*Table->Compare)( Leaf->Element, Element ); + + if ( (CompareResult == GenericEqual) || + (CompareResult == GenericLessThan) ) { + if (LargestElementPath && (Children == 3)) { + (*RestartKey) = NULL; // Restart at beginning of list + } else { + (*RestartKey) = (PVOID)(Leaf); // Restart here + } + return(Leaf->Element); + } + } + + // + // Try second child + // + + if (Children >= 2) { // Must allow for Root node case + Leaf = (PGTB_TWO_THREE_LEAF)Node->SecondChild; + CompareResult = (*Table->Compare)( Leaf->Element, Element ); + + if ( (CompareResult == GenericEqual) || + (CompareResult == GenericLessThan) ) { + if (LargestElementPath && (Children == 2)) { + (*RestartKey) = NULL; // Restart at beginning of list + } else { + (*RestartKey) = (PVOID)(Leaf); // Restart here + } + return(Leaf->Element); + } + } + + // + // Try first child + // + + Leaf = (PGTB_TWO_THREE_LEAF)Node->FirstChild; + CompareResult = (*Table->Compare)( Leaf->Element, Element ); + + if ( (CompareResult == GenericEqual) || + (CompareResult == GenericLessThan) ) { + if (LargestElementPath && (Children == 1)) { + (*RestartKey) = NULL; // Restart at beginning of list + } else { + (*RestartKey) = (PVOID)(Leaf); // Restart here + } + return(Leaf->Element); + } + + + + (*RestartKey) = NULL; + return(NULL); +} + + +ULONG +RtlNumberElementsGenericTable2( + PRTL_GENERIC_TABLE2 Table + ) + +/*++ + +Routine Description: + + The function NumberGenericTableElements returns a ULONG value + which is the number of generic table elements currently inserted + in the generic table. + + +Arguments: + + Table - Pointer to the generic table. + + + Return Value: + + ULONG - The number of elements in the table. + +--*/ + +{ + return Table->ElementCount; +} + + +BOOLEAN +RtlIsGenericTable2Empty ( + PRTL_GENERIC_TABLE2 Table + ) +/*++ + +Routine Description: + + The function IsGenericTableEmpty will return to the caller TRUE if + the generic table is empty (i.e., does not contain any elements) + and FALSE otherwise. + + +Arguments: + + Table - Pointer to the generic table. + + + Return Value: + + BOOLEAN - True if table is empty, otherwise FALSE. + +--*/ + +{ + return (Table->ElementCount == 0); +} + + + +////////////////////////////////////////////////////////////////////////// +// // +// Internal (private) Services ... // +// // +////////////////////////////////////////////////////////////////////////// + + +VOID +GtbpDeleteFromSubTree ( + IN PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN PVOID Element, + OUT PGTB_TWO_THREE_LEAF *LowOfNode, + OUT BOOLEAN *ElementDeleted, + OUT BOOLEAN *OnlyOneChildLeft + ) + +/*++ + +Routine Description: + + Delete an element from a SubTree. + + +Arguments: + + Table - Points to the table. This is needed for comparison + and memory-free routine. + + Node - Points to the child node within which the element to + delete resides (if it is in the tree at all). + + Element - points to an element. We are to delete any element + found to be equal to this element. + + LowOfNode - If the first child of Node isn't changed, then + a zero will be returned to this parameter, signifying that + the caller doesn't have to worry about updating LowOfXxx values. + However, if the first child of Node does change, then this + value will point to the new Low Leaf for the node's subtrees. + + ElementDeleted - Receives a boolean value indicating whether or + not an element was actually deleted. TRUE is returned if + an element was deleted. FALSE is returned if no element + was deleted. + + OnlyOneChildLeft - Receives a boolean value indicating whether or + not ChildNode was reduced to having only a single child. + TRUE indicates ChildNode now has only one child. + FALSE indicates ChildNode has at least two children. + +Return Value: + + None. + +--*/ + +{ + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + PGTB_TWO_THREE_NODE + SubTreeNode; + + PGTB_TWO_THREE_LEAF + Leaf; + + ULONG + SubTree; + + (*LowOfNode) = 0; // Default is no change + (*OnlyOneChildLeft) = FALSE; // Default return value + + + // + // If we are a parent of leaves, then we can look for an + // element to delete. Otherwise, just find the subtree + // to continue or search in and recurse. + // + + if (GtbpChildrenAreLeaves(Node)) { + + (*ElementDeleted) = FALSE; // Default return value + + // + // See if any of the elements match the one passed in. + // If so, delete the element and shift larger elements + // to take up the free'd child's spot (unless it is the + // third child). + // + + if (Node->ThirdChild != NULL) { + Leaf = (PGTB_TWO_THREE_LEAF)Node->ThirdChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + + GtbpDiagPrint( DELETION, + ("GTB: Deleting 3rd child (0x%lx) of node 0x%lx\n" + " ElementCount before deletion: %d\n", + Leaf, Node, Table->ElementCount)); + + RemoveEntryList( &Leaf->SortOrderEntry ); + (*Table->Free)(Leaf); + Node->ThirdChild = NULL; + + Table->ElementCount--; + + (*ElementDeleted) = TRUE; + return; + } + } + + // + // Try second child + // + + Leaf = (PGTB_TWO_THREE_LEAF)Node->SecondChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + + GtbpDiagPrint( DELETION, + ("GTB: Deleting 2nd child (0x%lx) of node 0x%lx\n" + " ElementCount before deletion: %d\n", + Leaf, Node, Table->ElementCount)); + + RemoveEntryList( &Leaf->SortOrderEntry ); + (*Table->Free)(Leaf); + Node->SecondChild = Node->ThirdChild; + Node->LowOfSecond = Node->LowOfThird; + Node->ThirdChild = NULL; + + + // + // If we are down to the last element, let that + // be known. + // + + if (Node->SecondChild == NULL) { + GtbpDiagPrint( DELETION, + ("GTB: Only one child left in node (0x%lx).\n", + Node)); + (*OnlyOneChildLeft) = TRUE; + } + + Table->ElementCount--; + (*ElementDeleted) = TRUE; + return; + } + + // + // Try first child + // + + Leaf = (PGTB_TWO_THREE_LEAF)Node->FirstChild; + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + + GtbpDiagPrint( DELETION, + ("GTB: Deleting 1st child (0x%lx) of node 0x%lx\n" + " ElementCount before deletion: %d\n", + Leaf, Node, Table->ElementCount)); + + RemoveEntryList( &Leaf->SortOrderEntry ); + (*Table->Free)(Leaf); + Node->FirstChild = Node->SecondChild; + (*LowOfNode) = Node->LowOfSecond; + + Node->SecondChild = Node->ThirdChild; + Node->LowOfSecond = Node->LowOfThird; + + Node->ThirdChild = NULL; + + + // + // If we are down to the last element, let that + // be known. + // + + if (Node->SecondChild == NULL) { + GtbpDiagPrint( DELETION, + ("GTB: Only one child left in node (0x%lx).\n", + Node)); + (*OnlyOneChildLeft) = TRUE; + } + + Table->ElementCount--; + (*ElementDeleted) = TRUE; + return; + } + + // + // Didn't match any of the leaves + // + + GtbpDiagPrint( DELETION, + ("GTB: No matching element found on DELETE attempt.\n")); + + return; // Default value already set + } + + // + // Find a subtree to continue our search... + // + + GtbpGetSubTreeOfElement( Table, Node, Element, &SubTreeNode, &SubTree ); + + GtbpDeleteFromSubTree( Table, + SubTreeNode, + Element, + LowOfNode, + ElementDeleted, + OnlyOneChildLeft + ); + + + // + // If we deleted an entry from either the second or third + // subtree, then we may need to set a new LowOfXxx value. + // If it was the first subtree, then we simply have to return + // the LowLeaf value we received. + // + + if ((*LowOfNode) != 0) { + if (SubTree == 2) { + Node->LowOfSecond = (*LowOfNode); + (*LowOfNode) = NULL; + } else if (SubTree == 3) { + Node->LowOfThird = (*LowOfNode); + (*LowOfNode) = NULL; + } + } + + + // + // If the SubTreeNode has only one child left, then some + // adjustments are going to be necessary. Otherwise, + // we are done. + // + + if ((*OnlyOneChildLeft)) { + + GtbpDiagPrint( DELETION, + ("GTB: Only one child left in 0x%lx\n", SubTreeNode)); + + // + // One of our children has only one child. + // Re-shuffle our kid's kids. + // + + GtbpCoalesceChildren( Table, + Node, + SubTree, + OnlyOneChildLeft + ); + } + + return; +} + + +BOOLEAN +GtbpInsertIntoSubTree ( + PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN BOOLEAN NodeIsLeaf, + IN PVOID Element, + IN ULONG SplitCount, + IN PVOID *FoundElement, + OUT PGTB_TWO_THREE_NODE *ExtraNode, + OUT PGTB_TWO_THREE_LEAF *LowLeaf, + OUT PLIST_ENTRY *AllocatedNodes + ) + +/*++ + +Routine Description: + + Insert an element into the SubTree specified by Node. + + Special note: + + if FoundElement is returned as NULL, that means we + couldn't allocate memory to add the new element. + +Arguments: + + Table - Points to the table being inserted into. This is needed + for its allocation routine. + + + Node - Points to the root node of the SubTree into + which the element is to be inserted. + + NodeIsLeaf - TRUE if the node passed in is a leaf. FALSE + if it is an internal node. + + Element - Points to the element to be inserted. + + SplitCount - indicates how many nodes have been traversed since + a node with only two children. When inserting a new element + that causes nodes to be split, this will indicate how many + nodes will split. This allows all memory that will be required + to split nodes to be allocated at the very bottom routine + (before any changes to the tree are made). See the description + of the AllocatedNodes parameter for more information. + + FoundElement - Receives a pointer to the element that + was either inserted, or one already in the table + but found to match the one being inserted. + If null is returned, then not enough memory could be + allocated to insert the new element. + + ExtraNode - If it was necessary to create a new node to + accomodate the insertion, then ExtraNode will receive + a pointer to that node, otherwise ExtraNode will receive + NULL. + + LowLeaf - This value points to the lowest value leaf of the + SubTree starting at Node. + + AllocatedNodes - This is a tricky parameter. We have the problem + that when we insert an element in the tree, we may need to + allocate additional internal nodes further up the tree as + we return out of our recursive calls. We must avoid the + situation where we start making changes to the tree only to + find we can't allocate memory to re-arrange higher levels of + the tree. To accomodate this situation, we always allocate + all the nodes we will need at the very bottom of the call + chain and pass back a linked list of GTB_TWO_THREE_NODEs using + this parameter. We know how many nodes we will need to + allocate because it is the number of nodes between the leaf + and the lowest level node in the path between the leaf and the + root that has only 2 children. That is, all nodes directly + above the leaf that have 3 children will need to be split. + Example: + + 3 + / | \ + +-----+ | +---- + Won't split --> 2 ... ... + / | + +-----+ | + ... 3 <-- Will split + / | \ + +-----+ | +----+ + ... 3 <--- Will split + / | \ + +-----+ | +----+ + Leaf Leaf Leaf <- Add fourth leaf here. + + Adding a fourth leaf where indicated will cause a split at the + two nodes indicated. So, you can see that keeping a count of + the nodes with three children since the last encountered node + with only two children will tell us how many nodes will split. + + + + + + + +Return Value: + + TRUE - if element was added. + FALSE - if element not added (due to collision or out-of-memory) + +--*/ + +{ + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + ULONG + SubTree; // To track which SubTree an element is being placed in. + + + PGTB_TWO_THREE_NODE + SubTreeNode, + NodePassedBack; + + + PGTB_TWO_THREE_LEAF + NewLeaf, + LowPassedBack; + + BOOLEAN + Inserted, + SubNodeIsLeaf; + + + // + // Don't have an extra node to pass back yet. + // + + (*ExtraNode) = NULL; + + + // + // We are either at a leaf, or an internal node. + // + + if (NodeIsLeaf) { + + // + // Typecast the Node into a leaf + // + + PGTB_TWO_THREE_LEAF Leaf = (PGTB_TWO_THREE_LEAF)((PVOID)Node); + + // + // See if the value matches. + // + + CompareResult = (*Table->Compare)( Element, Leaf->Element ); + + if (CompareResult == GenericEqual) { + (*LowLeaf) = Leaf; + (*FoundElement) = Leaf->Element; + + GtbpDiagPrint( COLLISIONS, + ("GTB: Insertion attempt resulted in collision.\n" + " Element NOT being inserted.\n" + " Elements in table: %d\n", + Table->ElementCount)); + + return(FALSE); + } //end_if equal + + // + // The new element isn't in the tree. + // Allocate a new leaf for it. + // + + NewLeaf = GtbpAllocateLeafAndNodes( Table, SplitCount, AllocatedNodes ); + if (NewLeaf == NULL) { + + // + // The following (unusual) return value indicates we + // couldn't allocate memory to add the entry into the + // tree. + // + + (*FoundElement) = NULL; + return(FALSE); + + } //end_if (NewLeaf == NULL) + + switch (CompareResult) { + + case GenericLessThan: { + + // + // Move the original element into the new leaf. Notice + // that the SortOrderEntry of the existing leaf is + // still in the right place in the linked-list, even + // though the leaf now points at a different element. + // + + NewLeaf->Element = Leaf->Element; + Leaf->Element = Element; + + break; + } //end_case + + case GenericGreaterThan: { + + // + // The new element does not supplant the existing element. + // Put it in the new leaf. + // + + NewLeaf->Element = Element; + break; + } //end_case + + + } //end_switch + + // + // At this point, the lower-value element is in Leaf + // and the higher-value element is in NewLeaf. The + // caller is responsible to putting NewLeaf someplace + // else in the tree. + // + + // + // Now link the new leaf into our sort-order list. + // The new leaf immediately follows our existing leaf, + // regardless of which element is in the new leaf (original + // or new element). + // + + InsertHeadList(&Leaf->SortOrderEntry, &NewLeaf->SortOrderEntry); + Table->ElementCount++; // Increment the element count + + (*ExtraNode) = (PGTB_TWO_THREE_NODE)((PVOID)NewLeaf); + (*LowLeaf) = NewLeaf; + (*FoundElement) = Element; + + return(TRUE); + + } //end_if NodeIsLeaf + + // + // Node is internal (not a leaf) + // + + // + // See if we should re-set or increment the SplitCount. + // + + if (Node->ThirdChild == NULL) { + SplitCount = 0; + } else { + SplitCount += 1; + } + + GtbpGetSubTreeOfElement( Table, Node, Element, &SubTreeNode, &SubTree); + SubNodeIsLeaf = GtbpChildrenAreLeaves(Node); + + Inserted = GtbpInsertIntoSubTree ( Table, + SubTreeNode, + SubNodeIsLeaf, + Element, + SplitCount, + FoundElement, + &NodePassedBack, + &LowPassedBack, + AllocatedNodes + ); + + // + // One of several things could have happened: + // + // 1) We didn't have enough memory to add the new element. + // In this case we are done and simply return. + // + // 2) The element was added, and no-rearrangement to this + // node is needed. In this case we are done and simply + // return. + // + // 3) The element was added and caused a leaf to be pushed + // out of the SubTree. We have some work to do. + // + + if ( (FoundElement == NULL) || // Insufficient memory, or + (NodePassedBack == NULL) ) { // no work for this node + return(Inserted); + } + + if (Node->ThirdChild == NULL) { + + if (!GtbpChildrenAreLeaves(Node)) { + NodePassedBack->ParentNode = Node; + } + + // + // Node doesn't yet have a third child, so use it. + // This might require shuffling the second child to the + // be the third child. + // + + if (SubTree == 2) { + + // + // Node fell out of second SubTree and we don't have a + // third SubTree. Make that node the third SubTree. + // + + Node->ThirdChild = NodePassedBack; + Node->LowOfThird = LowPassedBack; + + } else { + + // + // Node fell out of first SubTree. + // Make the second SubTree the third SubTree and + // then make the passed back node the second SubTree. + // + + ASSERT(SubTree == 1); + + Node->ThirdChild = Node->SecondChild; + Node->LowOfThird = Node->LowOfSecond; + Node->SecondChild = NodePassedBack; + Node->LowOfSecond = LowPassedBack; + + // + // + + } + } else { + + // + // Node already has three children - split it. + // + + GtbpSplitNode( Node, + NodePassedBack, + LowPassedBack, + SubTree, + (*AllocatedNodes), + ExtraNode, + LowLeaf + ); + + } + + return(Inserted); +} + + +ULONG +GtbpNumberOfChildren( + IN PGTB_TWO_THREE_NODE Node + ) + +/*++ + +Routine Description: + + Return the number of children of a specified node. + +Arguments: + + Node - points to the node whose children are to be counted. + +Return Values: + + 0, 1, 2, or 3. + +--*/ +{ + if (Node->ThirdChild != NULL) { + return(3); + } + if (Node->SecondChild != NULL) { + return(2); + } + if (Node->FirstChild != NULL) { + return(1); + } + return(0); + +} + + +VOID +GtbpGetSubTreeOfElement( + IN PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN PVOID Element, + OUT PGTB_TWO_THREE_NODE *SubTreeNode, + OUT ULONG *SubTree + ) + +/*++ + +Routine Description: + + Find which SubTree of Node that Element might be in (or should be + in, if being inserted). + +Arguments: + + Table - Points to the table This is needed for its comparison routine. + + Node - Is the node, one of whose SubTrees is to be chosen as the + subtree in which Element could/should reside. + + Element - is the element we are interested in placing or locating. + + SubTreeNode - Receives a pointer to the node of the SubTree in + which the element could/should reside. + + SubTree - Receives the index (1, 2, or 3) of the subtree of Node + in which the element could/should reside. + +Return Values: + + None. + +--*/ +{ + RTL_GENERIC_COMPARE_RESULTS + CompareResult; + + CompareResult = (*Table->Compare)( Element, Node->LowOfSecond->Element ); + + if (CompareResult == GenericLessThan) { + + (*SubTree) = 1; + (*SubTreeNode) = Node->FirstChild; + + } else { + + // + // default to the second subtree, but + // if there is a subtree we may change it. + // + + (*SubTree) = 2; + (*SubTreeNode) = Node->SecondChild; + + if (Node->ThirdChild != NULL) { + + CompareResult = (*Table->Compare)( Element, Node->LowOfThird->Element ); + if ( (CompareResult == GenericGreaterThan) || + (CompareResult == GenericEqual) ) { + + (*SubTree) = 3; + (*SubTreeNode) = Node->ThirdChild; + } + } + } + + return; + +} + + + +VOID +GtbpCoalesceChildren( + IN PRTL_GENERIC_TABLE2 Table, + IN PGTB_TWO_THREE_NODE Node, + IN ULONG SubTree, + OUT BOOLEAN *OnlyOneChildLeft + ) + +/*++ + +Routine Description: + + This routine is called following a deletion that leaves a child + node with only one child of its own. That is, a child of the + Node parameter has only one child. The SubTree parameter indicates + which child of Node has only one child. + + + + +Arguments: + + Table - Points to the table. + + Node - Is the node, one of whose children has only one child. + + NOTE: The ParentNode field of this node must be valid. + The Low values of ParentNode will be referenced. + + SubTree - Indicates which child of Node (1, 2, or 3) has only one + child. + + OnlyOneChildLeft - Receives a boolean indicating whether or not + Node itself has been left with only one child due to the + coalescing. + +Return Values: + + None. + +--*/ +{ + PGTB_TWO_THREE_NODE + A, + B, + C; + + (*OnlyOneChildLeft) = FALSE; // Default return value + + // + // For the following discussion, using the following: + // + // N is the parent node + // S is the node which has only one child + // (S is a child of N) + // + // A is the first child of N + // B is the second child of N + // C is the third child of N + // + // If S is the first child of N (meaning S is A) + // then: + // + // if B has three children (let A adopt the smallest) + // then: + // + // Move B(1) to A(2) + // Move B(2) to B(1) + // Move B(3) to B(2) + // + // else (B has two children) + // + // (move the orphan into B) + // Move B(2) to B(3) + // Move B(1) to B(2) + // Move A(1) to B(1) + // + // Free A + // Make B the first child of N + // if (C is a real child) + // then: + // Make C the second child of N + // else (N only has one child now) + // (*OnlyOneChildLeft) = TRUE; + // + // else if S is the second child of N (meaning S is B) + // then: + // + // if A has three children + // then: + // Move B(1) to B(2) + // Move A(3) to B(1) + // + // else if C exists and has three children + // then: + // + // Move C(1) to B(2) + // Move C(2) to C(1) + // Move C(3) to C(2) + // + // else: (no other child of N has three children) + // + // (Move the orphan into A) + // Move B(1) to A(3) + // + // Free B + // if (C is a real child) + // then: + // Make C the second child of N + // else: (N only has one child now) + // (*OnlyOneChildLeft) = TRUE; + // + // else: (S is the third child of N (meaning S is C)) + // + // if B has three children + // then: + // (move one into C) + // Move C(1) to C(2) + // Move B(3) to C(1) + // + // else: (B only has two children) + // + // (move the orphan into B) + // Move C(1) to B(3) + // Free C + // Wow! + + + A = Node->FirstChild; + B = Node->SecondChild; + C = Node->ThirdChild; + + + // + // SubTree indicates which child has the orphan. + // + + if (SubTree == 1) { + + // + // First child has the orphan + // + + if (B->ThirdChild != NULL) { + + // (B has three children - let A adopt the smallest) + // + // Move B(1) to A(2) + // Move B(2) to B(1) + // Move B(3) to B(2) + // + + A->SecondChild = B->FirstChild; + A->LowOfSecond = Node->LowOfSecond; + + B->FirstChild = B->SecondChild; + Node->LowOfSecond = B->LowOfSecond; + + B->SecondChild = B->ThirdChild; + B->LowOfSecond = B->LowOfThird; + B->ThirdChild = NULL; + + } else { + + // + // (B has two children) + // + // (move the orphan into B) + // Move B(2) to B(3) + // Move B(1) to B(2) + // Move A(1) to B(1) + // + + B->ThirdChild = B->SecondChild; + B->LowOfThird = B->LowOfSecond; + + B->SecondChild = B->FirstChild; + B->LowOfSecond = Node->LowOfSecond; + + B->FirstChild = A->FirstChild; + //Node->LowOfSecond = Node->LowOfFirst; // This gets moved back in a few steps + + // Free A + // Make B the first child of N + // if (C is a real child) + // then: + // Make C the second child of N + // else (N only has one child now) + // (*OnlyOneChildLeft) = TRUE; + // + + (*Table->Free)(A); + Node->FirstChild = B; + //Node->LowOfFirst = Node->LowOfSecond; // See comment a few lines up + + if (C != NULL) { + Node->SecondChild = C; + Node->LowOfSecond = Node->LowOfThird; + Node->ThirdChild = NULL; + } else { + Node->SecondChild = NULL; + (*OnlyOneChildLeft) = TRUE; + } + } + + + } else if (SubTree == 2) { + + // + // Second child has the orphan + // + + if (A->ThirdChild != NULL) { + + // + // (A has three children) + // + // Move B(1) to B(2) + // Move A(3) to B(1) + // + + B->SecondChild = B->FirstChild; + B->LowOfSecond = Node->LowOfSecond; + + B->FirstChild = A->ThirdChild; + Node->LowOfSecond = A->LowOfThird; + A->ThirdChild = NULL; + + } else { + + if (C != NULL && + C->ThirdChild != NULL) { + + // + // (C exists and has three children) + // (move the smallest into B) + // + // Move C(1) to B(2) + // Move C(2) to C(1) + // Move C(3) to C(2) + // + + B->SecondChild = C->FirstChild; + B->LowOfSecond = Node->LowOfThird; + + C->FirstChild = C->SecondChild; + Node->LowOfThird = C->LowOfSecond; + + C->SecondChild = C->ThirdChild; + C->LowOfSecond = C->LowOfThird; + C->ThirdChild = NULL; + + + + + + } else { + + // + // (no other child of N has three children) + // (Move the orphan into A) + // + // Move B(1) to A(3) + // + // Free B + // if (C is a real child) + // then: + // Make C the second child of N + // else: (N only has one child now) + // (*OnlyOneChildLeft) = TRUE; + // + + A->ThirdChild = B->FirstChild; + A->LowOfThird = Node->LowOfSecond; + + (*Table->Free)(B); + + if (C != NULL) { + Node->SecondChild = C; + Node->LowOfSecond = Node->LowOfThird; + Node->ThirdChild = NULL; + } else { + Node->SecondChild = NULL; + (*OnlyOneChildLeft) = TRUE; + } + } + } + + + + } else { + + // + // Third child has the orphan + // + + ASSERT(SubTree == 3); + ASSERT(C != NULL); + ASSERT(B != NULL); + + if (B->ThirdChild != NULL) { + + // + // (B has three children) + // (move the largest of them into C) + // Move C(1) to C(2) + // Move B(3) to C(1) + // + + C->SecondChild = C->FirstChild; + C->LowOfSecond = Node->LowOfThird; + + C->FirstChild = B->ThirdChild; + Node->LowOfThird = B->LowOfThird; + B->ThirdChild = 0; + } else { + + // + // (B only has two children) + // (move the orphan into B) + // Move C(1) to B(3) + // Free C + // + + B->ThirdChild = C->FirstChild; + B->LowOfThird = Node->LowOfThird; + Node->ThirdChild = NULL; + + (*Table->Free)(C); + + } + } + + return; + +} + + +VOID +GtbpSplitNode( + IN PGTB_TWO_THREE_NODE Node, + IN PGTB_TWO_THREE_NODE NodePassedBack, + IN PGTB_TWO_THREE_LEAF LowPassedBack, + IN ULONG SubTree, + IN PLIST_ENTRY AllocatedNodes, + OUT PGTB_TWO_THREE_NODE *NewNode, + OUT PGTB_TWO_THREE_LEAF *LowLeaf + ) + +/*++ + +Routine Description: + + This routine splits a node that already has three children. + Memory necessary to perform the split is expected to have + already been allocated and available via the AllocatedNodes + parameter. + + +Parameters: + + Node - The node to be split. + + NodePassedBack - The 4th node passed back from an insertion operation + into the SubTree of Node specified by the SubTree parameter. + NOTE: that this may, in fact, be a GTB_TWO_THREE_LEAF !!! + + LowPassedBack - points the the low leaf value passed back by the + insertion operation that is causing the split. + + SubTree - Indicates which SubTree of Node an element was inserted + into which is causing the split. + + AllocatedNodes - Contains a list of allocated GTB_TWO_THREE_NODE + blocks for use in an insertion operation (which this split + is assumed to be part of). + + NewNode - Receives a pointer to the node generated by the split. + + LowLeaf - receives a pointer to the low leaf of the NewNode's SubTree. + + +Return Values: + + None. + +--*/ +{ + + PGTB_TWO_THREE_NODE + LocalNode; + + + + // Make a new node and split things up. + // The node has already been allocated and passed back to + // this routine via the AllocatedNodes parameter. + // + + LocalNode = GtbpGetNextAllocatedNode( AllocatedNodes ); + ASSERT( LocalNode != NULL); + (*NewNode) = LocalNode; + + // + // Set known fields of new node + // + + LocalNode->ParentNode = Node->ParentNode; + LocalNode->Control = Node->Control; + LocalNode->ThirdChild = NULL; //Low of third is left undefined + + // + // Now move around children... + // + + if (SubTree == 3) { + + // + // We were inserting into the 3rd SubTree. This implies: + // + // Node(child 3) moves to new(child 1) + // Back is put in new(child 2) + // + + LocalNode->FirstChild = Node->ThirdChild; + LocalNode->SecondChild = NodePassedBack; + LocalNode->LowOfSecond = LowPassedBack; + (*LowLeaf) = Node->LowOfThird; // low of the new node is low of old third + + Node->ThirdChild = NULL; //Low of third is left undefined + + + + } else { + + // + // We inserted into either SubTree 1 or 2. + // These cases cause: + // + // 1 => Node(child 3) moves to new(child 2) + // Node(child 2) moves to New(child 1) + // Back is put in Node(child 2) + // + // 2 => Node(child 3) moves to new(child 2) + // Back is put in New(child 1) + // + // In both these cases, Node(child 3) moves to New(child 2) + // and there are no third children. So do that. + // + + LocalNode->SecondChild = Node->ThirdChild; + LocalNode->LowOfSecond = Node->LowOfThird; + + + if (SubTree == 2) { + + LocalNode->FirstChild = NodePassedBack; + (*LowLeaf) = LowPassedBack; + + if (!GtbpChildrenAreLeaves(Node)) { + NodePassedBack->ParentNode = LocalNode; + } + + } else { + + // + // SubTree == 1 + // + + LocalNode->FirstChild = Node->SecondChild; + (*LowLeaf) = Node->LowOfSecond; + + Node->SecondChild = NodePassedBack; + Node->LowOfSecond = LowPassedBack; + if (!GtbpChildrenAreLeaves(Node)) { + NodePassedBack->ParentNode = Node; + } + + } + } + + // + // Neither node has a third child anymore + // + + LocalNode->ThirdChild = NULL; //Low of third is left undefined + Node->ThirdChild = NULL; + + // + // Set the ParentNodes only if the children aren't leaves. + // + + if (!GtbpChildrenAreLeaves(Node)) { + + Node->FirstChild->ParentNode = Node; + Node->SecondChild->ParentNode = Node; + + LocalNode->FirstChild->ParentNode = LocalNode; + LocalNode->SecondChild->ParentNode = LocalNode; + } + + + return; +} + + + +PGTB_TWO_THREE_LEAF +GtbpAllocateLeafAndNodes( + IN PRTL_GENERIC_TABLE2 Table, + IN ULONG SplitCount, + OUT PLIST_ENTRY *AllocatedNodes + ) +/*++ + +Routine Description: + + Allocate a leaf and some number of internal nodes. This is + used in conjunction with GtbpGetNextAllocatedNode() when splitting + nodes following an insertion. These two routines allow all necessary + memory to be allocated all at once, rather than trying to deal with + memory allocation failures once changes to the tree have begun. + + +Arguments: + + Table - the table into which the nodes will be added. This + provides the allocation routine. + + SplitCount - indicates how many nodes will need splitting, and, + thus, how many nodes need to be allocated. + + +Return Value: + + Pointer to the leaf if successful. + NULL if unsuccessful. + +--*/ +{ + + PGTB_TWO_THREE_LEAF + Leaf; + + PLIST_ENTRY + NodeRoot, + NextNode; + + ULONG + i; + +#ifdef GTBP_DIAGNOSTICS + PGTB_TWO_THREE_NODE + N; +#endif //GTBP_DIAGNOSTICS + + NodeRoot = NULL; + + // + // Allocate a chain of Nodes, if necessary + // + + if (SplitCount > 0) { + + NodeRoot = (PLIST_ENTRY) + ((*Table->Allocate)( sizeof(GTB_TWO_THREE_NODE))); + if (NodeRoot == NULL) { + goto error_return; + } + + InitializeListHead( NodeRoot ); + +#ifdef GTBP_DIAGNOSTICS + + GtbpDiagPrint(LEAF_AND_NODE_ALLOC, + ("GTB: Allocating %d nodes with leaf, root: 0x%lx\n", + SplitCount, NodeRoot)); + N = (PGTB_TWO_THREE_NODE)NodeRoot; + N->Control = 10000; //Used as a diagnostic allocation counter/index + +#endif //GTBP_DIAGNOSTICS + + for (i=1; i<SplitCount; i++) { + NextNode = (PLIST_ENTRY) + ((*Table->Allocate)( sizeof(GTB_TWO_THREE_NODE))); + if (NextNode == NULL) { + goto error_return; + } + InsertTailList( NodeRoot, NextNode ); + +#ifdef GTBP_DIAGNOSTICS + + N = (PGTB_TWO_THREE_NODE)NextNode; + N->Control = 10000+i; //Used as a diagnostic allocation counter/index + +#endif //GTBP_DIAGNOSTICS + } + } + + + // + // Finally, allocate the leaf + // + + Leaf = (PGTB_TWO_THREE_LEAF) + ((*Table->Allocate)( sizeof(GTB_TWO_THREE_LEAF))); + + if (Leaf == NULL) { + goto error_return; + } + + (*AllocatedNodes) = NodeRoot; + return(Leaf); + +error_return: + + GtbpDiagPrint(LEAF_AND_NODE_ALLOC, + ("GTB: ** error allocating leaf and nodes - insufficient memory.\n")); + // + // Deallocate any nodes that have already been allocated. + // + + if (NodeRoot != NULL) { + + NextNode = NodeRoot->Flink; + while (NextNode != NodeRoot) { + RemoveEntryList( NextNode ); + (*Table->Free)( NextNode ); + + + } + + (*Table->Free)( NodeRoot ); + } + + return(NULL); +} + + +PGTB_TWO_THREE_NODE +GtbpGetNextAllocatedNode( + IN PLIST_ENTRY AllocatedNodes + ) +/*++ + +Routine Description: + + Take the next node off of the list of allocated nodes and + return its address. + + +Arguments: + + AllocatedNodes - Points to the list of nodes allocated using + GtbpAllocateLeafAndNodes(). + + +Return Value: + + Pointer to the node. + any other return value indicates an error in the caller. + +--*/ +{ + PLIST_ENTRY + NextNode; + +#ifdef GTBP_DIAGNOSTICS + PGTB_TWO_THREE_NODE + N; +#endif //GTBP_DIAGNOSTICS + + // + // Remove the first entry on the list. + // This ensures that the passed in root is the last entry + // returned. + // + + NextNode = AllocatedNodes->Flink; + RemoveEntryList( NextNode ); + +#ifdef GTBP_DIAGNOSTICS + + NextNode->Flink = NULL; //Just to prevent accidental re-use + N = (PGTB_TWO_THREE_NODE)NextNode; + ASSERT(N->Control >= 10000); //under 10000 mplies it has already been allocated. + + + GtbpDiagPrint(LEAF_AND_NODE_ALLOC, + ("GTB: Allocating node (index: %d) from root: 0x%lx\n", + (N->Control-10000), AllocatedNodes)); +#endif //GTBP_DIAGNOSTICS + + return( (PGTB_TWO_THREE_NODE)((PVOID)NextNode) ); +} + + + +////////////////////////////////////////////////////////////////////////// +// // +// Diagnostic (Developer) routines ... // +// // +////////////////////////////////////////////////////////////////////////// + +#ifdef GTBP_DEVELOPER_BUILD + +#include <string.h> + +// +// This routine is expected to dump an element's value +// + +typedef +VOID +(NTAPI *PGTBP_DEV_DUMP_ELEMENT_ROUTINE) ( + PVOID Element + ); + + +VOID +GtbpDevIndent( + ULONG Depth + ) +{ + UNICODE_STRING + Indent; + + RtlInitUnicodeString( &Indent, + L" +"); + + Indent.Length = (USHORT)(Depth*4); + if (Indent.Length > Indent.MaximumLength) { + Indent.Length = Indent.MaximumLength; + } + + DbgPrint("\n%wZ%d: ", &Indent, Depth); + return; +} + + +VOID +GtbpDevDumpNode( + PGTB_TWO_THREE_NODE Parent, + PGTB_TWO_THREE_NODE N, + PGTB_TWO_THREE_LEAF Low, + ULONG Depth, + PGTBP_DEV_DUMP_ELEMENT_ROUTINE DumpElement + ) +/*++ + +Routine Description: + + Dump the 2-3 tree starting at the specified node. + +Arguments: + + N - Points to the node to start the dump at. + + Depth - Indicates the depth of the tree prior to this node. + This is used to indent the printing. + +Return Value: + + None. + +--*/ +{ + ASSERT(Parent == N->ParentNode); + + + GtbpDevIndent( Depth ); + DbgPrint("0x%lx ", N); + if (ARGUMENT_PRESENT(Low)) { + DbgPrint("(LowElement): "); + DumpElement( Low->Element ); + } + + Depth++; + + if (GtbpChildrenAreLeaves(N)) { + + GtbpDevIndent( Depth ); + DumpElement( ((PGTB_TWO_THREE_LEAF)N->FirstChild)->Element ); + + if (N->SecondChild != NULL) { + GtbpDevIndent( Depth ); + DumpElement( ((PGTB_TWO_THREE_LEAF)N->SecondChild)->Element ); + + if (N->ThirdChild != NULL) { + GtbpDevIndent( Depth ); + DumpElement( ((PGTB_TWO_THREE_LEAF)N->ThirdChild)->Element ); + } + } + } else { + + GtbpDevDumpNode( N, N->FirstChild, NULL, Depth, DumpElement ); + + if (N->SecondChild != NULL) { + GtbpDevDumpNode( N, N->SecondChild, N->LowOfSecond, Depth, DumpElement ); + + if (N->ThirdChild != NULL) { + GtbpDevDumpNode( N, N->ThirdChild, N->LowOfThird, Depth, DumpElement ); + } + } + } + + return; +} + + +VOID +GtbpDevDumpTwoThreeTree( + PRTL_GENERIC_TABLE2 Table, + PGTBP_DEV_DUMP_ELEMENT_ROUTINE DumpElement + ) +/*++ + +Routine Description: + + This routine causes the entire 2-3 tree to be dumped. + + +Arguments: + + Table - The table containing the 2-3 tree to dump. + + DumpElement - A caller supplied routine that may be called + to dump element values. + +Return Value: + + None. + +--*/ +{ + PLIST_ENTRY + Next; + + + DbgPrint("\n\n\n **** Dump Of Generic Table2 (2-3 tree) **** \n\n"); + + DbgPrint("Table : 0x%lx\n", Table); + DbgPrint("Element Count: %d\n", Table->ElementCount); + + + DbgPrint("\n\nSort Order Of Elements..."); + + Next = Table->SortOrderHead.Flink; + if (Next == &(Table->SortOrderHead)) { + DbgPrint("Sorted list is empty.\n"); + } else { + + while (Next != &(Table->SortOrderHead)) { + DbgPrint("\n0x%lx: ", Next); + (*DumpElement)( ((PGTB_TWO_THREE_LEAF)((PVOID)Next))->Element ); + Next = Next->Flink; + } //end_while + } //end_if + + DbgPrint("\n\n\nTree Structure..."); + + if (Table->Root == NULL) { + DbgPrint(" Root of table is NULL - no tree present\n"); + } else { + + // + // Walk the tree first-SubTree, second-SubTree, third-SubTree order + // + + GtbpDevDumpNode(NULL, Table->Root, NULL, 0, DumpElement); + } + + DbgPrint("\n\n"); + + + return; +} + + + +BOOLEAN +GtbpDevValidateLeaf( + IN PGTB_TWO_THREE_LEAF Leaf, + IN PLIST_ENTRY ListHead, + IN OUT ULONG *ElementCount, + IN OUT PLIST_ENTRY *ListEntry + ) + +/*++ + +Routine Description: + + Validate the specified leaf matches the next leaf in the + SortOrder list. + + +Arguments: + + Leaf - Points to the leaf to validate. + + ListHead - Points to the head of the SortOrderList. + + ElementCount - Contains a count of elements already validated. + This parameter will be incremented by 1 if the leaf is + found to be valid. + + ListEntry - Points to the next element in the SortOrderList. + This pointer will be updated to point to the next element + in the list if the leaf is found to be valid. + +Return Value: + + TRUE - Leaf is valid. + + FALSE - Leaf is not valid. + +--*/ +{ + + if ((*ListEntry) == ListHead) { + GtbpDiagPrint( VALIDATE, + ("GTB: Exhausted entries in SortOrderList while there are still\n" + " entries in the tree.\n")); + } + + + if ((PVOID)Leaf == (PVOID)(*ListEntry)) { + (*ElementCount)++; + (*ListEntry) = (*ListEntry)->Flink; + return(TRUE); + } else { + GtbpDiagPrint( VALIDATE, + ("GTB: Tree leaf doesn't match sort order leaf.\n" + " Tree Leaf : 0x%lx\n" + " sort order leaf: 0x%lx\n", + Leaf, (*ListEntry))); + return(FALSE); + } +} + + +BOOLEAN +GtbpDevValidateTwoThreeSubTree( + IN PGTB_TWO_THREE_NODE Node, + IN PLIST_ENTRY ListHead, + IN OUT ULONG *ElementCount, + IN OUT PLIST_ENTRY *ListEntry + ) + +/*++ + +Routine Description: + + Validate the specified subtree of a 2-3 tree. + + The ParentNode of the tree is expected to already have been + validated by the caller of this routine. + + +Arguments: + + Node - Pointer to the root node of the subtree to validate. + Validate the specified leaf matches the next leaf in the + SortOrder list. + + +Arguments: + + Leaf - Points to the leaf to validate. + + ListHead - points to the SortOrderList's listhead. + + ElementCount - Contains a count of elements already validated. + This parameter will be incremented by the number of elements + in this subtree. + + ListEntry - Points to the next element in the SortOrderList. + This pointer will be updated as elements are encountered and + compared to the elements in the SortOrderList. + +Return Value: + + TRUE - SubTree structure is valid. + + FALSE - SubTree structure is not valid. + +--*/ +{ + + BOOLEAN + Result; + + + // + // Must have at least two children unless we are the root node. + // + + if (Node->ParentNode != NULL) { + if (Node->SecondChild == NULL) { + GtbpDiagPrint( VALIDATE, + ("GTB: Non-root node has fewer than two children.\n" + " Node : 0x%lx\n" + " FirstChild : 0x%lx\n" + " SecondChild: 0x%lx\n" + " ThirdChild : 0x%lx\n", + Node, Node->FirstChild, Node->SecondChild, + Node->ThirdChild)); + return(FALSE); + } + } + + if (Node->FirstChild == NULL) { + GtbpDiagPrint( VALIDATE, + ("GTB: Non-root node does not have first child.\n" + " Node : 0x%lx\n" + " FirstChild : 0x%lx\n" + " SecondChild: 0x%lx\n" + " ThirdChild : 0x%lx\n", + Node, Node->FirstChild, Node->SecondChild, + Node->ThirdChild)); + return(FALSE); + } + + + + if (!GtbpChildrenAreLeaves(Node)) { + + + Result = GtbpDevValidateTwoThreeSubTree( Node->FirstChild, + ListHead, + ElementCount, + ListEntry); + + if ( Result && (Node->SecondChild != NULL) ) { + Result = GtbpDevValidateTwoThreeSubTree( Node->SecondChild, + ListHead, + ElementCount, + ListEntry); + if ( Result && (Node->ThirdChild != NULL) ) { + Result = GtbpDevValidateTwoThreeSubTree( Node->ThirdChild, + ListHead, + ElementCount, + ListEntry); + } + } + + return(Result); + } + + + // + // We are at a leaf node + // Check that we have a SortOrderList entry matching each + // leaf. + // + + Result = GtbpDevValidateLeaf( (PGTB_TWO_THREE_LEAF)Node->FirstChild, + ListHead, + ElementCount, + ListEntry); + + if (Result && (Node->SecondChild != NULL)) { + Result = GtbpDevValidateLeaf( (PGTB_TWO_THREE_LEAF)Node->SecondChild, + ListHead, + ElementCount, + ListEntry); + if (Result && (Node->ThirdChild != NULL)) { + Result = GtbpDevValidateLeaf( (PGTB_TWO_THREE_LEAF)Node->ThirdChild, + ListHead, + ElementCount, + ListEntry); + } + } + + if (!Result) { + GtbpDiagPrint( VALIDATE, + ("GTB: previous error in child analysis prevents further\n" + " validation of node: 0x%lx\n", Node)); + } + + return(Result); +} + +BOOLEAN +GtbpDevValidateGenericTable2( + PRTL_GENERIC_TABLE2 Table + ) +/*++ + +Routine Description: + + This routine causes the entire 2-3 tree's structure to be + validated. + + !! DOESN'T YET VALIDATE LowOfChild VALUES !! + +Arguments: + + Table - The table containing the 2-3 tree to validate. + + +Return Value: + + TRUE - Table structure is valid. + + FALSE - Table structure is not valid. + +--*/ +{ + ULONG + ElementCount, + ElementsInList; + + PGTB_TWO_THREE_NODE + Node; + + PLIST_ENTRY + ListEntry; + + BOOLEAN + Result; + + // + // Walk the tree and the linked list simultaneously. + // Walk the tree first-child, second-child, third-child + // order to get ascending values that match the linked list. + // + + + if (Table->ElementCount == 0) { + if (Table->Root != NULL) { + GtbpDiagPrint( VALIDATE, + ("GTB: ElementCount is zero, but root node is not null.\n" + " Table: 0x%lx Root: 0x%lx\n", Table, Table->Root)); + Result = FALSE; + } else { + return(TRUE); + } + + + } else { + if (Table->Root == NULL) { + GtbpDiagPrint( VALIDATE, + ("GTB: ElementCount is non-zero, but root node is null.\n" + " Table: 0x%lx ElementCount: %d\n", + Table, Table->ElementCount)); + Result = FALSE; + } + + + if (Table->SortOrderHead.Flink == &Table->SortOrderHead) { + GtbpDiagPrint( VALIDATE, + ("GTB: ElementCount is non-zero, but sort order list is empty.\n" + " Table: 0x%lx ElementCount: %d\n", + Table, Table->ElementCount)); + Result = FALSE; + } + + } + + if (Result) { + + ListEntry = Table->SortOrderHead.Flink; + Node = Table->Root; + + // + // Verify parent pointer (responsibility of caller) + // + + if (Node->ParentNode != NULL) { + GtbpDiagPrint( VALIDATE, + ("GTB: Root parent pointer is non-null.\n" + " Table: 0x%lx Root: 0x%lx ParentNode: 0x%lx\n", + Table, Node, Node->ParentNode)); + Result = FALSE; + } + + if (Result) { + + ElementCount = 0; + Result = GtbpDevValidateTwoThreeSubTree( Node, + &Table->SortOrderHead, + &ElementCount, + &ListEntry); + if (Result) { + + ElementsInList = ElementCount; + while (ListEntry != &Table->SortOrderHead) { + ElementsInList++; + ListEntry = ListEntry->Flink; + } + if ( (ElementCount != Table->ElementCount) || + (ElementsInList != ElementCount) ) { + GtbpDiagPrint( VALIDATE, + ("GTB: Table is valid except either the ElementCount doesn't match\n" + " the number of elements in the table or there were entries on\n" + " the SortOrderList that weren't in the table.\n" + " Table : 0x%lx\n" + " Root : 0x%lx\n" + " ElementCount : %d\n" + " Elements In Tree: %d\n" + " Elements In List: %d\n", + Table, Node, Table->ElementCount, + ElementCount, ElementsInList)); + Result = FALSE; + } + } else { + GtbpDiagPrint( VALIDATE, + ("GTB: previous validation error in table 0x%lx prevents\n" + " further processing.\n", Table)); + } + } + } + + if (!Result) { + DbgBreakPoint(); + } + + + return(Result); +} +#endif //GTBP_DEVELOPER_BUILD |