reactos/sdk/lib/rtl/generictable.c

513 lines
14 KiB
C

/*
* PROJECT: ReactOS Runtime Library
* LICENSE: GPL - See COPYING in the top level directory
* FILE: lib/rtl/generictable.c
* PURPOSE: Splay Tree Generic Table Implementation
* PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <rtl.h>
#define NDEBUG
#include <debug.h>
/* Internal header for table entries */
typedef struct _TABLE_ENTRY_HEADER
{
RTL_SPLAY_LINKS SplayLinks;
LIST_ENTRY ListEntry;
LONGLONG UserData;
} TABLE_ENTRY_HEADER, *PTABLE_ENTRY_HEADER;
/* PRIVATE FUNCTIONS *********************************************************/
TABLE_SEARCH_RESULT
NTAPI
RtlpFindGenericTableNodeOrParent(IN PRTL_GENERIC_TABLE Table,
IN PVOID Buffer,
OUT PRTL_SPLAY_LINKS *NodeOrParent)
{
PRTL_SPLAY_LINKS CurrentNode, ChildNode;
RTL_GENERIC_COMPARE_RESULTS Result;
/* Quick check to see if the table is empty */
if (RtlIsGenericTableEmpty(Table))
{
return TableEmptyTree;
}
/* Set the current node */
CurrentNode = Table->TableRoot;
/* Start compare loop */
while (TRUE)
{
/* Do the compare */
Result = Table->CompareRoutine(Table,
Buffer,
&((PTABLE_ENTRY_HEADER)CurrentNode)->
UserData);
if (Result == GenericLessThan)
{
/* We're less, check if this is the left child */
if ((ChildNode = RtlLeftChild(CurrentNode)))
{
/* Continue searching from this node */
CurrentNode = ChildNode;
}
else
{
/* Otherwise, the element isn't in this tree */
*NodeOrParent = CurrentNode;
return TableInsertAsLeft;
}
}
else if (Result == GenericGreaterThan)
{
/* We're more, check if this is the right child */
if ((ChildNode = RtlRightChild(CurrentNode)))
{
/* Continue searching from this node */
CurrentNode = ChildNode;
}
else
{
/* Otherwise, the element isn't in this tree */
*NodeOrParent = CurrentNode;
return TableInsertAsRight;
}
}
else
{
/* We should've found the node */
ASSERT(Result == GenericEqual);
/* Return node found */
*NodeOrParent = CurrentNode;
return TableFoundNode;
}
}
}
/* SPLAY FUNCTIONS ***********************************************************/
/*
* @implemented
*/
VOID
NTAPI
RtlInitializeGenericTable(IN PRTL_GENERIC_TABLE Table,
IN PRTL_GENERIC_COMPARE_ROUTINE CompareRoutine,
IN PRTL_GENERIC_ALLOCATE_ROUTINE AllocateRoutine,
IN PRTL_GENERIC_FREE_ROUTINE FreeRoutine,
IN PVOID TableContext)
{
/* Initialize the table to default and passed values */
InitializeListHead(&Table->InsertOrderList);
Table->TableRoot = NULL;
Table->NumberGenericTableElements = 0;
Table->WhichOrderedElement = 0;
Table->OrderedPointer = &Table->InsertOrderList;
Table->CompareRoutine = CompareRoutine;
Table->AllocateRoutine = AllocateRoutine;
Table->FreeRoutine = FreeRoutine;
Table->TableContext = TableContext;
}
/*
* @implemented
*/
PVOID
NTAPI
RtlInsertElementGenericTable(IN PRTL_GENERIC_TABLE Table,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PBOOLEAN NewElement OPTIONAL)
{
PRTL_SPLAY_LINKS NodeOrParent;
TABLE_SEARCH_RESULT Result;
/* Get the splay links and table search result immediately */
Result = RtlpFindGenericTableNodeOrParent(Table, Buffer, &NodeOrParent);
/* Now call the routine to do the full insert */
return RtlInsertElementGenericTableFull(Table,
Buffer,
BufferSize,
NewElement,
NodeOrParent,
Result);
}
/*
* @implemented
*/
PVOID
NTAPI
RtlInsertElementGenericTableFull(IN PRTL_GENERIC_TABLE Table,
IN PVOID Buffer,
IN ULONG BufferSize,
OUT PBOOLEAN NewElement OPTIONAL,
IN PVOID NodeOrParent,
IN TABLE_SEARCH_RESULT SearchResult)
{
PRTL_SPLAY_LINKS NewNode;
/* Check if the entry wasn't already found */
if (SearchResult != TableFoundNode)
{
/* We're doing an allocation, sanity check */
ASSERT(Table->NumberGenericTableElements != (MAXULONG - 1));
/* Allocate a node */
NewNode = Table->AllocateRoutine(Table,
BufferSize +
FIELD_OFFSET(TABLE_ENTRY_HEADER,
UserData));
if (!NewNode)
{
/* No memory or other allocation error, fail */
if (NewElement) *NewElement = FALSE;
return NULL;
}
/* Initialize the new inserted element */
RtlInitializeSplayLinks(NewNode);
InsertTailList(&Table->InsertOrderList,
&((PTABLE_ENTRY_HEADER)NewNode)->ListEntry);
/* Increase element count */
Table->NumberGenericTableElements++;
/* Check where we should insert the entry */
if (SearchResult == TableEmptyTree)
{
/* This is the new root node */
Table->TableRoot = NewNode;
}
else if (SearchResult == TableInsertAsLeft)
{
/* Insert it left */
RtlInsertAsLeftChild(NodeOrParent, NewNode);
}
else
{
/* Right node */
RtlInsertAsRightChild(NodeOrParent, NewNode);
}
/* Copy user buffer */
RtlCopyMemory(&((PTABLE_ENTRY_HEADER)NewNode)->UserData,
Buffer,
BufferSize);
}
else
{
/* Return the node we already found */
NewNode = NodeOrParent;
}
/* Splay the tree */
Table->TableRoot = RtlSplay(NewNode);
/* Return status */
if (NewElement) *NewElement = (SearchResult != TableFoundNode);
/* Return pointer to user data */
return &((PTABLE_ENTRY_HEADER)NewNode)->UserData;
}
/*
* @implemented
*/
BOOLEAN
NTAPI
RtlIsGenericTableEmpty(IN PRTL_GENERIC_TABLE Table)
{
/* Check if the table root is empty */
return (Table->TableRoot) ? FALSE: TRUE;
}
/*
* @implemented
*/
ULONG
NTAPI
RtlNumberGenericTableElements(IN PRTL_GENERIC_TABLE Table)
{
/* Return the number of elements */
return Table->NumberGenericTableElements;
}
/*
* @implemented
*/
PVOID
NTAPI
RtlLookupElementGenericTable(IN PRTL_GENERIC_TABLE Table,
IN PVOID Buffer)
{
PRTL_SPLAY_LINKS NodeOrParent;
TABLE_SEARCH_RESULT Result;
/* Call the full version */
return RtlLookupElementGenericTableFull(Table,
Buffer,
(PVOID)&NodeOrParent,
&Result);
}
/*
* @implemented
*/
PVOID
NTAPI
RtlLookupElementGenericTableFull(IN PRTL_GENERIC_TABLE Table,
IN PVOID Buffer,
OUT PVOID *NodeOrParent,
OUT TABLE_SEARCH_RESULT *SearchResult)
{
/* Do the initial lookup */
*SearchResult = RtlpFindGenericTableNodeOrParent(Table,
Buffer,
(PRTL_SPLAY_LINKS *)
NodeOrParent);
/* Check if we found anything */
if ((*SearchResult == TableEmptyTree) || (*SearchResult != TableFoundNode))
{
/* Nothing found */
return NULL;
}
/* Otherwise, splay the tree and return this entry */
Table->TableRoot = RtlSplay(*NodeOrParent);
return &((PTABLE_ENTRY_HEADER)*NodeOrParent)->UserData;
}
/*
* @implemented
*/
BOOLEAN
NTAPI
RtlDeleteElementGenericTable(IN PRTL_GENERIC_TABLE Table,
IN PVOID Buffer)
{
PRTL_SPLAY_LINKS NodeOrParent;
TABLE_SEARCH_RESULT Result;
/* Get the splay links and table search result immediately */
Result = RtlpFindGenericTableNodeOrParent(Table, Buffer, &NodeOrParent);
if (Result != TableFoundNode)
{
/* Nothing to delete */
return FALSE;
}
/* Delete the entry */
Table->TableRoot = RtlDelete(NodeOrParent);
RemoveEntryList(&((PTABLE_ENTRY_HEADER)NodeOrParent)->ListEntry);
/* Update accounting data */
Table->NumberGenericTableElements--;
Table->WhichOrderedElement = 0;
Table->OrderedPointer = &Table->InsertOrderList;
/* Free the entry */
Table->FreeRoutine(Table, NodeOrParent);
return TRUE;
}
/*
* @implemented
*/
PVOID
NTAPI
RtlEnumerateGenericTable(IN PRTL_GENERIC_TABLE Table,
IN BOOLEAN Restart)
{
PRTL_SPLAY_LINKS FoundNode;
/* Check if the table is empty */
if (RtlIsGenericTableEmpty(Table)) return NULL;
/* Check if we have to restart */
if (Restart)
{
/* Then find the leftmost element */
FoundNode = Table->TableRoot;
while(RtlLeftChild(FoundNode))
{
/* Get the left child */
FoundNode = RtlLeftChild(FoundNode);
}
/* Splay it */
_Analysis_assume_(FoundNode != NULL);
Table->TableRoot = RtlSplay(FoundNode);
}
else
{
/* Otherwise, try using the real successor */
FoundNode = RtlRealSuccessor(Table->TableRoot);
if (FoundNode) Table->TableRoot = RtlSplay(FoundNode);
}
/* Check if we found the node and return it */
return FoundNode ? &((PTABLE_ENTRY_HEADER)FoundNode)->UserData : NULL;
}
/*
* @implemented
*/
PVOID
NTAPI
RtlEnumerateGenericTableWithoutSplaying(IN PRTL_GENERIC_TABLE Table,
IN OUT PVOID *RestartKey)
{
PRTL_SPLAY_LINKS FoundNode;
/* Check if the table is empty */
if (RtlIsGenericTableEmpty(Table)) return NULL;
/* Check if we have to restart */
if (!(*RestartKey))
{
/* Then find the leftmost element */
FoundNode = Table->TableRoot;
while(RtlLeftChild(FoundNode))
{
/* Get the left child */
FoundNode = RtlLeftChild(FoundNode);
}
/* Splay it */
*RestartKey = FoundNode;
}
else
{
/* Otherwise, try using the real successor */
FoundNode = RtlRealSuccessor(*RestartKey);
if (FoundNode) *RestartKey = FoundNode;
}
/* Check if we found the node and return it */
return FoundNode ? &((PTABLE_ENTRY_HEADER)FoundNode)->UserData : NULL;
}
/*
* @unimplemented
*/
PVOID
NTAPI
RtlEnumerateGenericTableLikeADirectory(IN PRTL_AVL_TABLE Table,
IN PRTL_AVL_MATCH_FUNCTION MatchFunction,
IN PVOID MatchData,
IN ULONG NextFlag,
IN OUT PVOID *RestartKey,
IN OUT PULONG DeleteCount,
IN OUT PVOID Buffer)
{
UNIMPLEMENTED;
return 0;
}
/*
* @implemented
*/
PVOID
NTAPI
RtlGetElementGenericTable(IN PRTL_GENERIC_TABLE Table,
IN ULONG I)
{
ULONG OrderedElement, ElementCount;
PLIST_ENTRY OrderedNode;
ULONG DeltaUp, DeltaDown;
ULONG NextI = I + 1;
/* Setup current accounting data */
OrderedNode = Table->OrderedPointer;
OrderedElement = Table->WhichOrderedElement;
ElementCount = Table->NumberGenericTableElements;
/* Sanity checks */
if ((I == MAXULONG) || (NextI > ElementCount)) return NULL;
/* Check if we already found the entry */
if (NextI == OrderedElement)
{
/* Return it */
return &CONTAINING_RECORD(OrderedNode,
TABLE_ENTRY_HEADER,
ListEntry)->UserData;
}
/* Now check if we're farther behind */
if (OrderedElement > NextI)
{
/* Find out if the distance is more then the half-way point */
if (NextI > (OrderedElement / 2))
{
/* Do the search backwards, since this takes less iterations */
DeltaDown = OrderedElement - NextI;
while (DeltaDown)
{
/* Get next node */
OrderedNode = OrderedNode->Blink;
DeltaDown--;
}
}
else
{
/* Follow the list directly instead */
OrderedNode = &Table->InsertOrderList;
while (NextI)
{
/* Get next node */
OrderedNode = OrderedNode->Flink;
NextI--;
}
}
}
else
{
/* We are farther ahead, calculate distances */
DeltaUp = NextI - OrderedElement;
DeltaDown = (ElementCount - NextI) + 1;
/* Check if the up distance is smaller then the down distance */
if (DeltaUp <= DeltaDown)
{
/* Do the search forwards, since this takes less iterations */
while (DeltaUp)
{
/* Get next node */
OrderedNode = OrderedNode->Blink;
DeltaUp--;
}
}
else
{
/* Do the search downwards, since this takes less iterations */
OrderedNode = &Table->InsertOrderList;
while (DeltaDown)
{
/* Get next node */
OrderedNode = OrderedNode->Blink;
DeltaDown--;
}
}
}
/* Got the element, save it */
Table->OrderedPointer = OrderedNode;
Table->WhichOrderedElement = NextI;
/* Return the element */
return &CONTAINING_RECORD(OrderedNode,
TABLE_ENTRY_HEADER,
ListEntry)->UserData;
}
/* EOF */