mirror of
https://github.com/reactos/reactos.git
synced 2024-12-28 10:04:49 +00:00
513 lines
14 KiB
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 */
|