summaryrefslogtreecommitdiffstats
path: root/private/newsam/server/gentab2.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/newsam/server/gentab2.c')
-rw-r--r--private/newsam/server/gentab2.c3561
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