windows-nt/Source/XPSP1/NT/net/rras/rtmv2/avltrie.c
2020-09-26 16:20:57 +08:00

1632 lines
34 KiB
C

/*++
Copyright (c) 1997 - 98, Microsoft Corporation
Module Name:
avltrie.c
Abstract:
Contains routines for a best matching
prefix lookup using an AVL trie.
Author:
Chaitanya Kodeboyina (chaitk) 24-Jun-1998
Revision History:
--*/
#include "pchrtm.h"
#pragma hdrstop
#include "avltrie.h"
DWORD
WINAPI
CreateTable(
IN UINT MaxBytes,
OUT HANDLE *Table
)
/*++
Routine Description:
Create a table that enables to you add and delete prefixes
(and associated data) and do best matching prefix queries.
Arguments:
MaxBytes - Max length of any prefix in the table,
Table - Pointer to the table that was created.
Return Value:
Status of the operation
--*/
{
PAVL_TRIE NewTrie;
ASSERT(sizeof(AVL_CONTEXT) <= sizeof(LOOKUP_CONTEXT));
ASSERT(sizeof(AVL_LINKAGE) <= sizeof(LOOKUP_LINKAGE));
if (MaxBytes == 0)
{
return ERROR_INVALID_PARAMETER;
}
//
// Allocate and initialize a new prefix table
//
NewTrie = AllocNZeroMemory(sizeof(AVL_TRIE));
if (NewTrie == NULL)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
#if _PROF_
NewTrie->MemoryInUse = sizeof(AVL_TRIE);
#endif
NewTrie->MaxKeyBytes = MaxBytes;
*Table = NewTrie;
return NO_ERROR;
}
DWORD
WINAPI
InsertIntoTable(
IN HANDLE Table,
IN USHORT NumBits,
IN PUCHAR KeyBits,
IN PLOOKUP_CONTEXT Context OPTIONAL,
IN PLOOKUP_LINKAGE Data
)
/*++
Routine Description:
Inserts new prefix (and associated data) into a prefix table.
Arguments:
Table - Table into which prefix is being inserted,
NumBits - Number of bits in the prefix being added,
KeyBits - Value of the bits that form the prefix,
Context - Search context for the prefix being added,
Data - Data associated with this prefix being added.
Return Value:
Status of the operation
--*/
{
PAVL_TRIE Trie;
PAVL_NODE PrevNode;
PAVL_NODE BestNode;
PAVL_NODE NewNode;
LOOKUP_CONTEXT Context1;
AVL_BALANCE NextChild;
PLOOKUP_LINKAGE Dummy;
Trie = Table;
#if PROF
Trie->NumInsertions++;
#endif
//
// If there is a search context, and we have an
// update, we can avoid the lookup (common case)
//
if (!ARGUMENT_PRESENT(Context))
{
Context = &Context1;
SearchInTable(Table, NumBits, KeyBits, Context, &Dummy);
}
BestNode = ((PAVL_CONTEXT) Context)->BestNode;
if (BestNode && (BestNode->NumBits == NumBits))
{
SET_NODEPTR_INTO_DATA(BestNode->Data, NULL);
BestNode->Data = Data;
SET_NODEPTR_INTO_DATA(Data, BestNode);
return NO_ERROR;
}
NewNode = CreateTrieNode(Trie, NumBits, KeyBits, BestNode, Data);
if (NewNode)
{
PrevNode = ((PAVL_CONTEXT) Context)->InsPoint;
if (PrevNode)
{
NextChild = ((PAVL_CONTEXT) Context)->InsChild;
PrevNode->Child[NextChild] = NewNode;
NewNode->Parent = PrevNode;
((PAVL_CONTEXT) Context)->BestNode = NewNode;
// Enumerate in range of the new node & update prefixes
AdjustPrefixes(Trie, BestNode, NewNode, NewNode, Context);
// Balance trie if it was thrown off balance
BalanceAfterInsert(Trie, PrevNode, NextChild);
}
else
{
Trie->TrieRoot = NewNode;
}
#if _DBG_
if (CheckTable(Table) != TRUE)
{
DbgBreakPoint();
}
#endif
return NO_ERROR;
}
else // if CreateTrieNode failed
{
return ERROR_NOT_ENOUGH_MEMORY;
}
}
DWORD
WINAPI
DeleteFromTable(
IN HANDLE Table,
IN USHORT NumBits,
IN PUCHAR KeyBits,
IN PLOOKUP_CONTEXT Context OPTIONAL,
OUT PLOOKUP_LINKAGE *Data
)
/*++
Routine Description:
Deletes a prefix from a prefix table and returns associated data.
Arguments:
Table - Table from which prefix is being deleted,
NumBits - Number of bits in the prefix being deleted,
KeyBits - Value of the bits that form the prefix,
Context - Search context for the prefix being deleted,
Data - Data associated with this prefix is retd here.
Return Value:
Status of the operation
--*/
{
PAVL_TRIE Trie;
PAVL_NODE PrevNode;
PAVL_NODE CurrNode;
PAVL_NODE NextNode;
LOOKUP_CONTEXT Context1;
AVL_BALANCE NextChild;
DWORD Status;
#if _DBG_
USHORT Depth = 0;
#endif
Trie = Table;
#if PROF
Trie->NumDeletions++;
#endif
//
// If there is a search context that is valid,
// we will avoid doing a lookup (common case)
//
if (!ARGUMENT_PRESENT(Context))
{
Context = &Context1;
Status = SearchInTable(Table, NumBits, KeyBits, Context, Data);
if (Status != NO_ERROR)
{
return Status;
}
}
#if WRN
NextChild = INVALID;
#endif
//
// We should not come here unless the context
// points accurately to element to be deleted
//
CurrNode = ((PAVL_CONTEXT) Context)->BestNode;
ASSERT(CurrNode && (CurrNode->NumBits == NumBits) &&
(CompareFullKeys(CurrNode->KeyBits,
KeyBits,
Trie->MaxKeyBytes) == 0));
PrevNode = ((PAVL_CONTEXT) Context)->InsPoint;
ASSERT(PrevNode == CurrNode->Parent);
if (PrevNode)
{
NextChild = ((PAVL_CONTEXT) Context)->InsChild;
}
ASSERT(((PrevNode == NULL) && (Trie->TrieRoot == CurrNode))
|| (PrevNode->Child[NextChild] == CurrNode));
//
// If the node being deleted has two children,
// swap its position with its successor node
//
if (CurrNode->Child[LEFT] && CurrNode->Child[RIGHT])
{
#if _DBG_
if (CheckSubTrie(PrevNode, &Depth) != NO_ERROR)
{
DbgBreakPoint();
}
#endif
SwapWithSuccessor(Trie, (PAVL_CONTEXT) Context);
#if _DBG_
if (CheckSubTrie(PrevNode, &Depth) != NO_ERROR)
{
DbgBreakPoint();
}
#endif
CurrNode = ((PAVL_CONTEXT) Context)->BestNode;
PrevNode = ((PAVL_CONTEXT) Context)->InsPoint;
NextChild = ((PAVL_CONTEXT) Context)->InsChild;
}
ASSERT(((PrevNode == NULL) && (Trie->TrieRoot == CurrNode))
|| (PrevNode->Child[NextChild] == CurrNode));
#if _DBG_
if (CheckTable(Table) != TRUE)
{
DbgBreakPoint();
}
#endif
AdjustPrefixes(Trie, CurrNode, CurrNode->Prefix, CurrNode, Context);
#if _DBG_
if (CheckTable(Table) != TRUE)
{
DbgBreakPoint();
}
#endif
if (!CurrNode->Child[LEFT])
{
// (LEFT Child = NULL) => Promote the right child
NextNode = CurrNode->Child[RIGHT];
if (NextNode)
{
NextNode->Parent = CurrNode->Parent;
}
}
else
{
// (RIGHT Child = NULL) => Promote the left child
ASSERT(!CurrNode->Child[RIGHT]);
NextNode = CurrNode->Child[LEFT];
NextNode->Parent = CurrNode->Parent;
}
if (PrevNode)
{
PrevNode->Child[NextChild] = NextNode;
// Balance trie if it was thrown off balance
BalanceAfterDelete(Trie, PrevNode, NextChild);
}
else
{
Trie->TrieRoot = NextNode;
}
*Data = CurrNode->Data;
DestroyTrieNode(Trie, CurrNode);
#if _DBG_
if (CheckTable(Table) != TRUE)
{
DbgBreakPoint();
}
#endif
return NO_ERROR;
}
DWORD
WINAPI
SearchInTable(
IN HANDLE Table,
IN USHORT NumBits,
IN PUCHAR KeyBits,
OUT PLOOKUP_CONTEXT Context OPTIONAL,
OUT PLOOKUP_LINKAGE *Data
)
{
PAVL_TRIE Trie;
PAVL_NODE PrevNode;
PAVL_NODE CurrNode;
PAVL_NODE BestNode;
AVL_BALANCE NextChild;
INT Comp;
#if _PROF_
UINT NumTravsDn;
UINT NumTravsUp;
#endif
Trie = Table;
ASSERT(NumBits <= Trie->MaxKeyBytes * BITS_IN_BYTE);
#if _PROF_
NumTravsDn = 0;
NumTravsUp = 0;
#endif
//
// Go down the trie using key comparisions
// in search of a prefix matching this key
//
CurrNode = Trie->TrieRoot;
PrevNode = NULL;
NextChild = LEFT;
BestNode = NULL;
while (CurrNode)
{
#if _PROF_
NumTravsDn++;
#endif
Comp = CompareFullKeys(KeyBits,
CurrNode->KeyBits,
Trie->MaxKeyBytes);
if ((Comp < 0) || ((Comp == 0) && (NumBits < CurrNode->NumBits)))
{
NextChild = LEFT;
}
else
if ((Comp > 0) || (NumBits > CurrNode->NumBits))
{
NextChild = RIGHT;
BestNode = CurrNode;
}
else
{
BestNode = CurrNode;
break;
}
PrevNode = CurrNode;
CurrNode = PrevNode->Child[NextChild];
}
if (!CurrNode)
{
//
// We do not have an exact match - so now
// we try to refine BestNode guess to get
// the next best prefix to the new prefix
//
while(BestNode)
{
if (BestNode->NumBits <= NumBits)
{
if (!(ComparePartialKeys(BestNode->KeyBits,
KeyBits,
BestNode->NumBits)))
{
break;
}
}
BestNode = BestNode->Prefix;
#if _PROF_
if (BestNode)
{
NumTravsUp++;
}
#endif
}
}
if (ARGUMENT_PRESENT(Context))
{
((PAVL_CONTEXT) Context)->BestNode = BestNode;
((PAVL_CONTEXT) Context)->InsPoint = PrevNode;
((PAVL_CONTEXT) Context)->InsChild = NextChild;
}
*Data = BestNode ? BestNode->Data : NULL;
#if _PROF_
Print("Num Travs Dn = %5d, Travs Up = %5d\n",
NumTravsDn,
NumTravsUp);
#endif
return CurrNode ? NO_ERROR : ERROR_NOT_FOUND;
}
DWORD
WINAPI
BestMatchInTable(
IN HANDLE Table,
IN PUCHAR KeyBits,
OUT PLOOKUP_LINKAGE *BestData
)
{
PAVL_TRIE Trie;
PAVL_NODE CurrNode;
PAVL_NODE BestNode;
INT Comp;
#if _PROF_
UINT NumTravsDn;
UINT NumTravsUp;
#endif
Trie = Table;
#if _PROF_
NumTravsDn = 0;
NumTravsUp = 0;
#endif
//
// Go down the trie using key comparisions
// in search of a prefix matching this key
//
CurrNode = Trie->TrieRoot;
BestNode = NULL;
while (CurrNode)
{
#if _PROF_
NumTravsDn++;
#endif
Comp = CompareFullKeys(KeyBits,
CurrNode->KeyBits,
Trie->MaxKeyBytes);
if (Comp < 0)
{
CurrNode = CurrNode->Child[LEFT];
}
else
{
BestNode = CurrNode;
CurrNode = CurrNode->Child[RIGHT];
}
}
//
// Now we refine the BestNode guess to get
// the next best prefix to the new prefix
//
while(BestNode)
{
if (!(ComparePartialKeys(BestNode->KeyBits,
KeyBits,
BestNode->NumBits)))
{
break;
}
BestNode = BestNode->Prefix;
#if _PROF_
if (BestNode)
{
NumTravsUp++;
}
#endif
}
*BestData = BestNode ? BestNode->Data : NULL;
#if _PROF_
Print("Num Travs Dn = %5d, Travs Up = %5d\n",
NumTravsDn,
NumTravsUp);
#endif
return NO_ERROR;
}
DWORD
WINAPI
NextMatchInTable(
IN HANDLE Table,
IN PLOOKUP_LINKAGE BestData,
OUT PLOOKUP_LINKAGE *NextBestData
)
{
PAVL_NODE BestNode;
UNREFERENCED_PARAMETER(Table);
//
// Assume the input data passed in is valid,
// and the data is one of the items in trie
//
BestNode = GET_NODEPTR_FROM_DATA(BestData);
*NextBestData = BestNode->Prefix ? BestNode->Prefix->Data : NULL;
return NO_ERROR;
}
DWORD
WINAPI
EnumOverTable(
IN HANDLE Table,
IN OUT PUSHORT StartNumBits,
IN OUT PUCHAR StartKeyBits,
IN OUT PLOOKUP_CONTEXT Context OPTIONAL,
IN USHORT StopNumBits OPTIONAL,
IN PUCHAR StopKeyBits OPTIONAL,
IN OUT PUINT NumItems,
OUT PLOOKUP_LINKAGE *DataItems
)
{
PAVL_TRIE Trie;
PLOOKUP_LINKAGE Data;
PAVL_NODE PrevNode;
PAVL_NODE CurrNode;
PAVL_NODE NextNode;
LOOKUP_CONTEXT Context1;
AVL_BALANCE NextChild;
UINT ItemsCopied;
INT Comp;
Trie = Table;
if (!ARGUMENT_PRESENT(Context))
{
// No context - initialize local context
Context = &Context1;
((PAVL_CONTEXT) Context)->InsChild = EVEN;
}
//
// If there is a search context that is valid,
// we will avoid doing a lookup (common case)
//
if (((PAVL_CONTEXT) Context)->InsChild == EVEN)
{
//
// If we did not find an exact match,
// remember it by modifying context
//
if (SearchInTable(Table,
*StartNumBits,
StartKeyBits,
Context,
&Data) != NO_ERROR)
{
((PAVL_CONTEXT) Context)->BestNode = NULL;
}
}
CurrNode = ((PAVL_CONTEXT) Context)->BestNode;
//
// If we did not find an exact match, find the
// successor ( node with smallest key > key )
//
if (!CurrNode)
{
PrevNode = ((PAVL_CONTEXT) Context)->InsPoint;
if (!PrevNode)
{
// No items copied
*NumItems = 0;
return ERROR_NO_MORE_ITEMS;
}
NextChild = ((PAVL_CONTEXT) Context)->InsChild;
if (NextChild == LEFT)
{
CurrNode = PrevNode;
}
else
{
CurrNode = PrevNode;
while (CurrNode->Parent)
{
if (CurrNode->Parent->Child[LEFT] == CurrNode)
{
break;
}
CurrNode = CurrNode->Parent;
}
if (CurrNode->Parent)
{
CurrNode = CurrNode->Parent;
}
else
{
// No Items copied
*NumItems = 0;
return ERROR_NO_MORE_ITEMS;
}
}
}
if (*NumItems == 0)
{
return ERROR_INVALID_PARAMETER;
}
//
// Enumeration Order: Node->LeftTree, Node, Node->RightTree
//
ItemsCopied = 0;
do
{
// Check if this dest is before Stop Prefix (if it exists)
if (StopKeyBits)
{
Comp = CompareFullKeys(CurrNode->KeyBits,
StopKeyBits,
Trie->MaxKeyBytes);
if (Comp == 0)
{
if (CurrNode->NumBits <= StopNumBits)
{
Comp = -1;
}
else
{
Comp = +1;
}
}
if (Comp > 0)
{
// Return Items Copied
*NumItems = ItemsCopied;
return ERROR_NO_MORE_ITEMS;
}
}
// Copy current data to the output buffer
DataItems[ItemsCopied++] = CurrNode->Data;
// Find successor (smallest node > this node)
if (CurrNode->Child[RIGHT])
{
NextNode = CurrNode->Child[RIGHT];
while (NextNode->Child[LEFT])
{
NextNode = NextNode->Child[LEFT];
}
CurrNode = NextNode;
}
else
{
while (CurrNode->Parent)
{
if (CurrNode->Parent->Child[LEFT] == CurrNode)
{
break;
}
CurrNode = CurrNode->Parent;
}
if (CurrNode->Parent)
{
CurrNode = CurrNode->Parent;
}
else
{
// Return Items Copied
*NumItems = ItemsCopied;
return ERROR_NO_MORE_ITEMS;
}
}
}
while (ItemsCopied < *NumItems);
// Update the temporary context
((PAVL_CONTEXT) Context)->BestNode = CurrNode;
// Update enumeration context by adjusting starting prefix
if (StartKeyBits)
{
*StartNumBits = CurrNode->NumBits;
CopyFullKeys(StartKeyBits,
CurrNode->KeyBits,
Trie->MaxKeyBytes);
}
// Return Items Copied
*NumItems = ItemsCopied;
return NO_ERROR;
}
DWORD
WINAPI
DestroyTable(
IN HANDLE Table
)
{
PAVL_TRIE Trie;
Trie = Table;
if (Trie->TrieRoot != NULL)
{
return ERROR_NOT_EMPTY;
}
ASSERT(Trie->NumNodes == 0);
#if _PROF_
Trie->MemoryInUse -= sizeof(AVL_TRIE);
#endif
FreeMemory(Trie);
return NO_ERROR;
}
BOOL
WINAPI
CheckTable(
IN HANDLE Table
)
{
BOOL Status;
USHORT Depth;
Status = CheckSubTrie(((PAVL_TRIE)Table)->TrieRoot, &Depth);
#if _DBG_
if (SUCCESS(Status))
{
Print("\nDepth of the AVL Trie = %lu\n\n", Depth);
}
#endif
return SUCCESS(Status) ? TRUE : FALSE;
}
VOID
WINAPI
DumpTable(
IN HANDLE Table,
IN DWORD Flags
)
{
PAVL_TRIE Trie;
Trie = Table;
Print("---------------- TABLE BEGIN ---------------------------\n\n");
if (Flags & SUMMARY)
{
;
}
#if PROF
if (Flags & STATS)
{
Print(
"Num of Ins = %6lu, Dels = %6lu, Sing Rots = %6lu, Dob Rots = %6lu\n"
"Num Allocs = %6lu, Free = %6lu, Num Nodes = %6lu, Mem Used = %6lu\n",
Trie->NumInsertions,
Trie->NumDeletions,
Trie->NumSingleRots,
Trie->NumDoubleRots,
Trie->NumAllocs,
Trie->NumFrees,
Trie->NumNodes,
Trie->MemoryInUse);
}
#endif
if (Flags & ITEMS)
{
Print("\n");
DumpSubTrie(Trie->TrieRoot);
Print("\n");
}
Print("---------------- TABLE END ---------------------------\n\n");
}
//
// Helper Functions - used in insert and delete
//
VOID
BalanceAfterInsert(
IN PAVL_TRIE Trie,
IN PAVL_NODE Node,
IN AVL_BALANCE Longer
)
{
#if _DBG_
Print("Balance after Insert Called: %p %02x\n", Node, Longer);
#endif
ASSERT((Longer == LEFT) || (Longer == RIGHT));
// Go up the tree adjusting the balances
while (Node->Balance == EVEN)
{
Node->Balance = Longer;
if (!Node->Parent)
{
return;
}
Longer = (Node->Parent->Child[LEFT] == Node) ? LEFT : RIGHT;
Node = Node->Parent;
}
// We made the balance of an ancestor even
if (Node->Balance != Longer)
{
Node->Balance = EVEN;
return;
}
// Unbalanced a ancestor - rotate the tree
if (Node->Child[Longer]->Balance == Longer)
{
SingleRotate(Trie, Node, (AVL_BALANCE) -Longer, &Node);
}
else
{
DoubleRotate(Trie, Node, (AVL_BALANCE) -Longer, &Node);
}
return;
}
VOID
BalanceAfterDelete(
IN PAVL_TRIE Trie,
IN PAVL_NODE Node,
IN AVL_BALANCE Shorter
)
{
#if _DBG_
Print("Balance after Delete Called: %p %02x\n", Node, Shorter);
#endif
ASSERT((Shorter == LEFT) || (Shorter == RIGHT));
while (TRUE)
{
if (Node->Balance == EVEN)
{
Node->Balance = -Shorter;
return;
}
if (Node->Balance == Shorter)
{
Node->Balance = EVEN;
}
else
{
ASSERT(Node->Child[-Shorter] != NULL);
if (Node->Child[-Shorter]->Balance == -Shorter)
{
SingleRotate(Trie, Node, Shorter, &Node);
}
else
if (Node->Child[-Shorter]->Balance == Shorter)
{
DoubleRotate(Trie, Node, Shorter, &Node);
}
else
{
SingleRotate(Trie, Node, Shorter, &Node);
Node->Balance = Shorter;
Node->Child[Shorter]->Balance = -Shorter;
return;
}
}
if (!Node->Parent)
{
return;
}
Shorter = (Node->Parent->Child[LEFT] == Node) ? LEFT : RIGHT;
Node = Node->Parent;
}
}
VOID
SingleRotate(
IN PAVL_TRIE Trie,
IN PAVL_NODE UnbalNode,
IN AVL_BALANCE Direction,
OUT PAVL_NODE *BalancedNode
)
{
PAVL_NODE PrevNode;
PAVL_NODE CurrNode;
PAVL_NODE NextNode;
#if _DBG_
Print("Single Rotate Called: %p %02x\n", UnbalNode, Direction);
#endif
#if PROF
Trie->NumSingleRots++;
#endif
ASSERT((Direction == LEFT) || (Direction == RIGHT));
CurrNode = UnbalNode;
ASSERT(CurrNode != NULL);
// To rotate right, we need left child and vice versa
NextNode = CurrNode->Child[-Direction];
ASSERT(NextNode != NULL);
//
// Promote the child to the unbalanced node's position
//
PrevNode = CurrNode->Parent;
if (PrevNode)
{
if (PrevNode->Child[LEFT] == CurrNode)
{
PrevNode->Child[LEFT] = NextNode;
}
else
{
PrevNode->Child[RIGHT] = NextNode;
}
}
else
{
Trie->TrieRoot = NextNode;
}
NextNode->Parent = PrevNode;
//
// Shift a subtree of child node to unbalanced node
//
CurrNode->Child[-Direction] = NextNode->Child[Direction];
if (NextNode->Child[Direction])
{
NextNode->Child[Direction]->Parent = CurrNode;
}
//
// Push unbalanced node as child of the next node
// in place of this subtree that was moved before
//
NextNode->Child[Direction] = CurrNode;
CurrNode->Parent = NextNode;
//
// Adjust balances that have changed due to rotation.
// When this is not accurate, the caller adjusts the
// balances appropriately upon return from this func.
//
CurrNode->Balance = NextNode->Balance = EVEN;
// Return the next node as the new balanced node
*BalancedNode = NextNode;
return;
}
VOID
DoubleRotate(
IN PAVL_TRIE Trie,
IN PAVL_NODE UnbalNode,
IN AVL_BALANCE Direction,
OUT PAVL_NODE *BalancedNode
)
{
PAVL_NODE PrevNode;
PAVL_NODE CurrNode;
PAVL_NODE NextNode;
PAVL_NODE LastNode;
#if _DBG_
Print("Double Rotate Called: %p %02x\n", UnbalNode, Direction);
#endif
#if PROF
Trie->NumDoubleRots++;
#endif
ASSERT((Direction == LEFT) || (Direction == RIGHT));
CurrNode = UnbalNode;
ASSERT(CurrNode != NULL);
//
// To rotate right, we need left child and its right child
//
NextNode = CurrNode->Child[-Direction];
ASSERT(NextNode != NULL);
LastNode = NextNode->Child[Direction];
ASSERT(LastNode != NULL);
//
// Move grandchild's children to other nodes higher up
//
CurrNode->Child[-Direction] = LastNode->Child[Direction];
if (LastNode->Child[Direction])
{
LastNode->Child[Direction]->Parent = CurrNode;
}
NextNode->Child[Direction] = LastNode->Child[-Direction];
if (LastNode->Child[-Direction])
{
LastNode->Child[-Direction]->Parent = NextNode;
}
//
// Adjust the balances after the above node movements
//
CurrNode->Balance = EVEN;
NextNode->Balance = EVEN;
if (LastNode->Balance == LEFT)
{
if (Direction == LEFT)
{
NextNode->Balance = RIGHT;
}
else
{
CurrNode->Balance = RIGHT;
}
}
else
if (LastNode->Balance == RIGHT)
{
if (Direction == LEFT)
{
CurrNode->Balance = LEFT;
}
else
{
NextNode->Balance = LEFT;
}
}
//
// Promote grandchild to the unbalanced node's position
//
PrevNode = CurrNode->Parent;
LastNode->Parent = PrevNode;
if (PrevNode)
{
if (PrevNode->Child[LEFT] == CurrNode)
{
PrevNode->Child[LEFT] = LastNode;
}
else
{
PrevNode->Child[RIGHT] = LastNode;
}
}
else
{
Trie->TrieRoot = LastNode;
}
LastNode->Child[-Direction] = NextNode;
NextNode->Parent = LastNode;
LastNode->Child[Direction] = CurrNode;
CurrNode->Parent = LastNode;
LastNode->Balance = EVEN;
// The grandchild node is the new balanced node now
*BalancedNode = LastNode;
return;
}
VOID
SwapWithSuccessor(
IN PAVL_TRIE Trie,
IN OUT PAVL_CONTEXT Context
)
{
PAVL_NODE PrevNode;
PAVL_NODE CurrNode;
PAVL_NODE NextNode;
PAVL_NODE TempNode1;
PAVL_NODE TempNode2;
AVL_BALANCE NextChild;
// Get the context before the successor swap
CurrNode = Context->BestNode;
PrevNode = Context->InsPoint;
NextChild = Context->InsChild;
ASSERT(CurrNode->Child[LEFT] && CurrNode->Child[RIGHT]);
// Find successor (smallest node > this node)
NextNode = CurrNode->Child[RIGHT];
while (NextNode->Child[LEFT])
{
NextNode = NextNode->Child[LEFT];
}
//
// Save info for swapping node with its successor
//
TempNode1 = NextNode->Parent;
TempNode2 = NextNode->Child[RIGHT];
//
// Promote the successor to the node's position
//
NextNode->Balance = CurrNode->Balance;
NextNode->Parent = PrevNode;
if (PrevNode)
{
PrevNode->Child[NextChild] = NextNode;
}
else
{
Trie->TrieRoot = NextNode;
}
NextNode->Child[LEFT] = CurrNode->Child[LEFT];
NextNode->Child[LEFT]->Parent = NextNode;
// Is the successor the immediate right child ?
if (NextNode != CurrNode->Child[RIGHT])
{
NextNode->Child[RIGHT] = CurrNode->Child[RIGHT];
CurrNode->Parent = TempNode1;
TempNode1->Child[LEFT] = CurrNode;
NextChild = LEFT;
}
else
{
NextNode->Child[RIGHT] = CurrNode;
NextChild = RIGHT;
}
NextNode->Child[RIGHT]->Parent = NextNode;
//
// Put the node in the successor position
//
CurrNode->Child[LEFT] = NULL;
CurrNode->Child[RIGHT] = TempNode2;
if (CurrNode->Child[RIGHT])
{
CurrNode->Child[RIGHT]->Parent = CurrNode;
CurrNode->Balance = RIGHT;
}
else
{
CurrNode->Balance = EVEN;
}
PrevNode = CurrNode->Parent;
//
// Adjust prefix relationship between the
// node and its successor (if it existed)
//
if (NextNode->Prefix == CurrNode)
{
NextNode->Prefix = CurrNode->Prefix;
}
// Update context to reflect the successor swap
Context->BestNode = CurrNode;
Context->InsPoint = PrevNode;
Context->InsChild = NextChild;
return;
}
VOID
AdjustPrefixes(
IN PAVL_TRIE Trie,
IN PAVL_NODE OldNode,
IN PAVL_NODE NewNode,
IN PAVL_NODE TheNode,
IN PLOOKUP_CONTEXT Context
)
{
PAVL_NODE CurrNode;
UINT NumItems;
PLOOKUP_LINKAGE Dummy;
DWORD Status;
INT Comp;
#if _PROF_
UINT NumChecks;
UINT NumAdjust;
#endif
#if _DBG_
Print("Adjust Prefix Called: %p %p %p\n", OldNode, NewNode, TheNode);
#endif
//
// If this is part of an insert, we end our prefixes'
// adjustment when we pass out of the range of the
// node being inserted, while in the case of delete
// the range is determined by the node being deleted
//
// This node being deleted or inserted is "TheNode"
//
ASSERT((OldNode == TheNode) || (NewNode == TheNode));
#if _PROF_
NumChecks = 0;
NumAdjust = 0;
#endif
NumItems = 1;
do
{
#if _PROF_
NumChecks++;
#endif
Status =
EnumOverTable(Trie, NULL, NULL, Context, 0, NULL, &NumItems, &Dummy);
CurrNode = ((PAVL_CONTEXT) Context)->BestNode;
if (CurrNode->NumBits > TheNode->NumBits)
{
// Did we reach the end of our range ?
Comp = ComparePartialKeys(CurrNode->KeyBits,
TheNode->KeyBits,
TheNode->NumBits);
if (Comp > 0)
{
break;
}
if (CurrNode->Prefix == OldNode)
{
#if _PROF_
NumAdjust++;
#endif
CurrNode->Prefix = NewNode;
}
}
}
while (Status != ERROR_NO_MORE_ITEMS);
#if _PROF_
Print("Num Checks = %5d, Num Adjusts = %5d\n",
NumChecks,
NumAdjust);
#endif
}
//
// Helper Functions - used in CheckTable
//
DWORD
CheckSubTrie(
IN PAVL_NODE Node,
OUT PUSHORT Depth
)
{
DWORD Status;
USHORT LDepth;
USHORT RDepth;
Status = NO_ERROR;
*Depth = 0;
#if WRN
LDepth = 0;
RDepth = 0;
#endif
if (Node)
{
if (SUCCESS(Status))
{
Status = CheckSubTrie(Node->Child[LEFT], &LDepth);
}
if (SUCCESS(Status))
{
Status = CheckSubTrie(Node->Child[RIGHT], &RDepth);
}
if (SUCCESS(Status))
{
Status = CheckTrieNode(Node, LDepth, RDepth);
if (!SUCCESS(Status))
{
Print("Inconsistent information @ Node: %p\n",
Node);
}
}
if (SUCCESS(Status))
{
*Depth = (USHORT)((LDepth > RDepth) ? (LDepth + 1) : (RDepth + 1));
}
}
return Status;
}
DWORD
CheckTrieNode(
IN PAVL_NODE Node,
IN USHORT LDepth,
IN USHORT RDepth
)
{
AVL_BALANCE Balance;
// Check the balance first w.r.t LDepth and RDepth
Balance = RDepth - LDepth;
if ((Balance < -1) || (Balance > 1))
{
Print("Balance out of bounds: %d\n", Balance);
Print("LDepth = %lu, RDepth = %lu, NodeBal = %d\n",
LDepth, RDepth, Node->Balance);
DumpSubTrie(Node);
return ERROR_INVALID_DATA;
}
if (Balance != Node->Balance)
{
Print("Balance inconsistent\n");
return ERROR_INVALID_DATA;
}
// Check its child relationship with its parent
if (Node->Parent)
{
if ((Node->Parent->Child[LEFT] != Node) &&
(Node->Parent->Child[RIGHT] != Node))
{
Print("Parent relationship bad\n");
return ERROR_INVALID_DATA;
}
}
// Check its prefix relationship with its prefix
if (Node->Prefix)
{
if (Node->Prefix->NumBits >= Node->NumBits)
{
Print("Prefix relationship bad @1\n");
return ERROR_INVALID_DATA;
}
if (ComparePartialKeys(Node->Prefix->KeyBits,
Node->KeyBits,
Node->Prefix->NumBits) != 0)
{
Print("Prefix relationship bad @2\n");
return ERROR_INVALID_DATA;
}
}
return NO_ERROR;
}
//
// Helper Functions - used in DumpTable
//
VOID
DumpSubTrie(
IN PAVL_NODE Node
)
{
if (Node)
{
DumpSubTrie(Node->Child[LEFT]);
DumpTrieNode(Node);
DumpSubTrie(Node->Child[RIGHT]);
}
}
VOID
DumpTrieNode(
IN PAVL_NODE Node
)
{
USHORT i;
if (Node)
{
Print("TrieNode @ %p: NB = %8d, KB = ", Node, Node->NumBits);
for (i = 0; i < (Node->NumBits + BITS_IN_BYTE - 1) / BITS_IN_BYTE; i++)
{
Print("%3d.", Node->KeyBits[i]);
}
Print("\nLeft = %p, Parent = %p, Right = %p\n",
Node->Child[LEFT],
Node->Parent,
Node->Child[RIGHT]);
Print("Prefix = %p, Data = %p, Balance = %2d\n\n",
Node->Prefix,
Node->Data,
Node->Balance);
}
}