2 * PROJECT: ReactOS Runtime Library
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: lib/rtl/generictable.c
5 * PURPOSE: Splay Tree Generic Table Implementation
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
15 /* Internal header for table entries */
16 typedef struct _TABLE_ENTRY_HEADER
18 RTL_SPLAY_LINKS SplayLinks
;
21 } TABLE_ENTRY_HEADER
, *PTABLE_ENTRY_HEADER
;
23 /* PRIVATE FUNCTIONS *********************************************************/
27 RtlpFindGenericTableNodeOrParent(IN PRTL_GENERIC_TABLE Table
,
29 OUT PRTL_SPLAY_LINKS
*NodeOrParent
)
31 PRTL_SPLAY_LINKS CurrentNode
, ChildNode
;
32 RTL_GENERIC_COMPARE_RESULTS Result
;
34 /* Quick check to see if the table is empty */
35 if (RtlIsGenericTableEmpty(Table
))
38 return TableEmptyTree
;
41 /* Set the current node */
42 CurrentNode
= Table
->TableRoot
;
44 /* Start compare loop */
48 Result
= Table
->CompareRoutine(Table
,
50 &((PTABLE_ENTRY_HEADER
)CurrentNode
)->
52 if (Result
== GenericLessThan
)
54 /* We're less, check if this is the left child */
55 if ((ChildNode
= RtlLeftChild(CurrentNode
)))
57 /* Continue searching from this node */
58 CurrentNode
= ChildNode
;
62 /* Otherwise, the element isn't in this tree */
63 *NodeOrParent
= CurrentNode
;
64 return TableInsertAsLeft
;
67 else if (Result
== GenericGreaterThan
)
69 /* We're more, check if this is the right child */
70 if ((ChildNode
= RtlRightChild(CurrentNode
)))
72 /* Continue searching from this node */
73 CurrentNode
= ChildNode
;
77 /* Otherwise, the element isn't in this tree */
78 *NodeOrParent
= CurrentNode
;
79 return TableInsertAsRight
;
84 /* We should've found the node */
85 ASSERT(Result
== GenericEqual
);
87 /* Return node found */
88 *NodeOrParent
= CurrentNode
;
89 return TableFoundNode
;
94 /* SPLAY FUNCTIONS ***********************************************************/
101 RtlInitializeGenericTable(IN PRTL_GENERIC_TABLE Table
,
102 IN PRTL_GENERIC_COMPARE_ROUTINE CompareRoutine
,
103 IN PRTL_GENERIC_ALLOCATE_ROUTINE AllocateRoutine
,
104 IN PRTL_GENERIC_FREE_ROUTINE FreeRoutine
,
105 IN PVOID TableContext
)
107 /* Initialize the table to default and passed values */
108 InitializeListHead(&Table
->InsertOrderList
);
109 Table
->TableRoot
= NULL
;
110 Table
->NumberGenericTableElements
= 0;
111 Table
->WhichOrderedElement
= 0;
112 Table
->OrderedPointer
= &Table
->InsertOrderList
;
113 Table
->CompareRoutine
= CompareRoutine
;
114 Table
->AllocateRoutine
= AllocateRoutine
;
115 Table
->FreeRoutine
= FreeRoutine
;
116 Table
->TableContext
= TableContext
;
124 RtlInsertElementGenericTable(IN PRTL_GENERIC_TABLE Table
,
127 OUT PBOOLEAN NewElement OPTIONAL
)
129 PRTL_SPLAY_LINKS NodeOrParent
;
130 TABLE_SEARCH_RESULT Result
;
132 /* Get the splay links and table search result immediately */
133 Result
= RtlpFindGenericTableNodeOrParent(Table
, Buffer
, &NodeOrParent
);
135 /* Now call the routine to do the full insert */
136 return RtlInsertElementGenericTableFull(Table
,
149 RtlInsertElementGenericTableFull(IN PRTL_GENERIC_TABLE Table
,
152 OUT PBOOLEAN NewElement OPTIONAL
,
153 IN PVOID NodeOrParent
,
154 IN TABLE_SEARCH_RESULT SearchResult
)
156 PRTL_SPLAY_LINKS NewNode
;
158 /* Check if the entry wasn't already found */
159 if (SearchResult
!= TableFoundNode
)
161 /* We're doing an allocation, sanity check */
162 ASSERT(Table
->NumberGenericTableElements
!= (MAXULONG
- 1));
164 /* Allocate a node */
165 NewNode
= Table
->AllocateRoutine(Table
,
167 FIELD_OFFSET(TABLE_ENTRY_HEADER
,
171 /* No memory or other allocation error, fail */
172 if (NewElement
) *NewElement
= FALSE
;
176 /* Initialize the new inserted element */
177 RtlInitializeSplayLinks(NewNode
);
178 InsertTailList(&Table
->InsertOrderList
,
179 &((PTABLE_ENTRY_HEADER
)NewNode
)->ListEntry
);
181 /* Increase element count */
182 Table
->NumberGenericTableElements
++;
184 /* Check where we should insert the entry */
185 if (SearchResult
== TableEmptyTree
)
187 /* This is the new root node */
188 Table
->TableRoot
= NewNode
;
190 else if (SearchResult
== TableInsertAsLeft
)
193 RtlInsertAsLeftChild(NodeOrParent
, NewNode
);
198 RtlInsertAsRightChild(NodeOrParent
, NewNode
);
201 /* Copy user buffer */
202 RtlCopyMemory(&((PTABLE_ENTRY_HEADER
)NewNode
)->UserData
,
208 /* Return the node we already found */
209 NewNode
= NodeOrParent
;
213 Table
->TableRoot
= RtlSplay(NewNode
);
216 if (NewElement
) *NewElement
= (SearchResult
== TableFoundNode
);
218 /* Return pointer to user data */
219 return &((PTABLE_ENTRY_HEADER
)NewNode
)->UserData
;
227 RtlIsGenericTableEmpty(IN PRTL_GENERIC_TABLE Table
)
229 /* Check if the table root is empty */
230 return (Table
->TableRoot
) ? FALSE
: TRUE
;
238 RtlNumberGenericTableElements(IN PRTL_GENERIC_TABLE Table
)
240 /* Return the number of elements */
241 return Table
->NumberGenericTableElements
;
249 RtlLookupElementGenericTable(IN PRTL_GENERIC_TABLE Table
,
252 PRTL_SPLAY_LINKS NodeOrParent
;
253 TABLE_SEARCH_RESULT Result
;
255 /* Call the full version */
256 return RtlLookupElementGenericTableFull(Table
,
258 (PVOID
)&NodeOrParent
,
267 RtlLookupElementGenericTableFull(IN PRTL_GENERIC_TABLE Table
,
269 OUT PVOID
*NodeOrParent
,
270 OUT TABLE_SEARCH_RESULT
*SearchResult
)
272 /* Do the initial lookup */
273 *SearchResult
= RtlpFindGenericTableNodeOrParent(Table
,
278 /* Check if we found anything */
279 if ((*SearchResult
== TableEmptyTree
) || (*SearchResult
!= TableFoundNode
))
285 /* Otherwise, splay the tree and return this entry */
286 Table
->TableRoot
= RtlSplay(*NodeOrParent
);
287 return &((PTABLE_ENTRY_HEADER
)*NodeOrParent
)->UserData
;
295 RtlDeleteElementGenericTable(IN PRTL_GENERIC_TABLE Table
,
298 PRTL_SPLAY_LINKS NodeOrParent
;
299 TABLE_SEARCH_RESULT Result
;
301 /* Get the splay links and table search result immediately */
302 Result
= RtlpFindGenericTableNodeOrParent(Table
, Buffer
, &NodeOrParent
);
303 if ((Result
== TableEmptyTree
) || (Result
!= TableFoundNode
))
305 /* Nothing to delete */
309 /* Delete the entry */
310 Table
->TableRoot
= RtlDelete(NodeOrParent
);
311 RemoveEntryList(&((PTABLE_ENTRY_HEADER
)NodeOrParent
)->ListEntry
);
313 /* Update accounting data */
314 Table
->NumberGenericTableElements
--;
315 Table
->WhichOrderedElement
= 0;
316 Table
->OrderedPointer
= &Table
->InsertOrderList
;
319 Table
->FreeRoutine(Table
, NodeOrParent
);
328 RtlEnumerateGenericTable(IN PRTL_GENERIC_TABLE Table
,
331 PRTL_SPLAY_LINKS FoundNode
;
333 /* Check if the table is empty */
334 if (RtlIsGenericTableEmpty(Table
)) return NULL
;
336 /* Check if we have to restart */
339 /* Then find the leftmost element */
340 FoundNode
= Table
->TableRoot
;
343 /* Get the left child */
344 FoundNode
= RtlLeftChild(FoundNode
);
345 } while(RtlLeftChild(FoundNode
));
348 Table
->TableRoot
= RtlSplay(FoundNode
);
352 /* Otherwise, try using the real successor */
353 FoundNode
= RtlRealSuccessor(Table
->TableRoot
);
354 if (FoundNode
) Table
->TableRoot
= RtlSplay(FoundNode
);
357 /* Check if we found the node and return it */
358 return FoundNode
? &((PTABLE_ENTRY_HEADER
)FoundNode
)->UserData
: NULL
;
366 RtlEnumerateGenericTableWithoutSplaying(IN PRTL_GENERIC_TABLE Table
,
367 IN OUT PVOID
*RestartKey
)
369 PRTL_SPLAY_LINKS FoundNode
;
371 /* Check if the table is empty */
372 if (RtlIsGenericTableEmpty(Table
)) return NULL
;
374 /* Check if we have to restart */
377 /* Then find the leftmost element */
378 FoundNode
= Table
->TableRoot
;
381 /* Get the left child */
382 FoundNode
= RtlLeftChild(FoundNode
);
383 } while(RtlLeftChild(FoundNode
));
386 *RestartKey
= FoundNode
;
390 /* Otherwise, try using the real successor */
391 FoundNode
= RtlRealSuccessor(*RestartKey
);
392 if (FoundNode
) *RestartKey
= FoundNode
;
395 /* Check if we found the node and return it */
396 return FoundNode
? &((PTABLE_ENTRY_HEADER
)FoundNode
)->UserData
: NULL
;
404 RtlEnumerateGenericTableLikeADirectory(IN PRTL_AVL_TABLE Table
,
405 IN PRTL_AVL_MATCH_FUNCTION MatchFunction
,
408 IN OUT PVOID
*RestartKey
,
409 IN OUT PULONG DeleteCount
,
421 RtlGetElementGenericTable(IN PRTL_GENERIC_TABLE Table
,
424 ULONG OrderedElement
, ElementCount
;
425 PLIST_ENTRY OrderedNode
;
426 ULONG DeltaUp
, DeltaDown
;
429 /* Setup current accounting data */
430 OrderedNode
= Table
->OrderedPointer
;
431 OrderedElement
= Table
->WhichOrderedElement
;
432 ElementCount
= Table
->NumberGenericTableElements
;
435 if ((I
== MAXULONG
) || (NextI
> ElementCount
)) return NULL
;
437 /* Check if we already found the entry */
438 if (NextI
== OrderedElement
)
441 return &((PTABLE_ENTRY_HEADER
)CONTAINING_RECORD(OrderedNode
,
443 ListEntry
))->UserData
;
446 /* Now check if we're farther behind */
447 if (OrderedElement
> NextI
)
449 /* Find out if the distance is more then the half-way point */
450 if (NextI
> (OrderedElement
/ 2))
452 /* Do the search backwards, since this takes less iterations */
453 DeltaDown
= OrderedElement
- NextI
;
457 OrderedNode
= OrderedNode
->Blink
;
458 } while (--DeltaDown
);
462 /* Follow the list directly instead */
463 OrderedNode
= &Table
->InsertOrderList
;
467 OrderedNode
= OrderedNode
->Flink
;
473 /* We are farther ahead, calculate distances */
474 DeltaUp
= NextI
- OrderedElement
;
475 DeltaDown
= (ElementCount
- NextI
) + 1;
477 /* Check if the up distance is smaller then the down distance */
478 if (DeltaUp
<= DeltaDown
)
480 /* Do the search forwards, since this takes less iterations */
484 OrderedNode
= OrderedNode
->Blink
;
489 /* Do the search downwards, since this takes less iterations */
490 OrderedNode
= &Table
->InsertOrderList
;
494 OrderedNode
= OrderedNode
->Blink
;
495 } while (--DeltaDown
);
499 /* Got the element, save it */
500 Table
->OrderedPointer
= OrderedNode
;
501 Table
->WhichOrderedElement
= NextI
;
503 /* Return the element */
504 return &((PTABLE_ENTRY_HEADER
)CONTAINING_RECORD(OrderedNode
,
506 ListEntry
))->UserData
;