Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenavlsupp.c
Go to the documentation of this file.
00001 /* 00002 * PROJECT: ReactOS Runtime Library 00003 * LICENSE: BSD - See COPYING.ARM in the top level directory 00004 * FILE: lib/rtl/avlsupp.c 00005 * PURPOSE: AVL Tree Internal Support Routines/Main Algorithms 00006 * PROGRAMMERS: ReactOS Portable Systems Group 00007 */ 00008 00009 /* INCLUDES ******************************************************************/ 00010 00011 /* Internal header for table entries */ 00012 typedef struct _TABLE_ENTRY_HEADER 00013 { 00014 RTL_BALANCED_LINKS BalancedLinks; 00015 LONGLONG UserData; 00016 } TABLE_ENTRY_HEADER, *PTABLE_ENTRY_HEADER; 00017 00018 typedef enum _RTL_AVL_BALANCE_FACTOR 00019 { 00020 RtlUnbalancedAvlTree = -2, 00021 RtlLeftHeavyAvlTree, 00022 RtlBalancedAvlTree, 00023 RtlRightHeavyAvlTree, 00024 } RTL_AVL_BALANCE_FACTOR; 00025 00026 C_ASSERT(RtlBalancedAvlTree == 0); 00027 00028 /* FUNCTIONS ******************************************************************/ 00029 00030 TABLE_SEARCH_RESULT 00031 FORCEINLINE 00032 RtlpFindAvlTableNodeOrParent(IN PRTL_AVL_TABLE Table, 00033 IN PVOID Buffer, 00034 OUT PRTL_BALANCED_LINKS *NodeOrParent) 00035 { 00036 PRTL_BALANCED_LINKS CurrentNode, ChildNode; 00037 RTL_GENERIC_COMPARE_RESULTS Result; 00038 00039 /* Quick check to see if the table is empty */ 00040 if (!Table->NumberGenericTableElements) return TableEmptyTree; 00041 00042 /* Set the current node */ 00043 CurrentNode = RtlRightChildAvl(&Table->BalancedRoot); 00044 00045 /* Start compare loop */ 00046 while (TRUE) 00047 { 00048 /* Compare which side is greater */ 00049 Result = RtlpAvlCompareRoutine(Table, 00050 Buffer, 00051 &((PTABLE_ENTRY_HEADER)CurrentNode)-> 00052 UserData); 00053 if (Result == GenericLessThan) 00054 { 00055 /* We're less, check if this is the left child */ 00056 ChildNode = RtlLeftChildAvl(CurrentNode); 00057 if (ChildNode) 00058 { 00059 /* Continue searching from this node */ 00060 CurrentNode = ChildNode; 00061 } 00062 else 00063 { 00064 /* Otherwise, the element isn't in this tree */ 00065 *NodeOrParent = CurrentNode; 00066 return TableInsertAsLeft; 00067 } 00068 } 00069 else if (Result == GenericGreaterThan) 00070 { 00071 /* We're more, check if this is the right child */ 00072 ChildNode = RtlRightChildAvl(CurrentNode); 00073 if (ChildNode) 00074 { 00075 /* Continue searching from this node */ 00076 CurrentNode = ChildNode; 00077 } 00078 else 00079 { 00080 /* Otherwise, the element isn't in this tree */ 00081 *NodeOrParent = CurrentNode; 00082 return TableInsertAsRight; 00083 } 00084 } 00085 else 00086 { 00087 /* We should've found the node */ 00088 ASSERT(Result == GenericEqual); 00089 00090 /* Return node found */ 00091 *NodeOrParent = CurrentNode; 00092 return TableFoundNode; 00093 } 00094 } 00095 } 00096 00097 VOID 00098 FORCEINLINE 00099 RtlPromoteAvlTreeNode(IN PRTL_BALANCED_LINKS Node) 00100 { 00101 PRTL_BALANCED_LINKS ParentNode, SuperParentNode; 00102 PRTL_BALANCED_LINKS *SwapNode1, *SwapNode2; 00103 00104 /* Grab parents up to 2 levels high */ 00105 ParentNode = RtlParentAvl(Node); 00106 SuperParentNode = RtlParentAvl(ParentNode); 00107 00108 /* Pick which nodes will be rotated */ 00109 SwapNode1 = RtlIsLeftChildAvl(Node) ? &ParentNode->LeftChild : &ParentNode->RightChild; 00110 SwapNode2 = RtlIsLeftChildAvl(Node) ? &Node->RightChild : &Node->LeftChild; 00111 00112 /* Do the rotate, and update the parent and super-parent as needed */ 00113 *SwapNode1 = *SwapNode2; 00114 if (*SwapNode1) RtlSetParent(*SwapNode1, ParentNode); 00115 *SwapNode2 = ParentNode; 00116 RtlSetParent(ParentNode, Node); 00117 00118 /* Now update the super-parent child link, and make it parent of the node*/ 00119 SwapNode1 = (RtlLeftChildAvl(SuperParentNode) == ParentNode) ? 00120 &SuperParentNode->LeftChild: &SuperParentNode->RightChild; 00121 *SwapNode1 = Node; 00122 RtlSetParent(Node, SuperParentNode); 00123 } 00124 00125 BOOLEAN 00126 FORCEINLINE 00127 RtlpRebalanceAvlTreeNode(IN PRTL_BALANCED_LINKS Node) 00128 { 00129 PRTL_BALANCED_LINKS ChildNode, SubChildNode; 00130 CHAR Balance; 00131 ASSERT(RtlParentAvl(Node) != Node); 00132 00133 /* Get the balance, and figure out which child node to go down on */ 00134 Balance = RtlBalance(Node); 00135 ChildNode = (Balance == RtlRightHeavyAvlTree) ? 00136 RtlRightChildAvl(Node) : RtlLeftChildAvl(Node); 00137 00138 /* The child and node have the same balance, promote the child upwards */ 00139 if (RtlBalance(ChildNode) == Balance) 00140 { 00141 /* This performs the rotation described in Knuth A8-A10 for Case 1 */ 00142 RtlPromoteAvlTreeNode(ChildNode); 00143 00144 /* The nodes are now balanced */ 00145 RtlSetBalance(ChildNode, RtlBalancedAvlTree); 00146 RtlSetBalance(Node, RtlBalancedAvlTree); 00147 return FALSE; 00148 } 00149 00150 /* The child has the opposite balance, a double promotion of the child's child must happen */ 00151 if (RtlBalance(ChildNode) == -Balance) 00152 { 00153 /* Pick which sub-child to use based on the balance */ 00154 SubChildNode = (Balance == RtlRightHeavyAvlTree) ? 00155 RtlLeftChildAvl(ChildNode) : RtlRightChildAvl(ChildNode); 00156 00157 /* Do the double-rotation described in Knuth A8-A10 for Case 2 */ 00158 RtlPromoteAvlTreeNode(SubChildNode); 00159 RtlPromoteAvlTreeNode(SubChildNode); 00160 00161 /* Was the sub-child sharing the same balance as the node? */ 00162 if (RtlBalance(SubChildNode) == Balance) 00163 { 00164 /* Then the subchild is now balanced, and the node's weight is inversed */ 00165 RtlSetBalance(ChildNode, RtlBalancedAvlTree); 00166 RtlSetBalance(Node, -Balance); 00167 } 00168 else if (RtlBalance(SubChildNode) == -Balance) 00169 { 00170 /* 00171 * In this case, the sub-child weight was the inverse of the node, so 00172 * the child now shares the node's balance original weight, while the 00173 * node becomes balanced. 00174 */ 00175 RtlSetBalance(ChildNode, Balance); 00176 RtlSetBalance(Node, RtlBalancedAvlTree); 00177 } 00178 else 00179 { 00180 /* 00181 * Otherwise, the sub-child was unbalanced, so both the child and node 00182 * now become balanced. 00183 */ 00184 RtlSetBalance(ChildNode, RtlBalancedAvlTree); 00185 RtlSetBalance(Node, RtlBalancedAvlTree); 00186 } 00187 00188 /* In all cases, the sub-child is now balanced */ 00189 RtlSetBalance(SubChildNode, RtlBalancedAvlTree); 00190 return FALSE; 00191 } 00192 00193 /* 00194 * The case that remains is that the child was already balanced, so this is 00195 * This is the rotation required for Case 3 in Knuth A8-A10 00196 */ 00197 RtlPromoteAvlTreeNode(ChildNode); 00198 00199 /* Now the child has the opposite weight of the node */ 00200 RtlSetBalance(ChildNode, -Balance); 00201 00202 /* This only happens on deletion, so we return TRUE to terminate the delete */ 00203 return TRUE; 00204 } 00205 00206 VOID 00207 FORCEINLINE 00208 RtlpInsertAvlTreeNode(IN PRTL_AVL_TABLE Table, 00209 IN PRTL_BALANCED_LINKS NewNode, 00210 IN OUT PVOID NodeOrParent, 00211 IN OUT TABLE_SEARCH_RESULT SearchResult) 00212 { 00213 CHAR Balance; 00214 00215 /* Initialize the new inserted element */ 00216 MI_ASSERT(SearchResult != TableFoundNode); 00217 NewNode->LeftChild = NewNode->RightChild = NULL; 00218 RtlSetBalance(NewNode, RtlBalancedAvlTree); 00219 00220 /* Increase element count */ 00221 Table->NumberGenericTableElements++; 00222 00223 /* Check where we should insert the entry */ 00224 if (SearchResult == TableEmptyTree) 00225 { 00226 /* This is the new root node */ 00227 RtlInsertAsRightChildAvl(&Table->BalancedRoot, NewNode); 00228 00229 /* On AVL trees, we also update the depth */ 00230 ASSERT(Table->DepthOfTree == 0); 00231 Table->DepthOfTree = 1; 00232 return; 00233 } 00234 else if (SearchResult == TableInsertAsLeft) 00235 { 00236 /* Insert it left */ 00237 RtlInsertAsLeftChildAvl(NodeOrParent, NewNode); 00238 } 00239 else 00240 { 00241 /* Right node */ 00242 RtlInsertAsRightChildAvl(NodeOrParent, NewNode); 00243 } 00244 00245 /* Little cheat to save on loop processing, taken from Timo */ 00246 RtlSetBalance(&Table->BalancedRoot, RtlLeftHeavyAvlTree); 00247 00248 /* 00249 * This implements A6-A7 from Knuth based on http://coding.derkeiler.com 00250 * /pdf/Archive/C_CPP/comp.lang.c/2004-01/1812.pdf, however the algorithm 00251 * is slightly modified to follow the tree based on the Parent Node such 00252 * as the Windows algorithm does it, instead of following the nodes down. 00253 */ 00254 while (TRUE) 00255 { 00256 /* Calculate which side to balance on */ 00257 Balance = RtlIsLeftChildAvl(NewNode) ? RtlLeftHeavyAvlTree : RtlRightHeavyAvlTree; 00258 00259 /* Check if the parent node was balanced */ 00260 if (RtlBalance(NodeOrParent) == RtlBalancedAvlTree) 00261 { 00262 /* It's not balanced anymore (heavy on one side) */ 00263 RtlSetBalance(NodeOrParent, Balance); 00264 00265 /* Move up */ 00266 NewNode = NodeOrParent; 00267 NodeOrParent = RtlParentAvl(NodeOrParent); 00268 } 00269 else if (RtlBalance(NodeOrParent) != Balance) 00270 { 00271 /* The parent's balance is opposite, so the tree is balanced now */ 00272 RtlSetBalance(NodeOrParent, RtlBalancedAvlTree); 00273 00274 /* Check if this is the root (the cheat applied earlier gets us here) */ 00275 if (RtlBalance(&Table->BalancedRoot) == RtlBalancedAvlTree) 00276 { 00277 /* The depth has thus increased */ 00278 Table->DepthOfTree++; 00279 } 00280 00281 /* We reached the root or a balanced node, so we're done */ 00282 break; 00283 } 00284 else 00285 { 00286 /* The tree is now unbalanced, so AVL rebalancing must happen */ 00287 RtlpRebalanceAvlTreeNode(NodeOrParent); 00288 break; 00289 } 00290 } 00291 } 00292 00293 VOID 00294 FORCEINLINE 00295 RtlpDeleteAvlTreeNode(IN PRTL_AVL_TABLE Table, 00296 IN PRTL_BALANCED_LINKS Node) 00297 { 00298 PRTL_BALANCED_LINKS DeleteNode = NULL, ParentNode; 00299 PRTL_BALANCED_LINKS *Node1, *Node2; 00300 CHAR Balance; 00301 00302 /* Take one of the children if possible */ 00303 if (!(RtlLeftChildAvl(Node)) || !(RtlRightChildAvl(Node))) DeleteNode = Node; 00304 00305 /* Otherwise, check if one side is longer */ 00306 if (!(DeleteNode) && (RtlBalance(Node) >= RtlBalancedAvlTree)) 00307 { 00308 /* Pick the successor which will be the longest side in this case */ 00309 DeleteNode = RtlRightChildAvl(Node); 00310 while (RtlLeftChildAvl(DeleteNode)) DeleteNode = RtlLeftChildAvl(DeleteNode); 00311 } 00312 else if (!DeleteNode) 00313 { 00314 /* Pick the predecessor which will be the longest side in this case */ 00315 DeleteNode = RtlLeftChildAvl(Node); 00316 while (RtlRightChildAvl(DeleteNode)) DeleteNode = RtlRightChildAvl(DeleteNode); 00317 } 00318 00319 /* Get the parent node */ 00320 ParentNode = RtlParentAvl(DeleteNode); 00321 DPRINT("Parent: %p\n", ParentNode); 00322 00323 /* Pick which now to use based on whether or not we have a left child */ 00324 Node1 = RtlLeftChildAvl(DeleteNode) ? &DeleteNode->LeftChild : &DeleteNode->RightChild; 00325 DPRINT("Node 1: %p %p\n", Node1, *Node1); 00326 00327 /* Pick which node to swap based on if we're already a left child or not */ 00328 Node2 = RtlIsLeftChildAvl(DeleteNode) ? &ParentNode->LeftChild : &ParentNode->RightChild; 00329 DPRINT("Node 2: %p %p\n", Node2, *Node2); 00330 00331 /* Pick the correct balance depending on which side will get heavier */ 00332 Balance = RtlIsLeftChildAvl(DeleteNode) ? RtlLeftHeavyAvlTree : RtlRightHeavyAvlTree; 00333 DPRINT("Balance: %lx\n", Balance); 00334 00335 /* Swap the children nodes, making one side heavier */ 00336 *Node2 = *Node1; 00337 00338 /* If the node has a child now, update its parent */ 00339 if (*Node1) RtlSetParent(*Node1, ParentNode); 00340 00341 /* Assume balanced root for loop optimization */ 00342 RtlSetBalance(&Table->BalancedRoot, RtlBalancedAvlTree); 00343 00344 /* Loop up the tree by parents */ 00345 while (TRUE) 00346 { 00347 /* Check if the tree's balance increased */ 00348 if (RtlBalance(ParentNode) == Balance) 00349 { 00350 /* Now the tree is balanced */ 00351 RtlSetBalance(ParentNode, RtlBalancedAvlTree); 00352 } 00353 else if (RtlBalance(ParentNode) == RtlBalancedAvlTree) 00354 { 00355 /* The tree has now become less balanced, since it was balanced */ 00356 RtlSetBalance(ParentNode, -Balance); 00357 00358 /* Deal with the loop optimization to detect loss of a tree level */ 00359 if (RtlBalance(&Table->BalancedRoot) != RtlBalancedAvlTree) Table->DepthOfTree--; 00360 break; 00361 } 00362 else 00363 { 00364 /* The tree has become unbalanced, so a rebalance is needed */ 00365 if (RtlpRebalanceAvlTreeNode(ParentNode)) break; 00366 00367 /* Get the new parent after the balance */ 00368 ParentNode = RtlParentAvl(ParentNode); 00369 } 00370 00371 /* Choose which balance factor to use based on which side we're on */ 00372 Balance = RtlIsRightChild(ParentNode) ? 00373 RtlRightHeavyAvlTree : RtlLeftHeavyAvlTree; 00374 00375 /* Iterate up the tree */ 00376 ParentNode = RtlParentAvl(ParentNode); 00377 } 00378 00379 /* Check if this isn't the node we ended up deleting directly */ 00380 if (Node == DeleteNode) return; 00381 00382 /* Copy the deleted node itself */ 00383 RtlpCopyAvlNodeData(DeleteNode, Node); 00384 00385 /* Pick the right node to unlink */ 00386 Node1 = RtlIsLeftChildAvl(Node) ? 00387 &(RtlParentAvl(DeleteNode))->LeftChild : &(RtlParentAvl(DeleteNode))->RightChild; 00388 *Node1 = DeleteNode; 00389 00390 /* Reparent as appropriate */ 00391 if (RtlLeftChildAvl(DeleteNode)) RtlSetParent(RtlLeftChildAvl(DeleteNode), DeleteNode); 00392 if (RtlRightChildAvl(DeleteNode)) RtlSetParent(RtlRightChildAvl(DeleteNode), DeleteNode); 00393 } 00394 00395 /* EOF */ Generated on Sun May 27 2012 04:36:21 for ReactOS by
1.7.6.1
|