/* * ReactOS kernel * Copyright (C) 1998-2002 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * PROJECT: ReactOS kernel * FILE: btree.c * PURPOSE: Binary tree support * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net) * UPDATE HISTORY: * 15-03-2002 CSH Created */ #include #include #define NDEBUG #include /* DATA **********************************************************************/ typedef struct _BINARY_TREE_NODE { struct _BINARY_TREE_NODE * Parent; struct _BINARY_TREE_NODE * LeftChild; struct _BINARY_TREE_NODE * RightChild; PVOID Key; PVOID Value; } BINARY_TREE_NODE, *PBINARY_TREE_NODE; typedef struct _TRAVERSE_CONTEXT { PTRAVERSE_ROUTINE Routine; PVOID Context; } TRAVERSE_CONTEXT, *PTRAVERSE_CONTEXT; /* FUNCTIONS ****************************************************************/ #define ExpBinaryTreeRootNode(Tree)(((PBINARY_TREE) (Tree))->RootNode) #define ExpBinaryTreeIsExternalNode(Node)(((Node)->LeftChild == NULL) && ((Node)->RightChild == NULL)) #define ExpBinaryTreeIsInternalNode(Node)(!ExpBinaryTreeIsExternalNode(Node)) #define ExpBinaryTreeNodeKey(Node)((Node)->Key) #define ExpBinaryTreeNodeValue(Node)((Node)->Value) #define ExpBinaryTreeParentNode(Node)((Node)->Parent) #define ExpBinaryTreeLeftChildNode(Node)((Node)->LeftChild) #define ExpBinaryTreeRightChildNode(Node)((Node)->RightChild) #define ExpBinaryTreeNodeEqual(Equality)((Equality) == 0) #define ExpBinaryTreeNodeLess(Equality)((Equality) < 0) #define ExpBinaryTreeNodeMore(Equality)((Equality) > 0) /* * Lock the binary tree */ inline VOID ExpLockBinaryTree(PBINARY_TREE Tree, PKIRQL OldIrql) { if (Tree->UseNonPagedPool) { KeAcquireSpinLock(&Tree->Lock.NonPaged, OldIrql); } else { ExAcquireFastMutex(&Tree->Lock.Paged); } } /* * Unlock the binary tree */ inline VOID ExpUnlockBinaryTree(PBINARY_TREE Tree, PKIRQL OldIrql) { if (Tree->UseNonPagedPool) { KeReleaseSpinLock(&Tree->Lock.NonPaged, *OldIrql); } else { ExReleaseFastMutex(&Tree->Lock.Paged); } } /* * Allocate resources for a new node and initialize it. */ inline PBINARY_TREE_NODE ExpCreateBinaryTreeNode(PBINARY_TREE Tree, PBINARY_TREE_NODE Parent, PVOID Value) { PBINARY_TREE_NODE Node; if (Tree->UseNonPagedPool) { Node = (PBINARY_TREE_NODE) ExAllocateFromNPagedLookasideList(&Tree->List.NonPaged); } else { Node = (PBINARY_TREE_NODE) ExAllocateFromPagedLookasideList(&Tree->List.Paged); } if (Node) { ExpBinaryTreeParentNode(Node) = Parent; ExpBinaryTreeLeftChildNode(Node) = NULL; ExpBinaryTreeRightChildNode(Node) = NULL; ExpBinaryTreeNodeValue(Node) = Value; } return Node; } /* * Release resources for the node. */ inline VOID ExpDestroyBinaryTreeNode(PBINARY_TREE Tree, PBINARY_TREE_NODE Node) { if (Tree->UseNonPagedPool) { ExFreeToNPagedLookasideList(&Tree->List.NonPaged, Node); } else { ExFreeToPagedLookasideList(&Tree->List.Paged, Node); } } /* * Replaces a child node of a node with a new node. * The lock for the tree must be acquired when this routine is called. */ inline VOID ExpBinaryTreeReplaceChildNode(PBINARY_TREE_NODE Child, PBINARY_TREE_NODE NewChild) { if (ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Child)) == Child) { ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Child)) = NewChild; } else { ExpBinaryTreeRightChildNode(ExpBinaryTreeParentNode(Child)) = NewChild; } } /* * Returns the sibling node of a node. * The lock for the tree must be acquired when this routine is called. */ inline PBINARY_TREE_NODE ExpSiblingBinaryTreeNode(PBINARY_TREE Tree, PBINARY_TREE_NODE Node) { if (Node == ExpBinaryTreeRootNode(Tree)) { return NULL; } else { if (ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Node)) == Node) { return ExpBinaryTreeRightChildNode(ExpBinaryTreeParentNode(Node)); } else { return ExpBinaryTreeLeftChildNode(ExpBinaryTreeParentNode(Node)); } } } /* * Expands an external node to an internal node. * The lock for the tree must be acquired when this routine is called. */ VOID ExpExpandExternalBinaryTreeNode(PBINARY_TREE Tree, PBINARY_TREE_NODE Node) { ExpBinaryTreeLeftChildNode(Node) = ExpCreateBinaryTreeNode(Tree, Node, NULL); if (!ExpBinaryTreeLeftChildNode(Node)) { /* FIXME: Throw exception */ DbgPrint("ExpCreateBinaryTreeNode() failed\n"); } ExpBinaryTreeRightChildNode(Node) = ExpCreateBinaryTreeNode(Tree, Node, NULL); if (!ExpBinaryTreeRightChildNode(Node)) { ExpDestroyBinaryTreeNode(Tree, ExpBinaryTreeLeftChildNode(Node)); /* FIXME: Throw exception */ DbgPrint("ExpCreateBinaryTreeNode() failed\n"); } } /* * Searches a tree for a node with the specified key. If a node with the * specified key is not found, the external node where it should be is * returned. * The lock for the tree must be acquired when this routine is called. */ inline PBINARY_TREE_NODE ExpSearchBinaryTree(PBINARY_TREE Tree, PVOID Key, PBINARY_TREE_NODE Node) { LONG Equality; /* FIXME: Possibly do this iteratively due to the small kernel-mode stack */ if (ExpBinaryTreeIsExternalNode(Node)) { return Node; } Equality = (*Tree->Compare)(Key, ExpBinaryTreeNodeKey(Node)); if (ExpBinaryTreeNodeEqual(Equality)) { return Node; } if (ExpBinaryTreeNodeLess(Equality)) { return ExpSearchBinaryTree(Tree, Key, ExpBinaryTreeLeftChildNode(Node)); } /* if (ExpBinaryTreeNodeMore(Equality)) */ { return ExpSearchBinaryTree(Tree, Key, ExpBinaryTreeRightChildNode(Node)); } } /* * Removes an external node and it's parent node from the tree. * The lock for the tree must be acquired when this routine is called. */ VOID ExpRemoveAboveExternalBinaryTreeNode(PBINARY_TREE Tree, PBINARY_TREE_NODE Node) { assertmsg(ExpBinaryTreeIsExternalNode(Node), ("Node is not external")); if (Node == ExpBinaryTreeRootNode(Tree)) { return; } else { PBINARY_TREE_NODE GrandParent; PBINARY_TREE_NODE NewChild; GrandParent = ExpBinaryTreeParentNode(ExpBinaryTreeParentNode(Node)); NewChild = ExpSiblingBinaryTreeNode(Tree, Node); if (GrandParent != NULL) { ExpBinaryTreeReplaceChildNode(ExpBinaryTreeParentNode(Node), NewChild); } ExpDestroyBinaryTreeNode(Tree, ExpBinaryTreeParentNode(Node)); ExpDestroyBinaryTreeNode(Tree, Node); } } /* * Release resources used by nodes of a binary (sub)tree. */ VOID ExpDeleteBinaryTree(PBINARY_TREE Tree, PBINARY_TREE_NODE Node) { /* FIXME: Possibly do this iteratively due to the small kernel-mode stack */ if (ExpBinaryTreeIsInternalNode(Node)) { ExpDeleteBinaryTree(Tree, ExpBinaryTreeLeftChildNode(Node)); ExpDeleteBinaryTree(Tree, ExpBinaryTreeRightChildNode(Node)); } ExpDestroyBinaryTreeNode(Tree, Node); } /* * Traverse a binary tree using preorder traversal method. * Returns FALSE, if the traversal was terminated prematurely or * TRUE if the callback routine did not request that the traversal * be terminated prematurely. * The lock for the tree must be acquired when this routine is called. */ BOOLEAN ExpTraverseBinaryTreePreorder(PTRAVERSE_CONTEXT Context, PBINARY_TREE_NODE Node) { if (ExpBinaryTreeIsInternalNode(Node)) { /* Call the traversal routine */ if (!(*Context->Routine)(Context->Context, ExpBinaryTreeNodeKey(Node), ExpBinaryTreeNodeValue(Node))) { return FALSE; } /* Traverse left subtree */ ExpTraverseBinaryTreePreorder(Context, ExpBinaryTreeLeftChildNode(Node)); /* Traverse right subtree */ ExpTraverseBinaryTreePreorder(Context, ExpBinaryTreeRightChildNode(Node)); } return TRUE; } /* * Traverse a binary tree using inorder traversal method. * Returns FALSE, if the traversal was terminated prematurely or * TRUE if the callback routine did not request that the traversal * be terminated prematurely. * The lock for the tree must be acquired when this routine is called. */ BOOLEAN ExpTraverseBinaryTreeInorder(PTRAVERSE_CONTEXT Context, PBINARY_TREE_NODE Node) { if (ExpBinaryTreeIsInternalNode(Node)) { /* Traverse left subtree */ ExpTraverseBinaryTreeInorder(Context, ExpBinaryTreeLeftChildNode(Node)); /* Call the traversal routine */ if (!(*Context->Routine)(Context->Context, ExpBinaryTreeNodeKey(Node), ExpBinaryTreeNodeValue(Node))) { return FALSE; } /* Traverse right subtree */ ExpTraverseBinaryTreeInorder(Context, ExpBinaryTreeRightChildNode(Node)); } return TRUE; } /* * Traverse a binary tree using postorder traversal method. * Returns FALSE, if the traversal was terminated prematurely or * TRUE if the callback routine did not request that the traversal * be terminated prematurely. * The lock for the tree must be acquired when this routine is called. */ BOOLEAN ExpTraverseBinaryTreePostorder(PTRAVERSE_CONTEXT Context, PBINARY_TREE_NODE Node) { if (ExpBinaryTreeIsInternalNode(Node)) { /* Traverse left subtree */ ExpTraverseBinaryTreePostorder(Context, ExpBinaryTreeLeftChildNode(Node)); /* Traverse right subtree */ ExpTraverseBinaryTreePostorder(Context, ExpBinaryTreeRightChildNode(Node)); /* Call the traversal routine */ return (*Context->Routine)(Context->Context, ExpBinaryTreeNodeKey(Node), ExpBinaryTreeNodeValue(Node)); } return TRUE; } /* * Default key compare function. Compares the integer values of the two keys. */ LONG STDCALL ExpBinaryTreeDefaultCompare(PVOID Key1, PVOID Key2) { if (Key1 == Key2) return 0; return (((LONG_PTR) Key1 < (LONG_PTR) Key2) ? -1 : 1); } /* * Initializes a binary tree. */ BOOLEAN STDCALL ExInitializeBinaryTree(IN PBINARY_TREE Tree, IN PKEY_COMPARATOR Compare, IN BOOLEAN UseNonPagedPool) { RtlZeroMemory(Tree, sizeof(BINARY_TREE)); Tree->Compare = (Compare == NULL) ? ExpBinaryTreeDefaultCompare : Compare; Tree->UseNonPagedPool = UseNonPagedPool; if (UseNonPagedPool) { ExInitializeNPagedLookasideList( &Tree->List.NonPaged, /* Lookaside list */ NULL, /* Allocate routine */ NULL, /* Free routine */ 0, /* Flags */ sizeof(BINARY_TREE_NODE), /* Size of each entry */ TAG('E','X','B','T'), /* Tag */ 0); /* Depth */ KeInitializeSpinLock(&Tree->Lock.NonPaged); } else { ExInitializePagedLookasideList( &Tree->List.Paged, /* Lookaside list */ NULL, /* Allocate routine */ NULL, /* Free routine */ 0, /* Flags */ sizeof(BINARY_TREE_NODE), /* Size of each entry */ TAG('E','X','B','T'), /* Tag */ 0); /* Depth */ ExInitializeFastMutex(&Tree->Lock.Paged); } ExpBinaryTreeRootNode(Tree) = ExpCreateBinaryTreeNode(Tree, NULL, NULL); if (ExpBinaryTreeRootNode(Tree) == NULL) { if (UseNonPagedPool) { ExDeleteNPagedLookasideList(&Tree->List.NonPaged); } else { ExDeletePagedLookasideList(&Tree->List.Paged); } return FALSE; } else { return TRUE; } } /* * Release resources used by a binary tree. */ VOID STDCALL ExDeleteBinaryTree(IN PBINARY_TREE Tree) { /* Remove all nodes */ ExpDeleteBinaryTree(Tree, ExpBinaryTreeRootNode(Tree)); if (Tree->UseNonPagedPool) { ExDeleteNPagedLookasideList(&Tree->List.NonPaged); } else { ExDeletePagedLookasideList(&Tree->List.Paged); } } /* * Insert a value in a binary tree. */ VOID STDCALL ExInsertBinaryTree(IN PBINARY_TREE Tree, IN PVOID Key, IN PVOID Value) { PBINARY_TREE_NODE Node; KIRQL OldIrql; /* FIXME: Use SEH for error reporting */ ExpLockBinaryTree(Tree, &OldIrql); Node = ExpBinaryTreeRootNode(Tree); do { Node = ExpSearchBinaryTree(Tree, Key, Node); if (ExpBinaryTreeIsExternalNode(Node)) { break; } else { Node = ExpBinaryTreeRightChildNode(Node); } } while (TRUE); ExpExpandExternalBinaryTreeNode(Tree, Node); ExpBinaryTreeNodeKey(Node) = Key; ExpBinaryTreeNodeValue(Node) = Value; ExpUnlockBinaryTree(Tree, &OldIrql); } /* * Search for a value associated with a given key in a binary tree. */ BOOLEAN STDCALL ExSearchBinaryTree(IN PBINARY_TREE Tree, IN PVOID Key, OUT PVOID * Value) { PBINARY_TREE_NODE Node; KIRQL OldIrql; ExpLockBinaryTree(Tree, &OldIrql); Node = ExpSearchBinaryTree(Tree, Key, ExpBinaryTreeRootNode(Tree)); if (ExpBinaryTreeIsInternalNode(Node)) { *Value = ExpBinaryTreeNodeValue(Node); ExpUnlockBinaryTree(Tree, &OldIrql); return TRUE; } else { ExpUnlockBinaryTree(Tree, &OldIrql); return FALSE; } } /* * Remove a value associated with a given key from a binary tree. */ BOOLEAN STDCALL ExRemoveBinaryTree(IN PBINARY_TREE Tree, IN PVOID Key, IN PVOID * Value) { PBINARY_TREE_NODE Node; KIRQL OldIrql; ExpLockBinaryTree(Tree, &OldIrql); Node = ExpSearchBinaryTree(Tree, Key, ExpBinaryTreeRootNode(Tree)); if (ExpBinaryTreeIsExternalNode(Node)) { ExpUnlockBinaryTree(Tree, &OldIrql); return FALSE; } else { *Value = ExpBinaryTreeNodeValue(Node); if (ExpBinaryTreeIsExternalNode(ExpBinaryTreeLeftChildNode(Node))) { Node = ExpBinaryTreeLeftChildNode(Node); } else if (ExpBinaryTreeIsExternalNode(ExpBinaryTreeRightChildNode(Node))) { Node = ExpBinaryTreeRightChildNode(Node); } else { // Node has internal children PBINARY_TREE_NODE SwapNode; SwapNode = Node; Node = ExpBinaryTreeRightChildNode(SwapNode); do { Node = ExpBinaryTreeLeftChildNode(Node); } while (ExpBinaryTreeIsInternalNode(Node)); } ExpRemoveAboveExternalBinaryTreeNode(Tree, Node); ExpUnlockBinaryTree(Tree, &OldIrql); return TRUE; } } /* * Traverse a binary tree using either preorder, inorder or postorder * traversal method. * Returns FALSE, if the traversal was terminated prematurely or * TRUE if the callback routine did not request that the traversal * be terminated prematurely. */ BOOLEAN STDCALL ExTraverseBinaryTree(IN PBINARY_TREE Tree, IN TRAVERSE_METHOD Method, IN PTRAVERSE_ROUTINE Routine, IN PVOID Context) { TRAVERSE_CONTEXT tc; BOOLEAN Status; KIRQL OldIrql; tc.Routine = Routine; tc.Context = Context; ExpLockBinaryTree(Tree, &OldIrql); switch (Method) { case TraverseMethodPreorder: Status = ExpTraverseBinaryTreePreorder(&tc, ExpBinaryTreeRootNode(Tree)); break; case TraverseMethodInorder: Status = ExpTraverseBinaryTreeInorder(&tc, ExpBinaryTreeRootNode(Tree)); break; case TraverseMethodPostorder: Status = ExpTraverseBinaryTreePostorder(&tc, ExpBinaryTreeRootNode(Tree)); break; default: Status = FALSE; break; } ExpUnlockBinaryTree(Tree, &OldIrql); return Status; } /* EOF */