/* * 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 #define NDEBUG #include /* 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 */