|
|
/*++
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); } }
|