1632 lines
34 KiB
C
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);
|
|
}
|
|
}
|