3562 lines
95 KiB
C
3562 lines
95 KiB
C
/*++
|
||
|
||
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
|