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
27 #include <ddk/ntddk.h>
28 #include <internal/ex.h>
31 #include <internal/debug.h>
33 /* DATA **********************************************************************/
35 typedef struct _BINARY_TREE_NODE
37 struct _BINARY_TREE_NODE * Parent;
38 struct _BINARY_TREE_NODE * LeftChild;
39 struct _BINARY_TREE_NODE * RightChild;
42 } BINARY_TREE_NODE, *PBINARY_TREE_NODE;
44 typedef struct _TRAVERSE_CONTEXT {
45 PTRAVERSE_ROUTINE Routine;
47 } TRAVERSE_CONTEXT, *PTRAVERSE_CONTEXT;
49 /* FUNCTIONS ****************************************************************/
51 #define ExpBinaryTreeRootNode(Tree)(((PBINARY_TREE) (Tree))->RootNode)
52 #define ExpBinaryTreeIsExternalNode(Node)(((Node)->LeftChild == NULL) && ((Node)->RightChild == NULL))
53 #define ExpBinaryTreeIsInternalNode(Node)(!ExpBinaryTreeIsExternalNode(Node))
54 #define ExpBinaryTreeNodeKey(Node)((Node)->Key)
55 #define ExpBinaryTreeNodeValue(Node)((Node)->Value)
56 #define ExpBinaryTreeParentNode(Node)((Node)->Parent)
57 #define ExpBinaryTreeLeftChildNode(Node)((Node)->LeftChild)
58 #define ExpBinaryTreeRightChildNode(Node)((Node)->RightChild)
59 #define ExpBinaryTreeNodeEqual(Equality)((Equality) == 0)
60 #define ExpBinaryTreeNodeLess(Equality)((Equality) < 0)
61 #define ExpBinaryTreeNodeMore(Equality)((Equality) > 0)
65 * Lock the binary tree
68 ExpLockBinaryTree(PBINARY_TREE Tree,
71 if (Tree->UseNonPagedPool)
73 KeAcquireSpinLock(&Tree->Lock.NonPaged, OldIrql);
77 ExAcquireFastMutex(&Tree->Lock.Paged);
83 * Unlock the binary tree
86 ExpUnlockBinaryTree(PBINARY_TREE Tree,
89 if (Tree->UseNonPagedPool)
91 KeReleaseSpinLock(&Tree->Lock.NonPaged, *OldIrql);
95 ExReleaseFastMutex(&Tree->Lock.Paged);
101 * Allocate resources for a new node and initialize it.
103 inline PBINARY_TREE_NODE
104 ExpCreateBinaryTreeNode(PBINARY_TREE Tree,
105 PBINARY_TREE_NODE Parent,
108 PBINARY_TREE_NODE Node;
110 if (Tree->UseNonPagedPool)
112 Node = (PBINARY_TREE_NODE) ExAllocateFromNPagedLookasideList(&Tree->List.NonPaged);
116 Node = (PBINARY_TREE_NODE) ExAllocateFromPagedLookasideList(&Tree->List.Paged);
121 ExpBinaryTreeParentNode(Node) = Parent;
122 ExpBinaryTreeLeftChildNode(Node) = NULL;
123 ExpBinaryTreeRightChildNode(Node) = NULL;
124 ExpBinaryTreeNodeValue(Node) = Value;
132 * Release resources for the node.
135 ExpDestroyBinaryTreeNode(PBINARY_TREE Tree,
136 PBINARY_TREE_NODE Node)
138 if (Tree->UseNonPagedPool)
140 ExFreeToNPagedLookasideList(&Tree->List.NonPaged, Node);
144 ExFreeToPagedLookasideList(&Tree->List.Paged, Node);
150 * Replaces a child node of a node with a new node.
151 * The lock for the tree must be acquired when this routine is called.
154 ExpBinaryTreeReplaceChildNode(PBINARY_TREE_NODE Child,
155 PBINARY_TREE_NODE NewChild)
157 if (ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Child)) == Child)
159 ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Child)) = NewChild;
163 ExpBinaryTreeRightChildNode(ExpBinaryTreeParentNode(Child)) = NewChild;
169 * Returns the sibling node of a node.
170 * The lock for the tree must be acquired when this routine is called.
172 inline PBINARY_TREE_NODE
173 ExpSiblingBinaryTreeNode(PBINARY_TREE Tree,
174 PBINARY_TREE_NODE Node)
176 if (Node == ExpBinaryTreeRootNode(Tree))
182 if (ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Node)) == Node)
184 return ExpBinaryTreeRightChildNode(ExpBinaryTreeParentNode(Node));
188 return ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Node));
195 * Expands an external node to an internal node.
196 * The lock for the tree must be acquired when this routine is called.
199 ExpExpandExternalBinaryTreeNode(PBINARY_TREE Tree,
200 PBINARY_TREE_NODE Node)
202 ExpBinaryTreeLeftChildNode(Node) = ExpCreateBinaryTreeNode(Tree, Node, NULL);
204 if (!ExpBinaryTreeLeftChildNode(Node))
206 /* FIXME: Throw exception */
207 DbgPrint("ExpCreateBinaryTreeNode() failed\n");
210 ExpBinaryTreeRightChildNode(Node) = ExpCreateBinaryTreeNode(Tree, Node, NULL);
212 if (!ExpBinaryTreeRightChildNode(Node))
214 ExpDestroyBinaryTreeNode(Tree, ExpBinaryTreeLeftChildNode(Node));
215 /* FIXME: Throw exception */
216 DbgPrint("ExpCreateBinaryTreeNode() failed\n");
222 * Searches a tree for a node with the specified key. If a node with the
223 * specified key is not found, the external node where it should be is
225 * The lock for the tree must be acquired when this routine is called.
227 inline PBINARY_TREE_NODE
228 ExpSearchBinaryTree(PBINARY_TREE Tree,
230 PBINARY_TREE_NODE Node)
234 /* FIXME: Possibly do this iteratively due to the small kernel-mode stack */
236 if (ExpBinaryTreeIsExternalNode(Node))
241 Equality = (*Tree->Compare)(Key, ExpBinaryTreeNodeKey(Node));
243 if (ExpBinaryTreeNodeEqual(Equality))
248 if (ExpBinaryTreeNodeLess(Equality))
250 return ExpSearchBinaryTree(Tree, Key, ExpBinaryTreeLeftChildNode(Node));
253 /* if (ExpBinaryTreeNodeMore(Equality)) */
255 return ExpSearchBinaryTree(Tree, Key, ExpBinaryTreeRightChildNode(Node));
261 * Removes an external node and it's parent node from the tree.
262 * The lock for the tree must be acquired when this routine is called.
265 ExpRemoveAboveExternalBinaryTreeNode(PBINARY_TREE Tree,
266 PBINARY_TREE_NODE Node)
268 assertmsg(ExpBinaryTreeIsExternalNode(Node), ("Node is not external"));
270 if (Node == ExpBinaryTreeRootNode(Tree))
276 PBINARY_TREE_NODE GrandParent;
277 PBINARY_TREE_NODE NewChild;
279 GrandParent = ExpBinaryTreeParentNode(ExpBinaryTreeParentNode(Node));
280 NewChild = ExpSiblingBinaryTreeNode(Tree, Node);
282 if (GrandParent != NULL)
284 ExpBinaryTreeReplaceChildNode(ExpBinaryTreeParentNode(Node), NewChild);
287 ExpDestroyBinaryTreeNode(Tree, ExpBinaryTreeParentNode(Node));
288 ExpDestroyBinaryTreeNode(Tree, Node);
294 * Release resources used by nodes of a binary (sub)tree.
297 ExpDeleteBinaryTree(PBINARY_TREE Tree,
298 PBINARY_TREE_NODE Node)
300 /* FIXME: Possibly do this iteratively due to the small kernel-mode stack */
302 if (ExpBinaryTreeIsInternalNode(Node))
304 ExpDeleteBinaryTree(Tree, ExpBinaryTreeLeftChildNode(Node));
305 ExpDeleteBinaryTree(Tree, ExpBinaryTreeRightChildNode(Node));
308 ExpDestroyBinaryTreeNode(Tree, Node);
313 * Traverse a binary tree using preorder traversal method.
314 * Returns FALSE, if the traversal was terminated prematurely or
315 * TRUE if the callback routine did not request that the traversal
316 * be terminated prematurely.
317 * The lock for the tree must be acquired when this routine is called.
320 ExpTraverseBinaryTreePreorder(PTRAVERSE_CONTEXT Context,
321 PBINARY_TREE_NODE Node)
323 if (ExpBinaryTreeIsInternalNode(Node))
325 /* Call the traversal routine */
326 if (!(*Context->Routine)(Context->Context,
327 ExpBinaryTreeNodeKey(Node),
328 ExpBinaryTreeNodeValue(Node)))
333 /* Traverse left subtree */
334 ExpTraverseBinaryTreePreorder(Context, ExpBinaryTreeLeftChildNode(Node));
336 /* Traverse right subtree */
337 ExpTraverseBinaryTreePreorder(Context, ExpBinaryTreeRightChildNode(Node));
345 * Traverse a binary tree using inorder traversal method.
346 * Returns FALSE, if the traversal was terminated prematurely or
347 * TRUE if the callback routine did not request that the traversal
348 * be terminated prematurely.
349 * The lock for the tree must be acquired when this routine is called.
352 ExpTraverseBinaryTreeInorder(PTRAVERSE_CONTEXT Context,
353 PBINARY_TREE_NODE Node)
355 if (ExpBinaryTreeIsInternalNode(Node))
357 /* Traverse left subtree */
358 ExpTraverseBinaryTreeInorder(Context, ExpBinaryTreeLeftChildNode(Node));
360 /* Call the traversal routine */
361 if (!(*Context->Routine)(Context->Context,
362 ExpBinaryTreeNodeKey(Node),
363 ExpBinaryTreeNodeValue(Node)))
368 /* Traverse right subtree */
369 ExpTraverseBinaryTreeInorder(Context, ExpBinaryTreeRightChildNode(Node));
377 * Traverse a binary tree using postorder traversal method.
378 * Returns FALSE, if the traversal was terminated prematurely or
379 * TRUE if the callback routine did not request that the traversal
380 * be terminated prematurely.
381 * The lock for the tree must be acquired when this routine is called.
384 ExpTraverseBinaryTreePostorder(PTRAVERSE_CONTEXT Context,
385 PBINARY_TREE_NODE Node)
387 if (ExpBinaryTreeIsInternalNode(Node))
389 /* Traverse left subtree */
390 ExpTraverseBinaryTreePostorder(Context, ExpBinaryTreeLeftChildNode(Node));
392 /* Traverse right subtree */
393 ExpTraverseBinaryTreePostorder(Context, ExpBinaryTreeRightChildNode(Node));
395 /* Call the traversal routine */
396 return (*Context->Routine)(Context->Context,
397 ExpBinaryTreeNodeKey(Node),
398 ExpBinaryTreeNodeValue(Node));
406 * Default key compare function. Compares the integer values of the two keys.
409 ExpBinaryTreeDefaultCompare(PVOID Key1,
415 return (((LONG_PTR) Key1 < (LONG_PTR) Key2) ? -1 : 1);
420 * Initializes a binary tree.
423 ExInitializeBinaryTree(IN PBINARY_TREE Tree,
424 IN PKEY_COMPARATOR Compare,
425 IN BOOLEAN UseNonPagedPool)
427 RtlZeroMemory(Tree, sizeof(BINARY_TREE));
429 Tree->Compare = (Compare == NULL)
430 ? ExpBinaryTreeDefaultCompare : Compare;
432 Tree->UseNonPagedPool = UseNonPagedPool;
436 ExInitializeNPagedLookasideList(
437 &Tree->List.NonPaged, /* Lookaside list */
438 NULL, /* Allocate routine */
439 NULL, /* Free routine */
441 sizeof(BINARY_TREE_NODE), /* Size of each entry */
442 TAG('E','X','B','T'), /* Tag */
445 KeInitializeSpinLock(&Tree->Lock.NonPaged);
449 ExInitializePagedLookasideList(
450 &Tree->List.Paged, /* Lookaside list */
451 NULL, /* Allocate routine */
452 NULL, /* Free routine */
454 sizeof(BINARY_TREE_NODE), /* Size of each entry */
455 TAG('E','X','B','T'), /* Tag */
458 ExInitializeFastMutex(&Tree->Lock.Paged);
461 ExpBinaryTreeRootNode(Tree) = ExpCreateBinaryTreeNode(Tree, NULL, NULL);
463 if (ExpBinaryTreeRootNode(Tree) == NULL)
467 ExDeleteNPagedLookasideList(&Tree->List.NonPaged);
471 ExDeletePagedLookasideList(&Tree->List.Paged);
483 * Release resources used by a binary tree.
486 ExDeleteBinaryTree(IN PBINARY_TREE Tree)
488 /* Remove all nodes */
489 ExpDeleteBinaryTree(Tree, ExpBinaryTreeRootNode(Tree));
491 if (Tree->UseNonPagedPool)
493 ExDeleteNPagedLookasideList(&Tree->List.NonPaged);
497 ExDeletePagedLookasideList(&Tree->List.Paged);
503 * Insert a value in a binary tree.
506 ExInsertBinaryTree(IN PBINARY_TREE Tree,
510 PBINARY_TREE_NODE Node;
513 /* FIXME: Use SEH for error reporting */
515 ExpLockBinaryTree(Tree, &OldIrql);
516 Node = ExpBinaryTreeRootNode(Tree);
519 Node = ExpSearchBinaryTree(Tree, Key, Node);
521 if (ExpBinaryTreeIsExternalNode(Node))
527 Node = ExpBinaryTreeRightChildNode(Node);
530 ExpExpandExternalBinaryTreeNode(Tree, Node);
531 ExpBinaryTreeNodeKey(Node) = Key;
532 ExpBinaryTreeNodeValue(Node) = Value;
533 ExpUnlockBinaryTree(Tree, &OldIrql);
538 * Search for a value associated with a given key in a binary tree.
541 ExSearchBinaryTree(IN PBINARY_TREE Tree,
545 PBINARY_TREE_NODE Node;
548 ExpLockBinaryTree(Tree, &OldIrql);
549 Node = ExpSearchBinaryTree(Tree, Key, ExpBinaryTreeRootNode(Tree));
551 if (ExpBinaryTreeIsInternalNode(Node))
553 *Value = ExpBinaryTreeNodeValue(Node);
554 ExpUnlockBinaryTree(Tree, &OldIrql);
559 ExpUnlockBinaryTree(Tree, &OldIrql);
566 * Remove a value associated with a given key from a binary tree.
569 ExRemoveBinaryTree(IN PBINARY_TREE Tree,
573 PBINARY_TREE_NODE Node;
576 ExpLockBinaryTree(Tree, &OldIrql);
578 Node = ExpSearchBinaryTree(Tree, Key, ExpBinaryTreeRootNode(Tree));
580 if (ExpBinaryTreeIsExternalNode(Node))
582 ExpUnlockBinaryTree(Tree, &OldIrql);
587 *Value = ExpBinaryTreeNodeValue(Node);
588 if (ExpBinaryTreeIsExternalNode(ExpBinaryTreeLeftChildNode(Node)))
590 Node = ExpBinaryTreeLeftChildNode(Node);
592 else if (ExpBinaryTreeIsExternalNode(ExpBinaryTreeRightChildNode(Node)))
594 Node = ExpBinaryTreeRightChildNode(Node);
598 // Node has internal children
599 PBINARY_TREE_NODE SwapNode;
602 Node = ExpBinaryTreeRightChildNode(SwapNode);
605 Node = ExpBinaryTreeLeftChildNode(Node);
606 } while (ExpBinaryTreeIsInternalNode(Node));
609 ExpRemoveAboveExternalBinaryTreeNode(Tree, Node);
610 ExpUnlockBinaryTree(Tree, &OldIrql);
617 * Traverse a binary tree using either preorder, inorder or postorder
619 * Returns FALSE, if the traversal was terminated prematurely or
620 * TRUE if the callback routine did not request that the traversal
621 * be terminated prematurely.
624 ExTraverseBinaryTree(IN PBINARY_TREE Tree,
625 IN TRAVERSE_METHOD Method,
626 IN PTRAVERSE_ROUTINE Routine,
633 tc.Routine = Routine;
634 tc.Context = Context;
636 ExpLockBinaryTree(Tree, &OldIrql);
640 case TraverseMethodPreorder:
641 Status = ExpTraverseBinaryTreePreorder(&tc, ExpBinaryTreeRootNode(Tree));
644 case TraverseMethodInorder:
645 Status = ExpTraverseBinaryTreeInorder(&tc, ExpBinaryTreeRootNode(Tree));
648 case TraverseMethodPostorder:
649 Status = ExpTraverseBinaryTreePostorder(&tc, ExpBinaryTreeRootNode(Tree));
657 ExpUnlockBinaryTree(Tree, &OldIrql);