3 * Copyright (C) 1998-2002 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * PROJECT: ReactOS kernel
22 * PURPOSE: Binary tree support
23 * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
25 * 15-03-2002 CSH Created
30 #include <internal/debug.h>
32 /* DATA **********************************************************************/
34 typedef struct _BINARY_TREE_NODE
36 struct _BINARY_TREE_NODE
* Parent
;
37 struct _BINARY_TREE_NODE
* LeftChild
;
38 struct _BINARY_TREE_NODE
* RightChild
;
41 } BINARY_TREE_NODE
, *PBINARY_TREE_NODE
;
43 typedef struct _TRAVERSE_CONTEXT
{
44 PTRAVERSE_ROUTINE Routine
;
46 } TRAVERSE_CONTEXT
, *PTRAVERSE_CONTEXT
;
48 /* FUNCTIONS ****************************************************************/
50 #define ExpBinaryTreeRootNode(Tree)(((PBINARY_TREE) (Tree))->RootNode)
51 #define ExpBinaryTreeIsExternalNode(Node)(((Node)->LeftChild == NULL) && ((Node)->RightChild == NULL))
52 #define ExpBinaryTreeIsInternalNode(Node)(!ExpBinaryTreeIsExternalNode(Node))
53 #define ExpBinaryTreeNodeKey(Node)((Node)->Key)
54 #define ExpBinaryTreeNodeValue(Node)((Node)->Value)
55 #define ExpBinaryTreeParentNode(Node)((Node)->Parent)
56 #define ExpBinaryTreeLeftChildNode(Node)((Node)->LeftChild)
57 #define ExpBinaryTreeRightChildNode(Node)((Node)->RightChild)
58 #define ExpBinaryTreeNodeEqual(Equality)((Equality) == 0)
59 #define ExpBinaryTreeNodeLess(Equality)((Equality) < 0)
60 #define ExpBinaryTreeNodeMore(Equality)((Equality) > 0)
64 * Lock the binary tree
67 ExpLockBinaryTree(PBINARY_TREE Tree
,
70 if (Tree
->UseNonPagedPool
)
72 KeAcquireSpinLock(&Tree
->Lock
.NonPaged
, OldIrql
);
76 ExAcquireFastMutex(&Tree
->Lock
.Paged
);
82 * Unlock the binary tree
85 ExpUnlockBinaryTree(PBINARY_TREE Tree
,
88 if (Tree
->UseNonPagedPool
)
90 KeReleaseSpinLock(&Tree
->Lock
.NonPaged
, *OldIrql
);
94 ExReleaseFastMutex(&Tree
->Lock
.Paged
);
100 * Allocate resources for a new node and initialize it.
102 inline PBINARY_TREE_NODE
103 ExpCreateBinaryTreeNode(PBINARY_TREE Tree
,
104 PBINARY_TREE_NODE Parent
,
107 PBINARY_TREE_NODE Node
;
109 if (Tree
->UseNonPagedPool
)
111 Node
= (PBINARY_TREE_NODE
) ExAllocateFromNPagedLookasideList(&Tree
->List
.NonPaged
);
115 Node
= (PBINARY_TREE_NODE
) ExAllocateFromPagedLookasideList(&Tree
->List
.Paged
);
120 ExpBinaryTreeParentNode(Node
) = Parent
;
121 ExpBinaryTreeLeftChildNode(Node
) = NULL
;
122 ExpBinaryTreeRightChildNode(Node
) = NULL
;
123 ExpBinaryTreeNodeValue(Node
) = Value
;
131 * Release resources for the node.
134 ExpDestroyBinaryTreeNode(PBINARY_TREE Tree
,
135 PBINARY_TREE_NODE Node
)
137 if (Tree
->UseNonPagedPool
)
139 ExFreeToNPagedLookasideList(&Tree
->List
.NonPaged
, Node
);
143 ExFreeToPagedLookasideList(&Tree
->List
.Paged
, Node
);
149 * Replaces a child node of a node with a new node.
150 * The lock for the tree must be acquired when this routine is called.
153 ExpBinaryTreeReplaceChildNode(PBINARY_TREE_NODE Child
,
154 PBINARY_TREE_NODE NewChild
)
156 if (ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Child
)) == Child
)
158 ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Child
)) = NewChild
;
162 ExpBinaryTreeRightChildNode(ExpBinaryTreeParentNode(Child
)) = NewChild
;
168 * Returns the sibling node of a node.
169 * The lock for the tree must be acquired when this routine is called.
171 inline PBINARY_TREE_NODE
172 ExpSiblingBinaryTreeNode(PBINARY_TREE Tree
,
173 PBINARY_TREE_NODE Node
)
175 if (Node
== ExpBinaryTreeRootNode(Tree
))
181 if (ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Node
)) == Node
)
183 return ExpBinaryTreeRightChildNode(ExpBinaryTreeParentNode(Node
));
187 return ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Node
));
194 * Expands an external node to an internal node.
195 * The lock for the tree must be acquired when this routine is called.
198 ExpExpandExternalBinaryTreeNode(PBINARY_TREE Tree
,
199 PBINARY_TREE_NODE Node
)
201 ExpBinaryTreeLeftChildNode(Node
) = ExpCreateBinaryTreeNode(Tree
, Node
, NULL
);
203 if (!ExpBinaryTreeLeftChildNode(Node
))
205 /* FIXME: Throw exception */
206 DbgPrint("ExpCreateBinaryTreeNode() failed\n");
209 ExpBinaryTreeRightChildNode(Node
) = ExpCreateBinaryTreeNode(Tree
, Node
, NULL
);
211 if (!ExpBinaryTreeRightChildNode(Node
))
213 ExpDestroyBinaryTreeNode(Tree
, ExpBinaryTreeLeftChildNode(Node
));
214 /* FIXME: Throw exception */
215 DbgPrint("ExpCreateBinaryTreeNode() failed\n");
221 * Searches a tree for a node with the specified key. If a node with the
222 * specified key is not found, the external node where it should be is
224 * The lock for the tree must be acquired when this routine is called.
226 inline PBINARY_TREE_NODE
227 ExpSearchBinaryTree(PBINARY_TREE Tree
,
229 PBINARY_TREE_NODE Node
)
233 /* FIXME: Possibly do this iteratively due to the small kernel-mode stack */
235 if (ExpBinaryTreeIsExternalNode(Node
))
240 Equality
= (*Tree
->Compare
)(Key
, ExpBinaryTreeNodeKey(Node
));
242 if (ExpBinaryTreeNodeEqual(Equality
))
247 if (ExpBinaryTreeNodeLess(Equality
))
249 return ExpSearchBinaryTree(Tree
, Key
, ExpBinaryTreeLeftChildNode(Node
));
252 /* if (ExpBinaryTreeNodeMore(Equality)) */
254 return ExpSearchBinaryTree(Tree
, Key
, ExpBinaryTreeRightChildNode(Node
));
260 * Removes an external node and it's parent node from the tree.
261 * The lock for the tree must be acquired when this routine is called.
264 ExpRemoveAboveExternalBinaryTreeNode(PBINARY_TREE Tree
,
265 PBINARY_TREE_NODE Node
)
267 assertmsg(ExpBinaryTreeIsExternalNode(Node
), ("Node is not external"));
269 if (Node
== ExpBinaryTreeRootNode(Tree
))
275 PBINARY_TREE_NODE GrandParent
;
276 PBINARY_TREE_NODE NewChild
;
278 GrandParent
= ExpBinaryTreeParentNode(ExpBinaryTreeParentNode(Node
));
279 NewChild
= ExpSiblingBinaryTreeNode(Tree
, Node
);
281 if (GrandParent
!= NULL
)
283 ExpBinaryTreeReplaceChildNode(ExpBinaryTreeParentNode(Node
), NewChild
);
286 ExpDestroyBinaryTreeNode(Tree
, ExpBinaryTreeParentNode(Node
));
287 ExpDestroyBinaryTreeNode(Tree
, Node
);
293 * Release resources used by nodes of a binary (sub)tree.
296 ExpDeleteBinaryTree(PBINARY_TREE Tree
,
297 PBINARY_TREE_NODE Node
)
299 /* FIXME: Possibly do this iteratively due to the small kernel-mode stack */
301 if (ExpBinaryTreeIsInternalNode(Node
))
303 ExpDeleteBinaryTree(Tree
, ExpBinaryTreeLeftChildNode(Node
));
304 ExpDeleteBinaryTree(Tree
, ExpBinaryTreeRightChildNode(Node
));
307 ExpDestroyBinaryTreeNode(Tree
, Node
);
312 * Traverse a binary tree using preorder traversal method.
313 * Returns FALSE, if the traversal was terminated prematurely or
314 * TRUE if the callback routine did not request that the traversal
315 * be terminated prematurely.
316 * The lock for the tree must be acquired when this routine is called.
319 ExpTraverseBinaryTreePreorder(PTRAVERSE_CONTEXT Context
,
320 PBINARY_TREE_NODE Node
)
322 if (ExpBinaryTreeIsInternalNode(Node
))
324 /* Call the traversal routine */
325 if (!(*Context
->Routine
)(Context
->Context
,
326 ExpBinaryTreeNodeKey(Node
),
327 ExpBinaryTreeNodeValue(Node
)))
332 /* Traverse left subtree */
333 ExpTraverseBinaryTreePreorder(Context
, ExpBinaryTreeLeftChildNode(Node
));
335 /* Traverse right subtree */
336 ExpTraverseBinaryTreePreorder(Context
, ExpBinaryTreeRightChildNode(Node
));
344 * Traverse a binary tree using inorder traversal method.
345 * Returns FALSE, if the traversal was terminated prematurely or
346 * TRUE if the callback routine did not request that the traversal
347 * be terminated prematurely.
348 * The lock for the tree must be acquired when this routine is called.
351 ExpTraverseBinaryTreeInorder(PTRAVERSE_CONTEXT Context
,
352 PBINARY_TREE_NODE Node
)
354 if (ExpBinaryTreeIsInternalNode(Node
))
356 /* Traverse left subtree */
357 ExpTraverseBinaryTreeInorder(Context
, ExpBinaryTreeLeftChildNode(Node
));
359 /* Call the traversal routine */
360 if (!(*Context
->Routine
)(Context
->Context
,
361 ExpBinaryTreeNodeKey(Node
),
362 ExpBinaryTreeNodeValue(Node
)))
367 /* Traverse right subtree */
368 ExpTraverseBinaryTreeInorder(Context
, ExpBinaryTreeRightChildNode(Node
));
376 * Traverse a binary tree using postorder traversal method.
377 * Returns FALSE, if the traversal was terminated prematurely or
378 * TRUE if the callback routine did not request that the traversal
379 * be terminated prematurely.
380 * The lock for the tree must be acquired when this routine is called.
383 ExpTraverseBinaryTreePostorder(PTRAVERSE_CONTEXT Context
,
384 PBINARY_TREE_NODE Node
)
386 if (ExpBinaryTreeIsInternalNode(Node
))
388 /* Traverse left subtree */
389 ExpTraverseBinaryTreePostorder(Context
, ExpBinaryTreeLeftChildNode(Node
));
391 /* Traverse right subtree */
392 ExpTraverseBinaryTreePostorder(Context
, ExpBinaryTreeRightChildNode(Node
));
394 /* Call the traversal routine */
395 return (*Context
->Routine
)(Context
->Context
,
396 ExpBinaryTreeNodeKey(Node
),
397 ExpBinaryTreeNodeValue(Node
));
405 * Default key compare function. Compares the integer values of the two keys.
408 ExpBinaryTreeDefaultCompare(PVOID Key1
,
414 return (((LONG_PTR
) Key1
< (LONG_PTR
) Key2
) ? -1 : 1);
419 * Initializes a binary tree.
422 ExInitializeBinaryTree(IN PBINARY_TREE Tree
,
423 IN PKEY_COMPARATOR Compare
,
424 IN BOOLEAN UseNonPagedPool
)
426 RtlZeroMemory(Tree
, sizeof(BINARY_TREE
));
428 Tree
->Compare
= (Compare
== NULL
)
429 ? ExpBinaryTreeDefaultCompare
: Compare
;
431 Tree
->UseNonPagedPool
= UseNonPagedPool
;
435 ExInitializeNPagedLookasideList(
436 &Tree
->List
.NonPaged
, /* Lookaside list */
437 NULL
, /* Allocate routine */
438 NULL
, /* Free routine */
440 sizeof(BINARY_TREE_NODE
), /* Size of each entry */
441 TAG('E','X','B','T'), /* Tag */
444 KeInitializeSpinLock(&Tree
->Lock
.NonPaged
);
448 ExInitializePagedLookasideList(
449 &Tree
->List
.Paged
, /* Lookaside list */
450 NULL
, /* Allocate routine */
451 NULL
, /* Free routine */
453 sizeof(BINARY_TREE_NODE
), /* Size of each entry */
454 TAG('E','X','B','T'), /* Tag */
457 ExInitializeFastMutex(&Tree
->Lock
.Paged
);
460 ExpBinaryTreeRootNode(Tree
) = ExpCreateBinaryTreeNode(Tree
, NULL
, NULL
);
462 if (ExpBinaryTreeRootNode(Tree
) == NULL
)
466 ExDeleteNPagedLookasideList(&Tree
->List
.NonPaged
);
470 ExDeletePagedLookasideList(&Tree
->List
.Paged
);
482 * Release resources used by a binary tree.
485 ExDeleteBinaryTree(IN PBINARY_TREE Tree
)
487 /* Remove all nodes */
488 ExpDeleteBinaryTree(Tree
, ExpBinaryTreeRootNode(Tree
));
490 if (Tree
->UseNonPagedPool
)
492 ExDeleteNPagedLookasideList(&Tree
->List
.NonPaged
);
496 ExDeletePagedLookasideList(&Tree
->List
.Paged
);
502 * Insert a value in a binary tree.
505 ExInsertBinaryTree(IN PBINARY_TREE Tree
,
509 PBINARY_TREE_NODE Node
;
512 /* FIXME: Use SEH for error reporting */
514 ExpLockBinaryTree(Tree
, &OldIrql
);
515 Node
= ExpBinaryTreeRootNode(Tree
);
518 Node
= ExpSearchBinaryTree(Tree
, Key
, Node
);
520 if (ExpBinaryTreeIsExternalNode(Node
))
526 Node
= ExpBinaryTreeRightChildNode(Node
);
529 ExpExpandExternalBinaryTreeNode(Tree
, Node
);
530 ExpBinaryTreeNodeKey(Node
) = Key
;
531 ExpBinaryTreeNodeValue(Node
) = Value
;
532 ExpUnlockBinaryTree(Tree
, &OldIrql
);
537 * Search for a value associated with a given key in a binary tree.
540 ExSearchBinaryTree(IN PBINARY_TREE Tree
,
544 PBINARY_TREE_NODE Node
;
547 ExpLockBinaryTree(Tree
, &OldIrql
);
548 Node
= ExpSearchBinaryTree(Tree
, Key
, ExpBinaryTreeRootNode(Tree
));
550 if (ExpBinaryTreeIsInternalNode(Node
))
552 *Value
= ExpBinaryTreeNodeValue(Node
);
553 ExpUnlockBinaryTree(Tree
, &OldIrql
);
558 ExpUnlockBinaryTree(Tree
, &OldIrql
);
565 * Remove a value associated with a given key from a binary tree.
568 ExRemoveBinaryTree(IN PBINARY_TREE Tree
,
572 PBINARY_TREE_NODE Node
;
575 ExpLockBinaryTree(Tree
, &OldIrql
);
577 Node
= ExpSearchBinaryTree(Tree
, Key
, ExpBinaryTreeRootNode(Tree
));
579 if (ExpBinaryTreeIsExternalNode(Node
))
581 ExpUnlockBinaryTree(Tree
, &OldIrql
);
586 *Value
= ExpBinaryTreeNodeValue(Node
);
587 if (ExpBinaryTreeIsExternalNode(ExpBinaryTreeLeftChildNode(Node
)))
589 Node
= ExpBinaryTreeLeftChildNode(Node
);
591 else if (ExpBinaryTreeIsExternalNode(ExpBinaryTreeRightChildNode(Node
)))
593 Node
= ExpBinaryTreeRightChildNode(Node
);
597 // Node has internal children
598 PBINARY_TREE_NODE SwapNode
;
601 Node
= ExpBinaryTreeRightChildNode(SwapNode
);
604 Node
= ExpBinaryTreeLeftChildNode(Node
);
605 } while (ExpBinaryTreeIsInternalNode(Node
));
608 ExpRemoveAboveExternalBinaryTreeNode(Tree
, Node
);
609 ExpUnlockBinaryTree(Tree
, &OldIrql
);
616 * Traverse a binary tree using either preorder, inorder or postorder
618 * Returns FALSE, if the traversal was terminated prematurely or
619 * TRUE if the callback routine did not request that the traversal
620 * be terminated prematurely.
623 ExTraverseBinaryTree(IN PBINARY_TREE Tree
,
624 IN TRAVERSE_METHOD Method
,
625 IN PTRAVERSE_ROUTINE Routine
,
632 tc
.Routine
= Routine
;
633 tc
.Context
= Context
;
635 ExpLockBinaryTree(Tree
, &OldIrql
);
639 case TraverseMethodPreorder
:
640 Status
= ExpTraverseBinaryTreePreorder(&tc
, ExpBinaryTreeRootNode(Tree
));
643 case TraverseMethodInorder
:
644 Status
= ExpTraverseBinaryTreeInorder(&tc
, ExpBinaryTreeRootNode(Tree
));
647 case TraverseMethodPostorder
:
648 Status
= ExpTraverseBinaryTreePostorder(&tc
, ExpBinaryTreeRootNode(Tree
));
656 ExpUnlockBinaryTree(Tree
, &OldIrql
);