Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2333 lines
70 KiB

  1. /*++
  2. Copyright (c) 1990, 1999 Microsoft Corporation
  3. Module Name:
  4. AvlTable.c
  5. Abstract:
  6. This module implements a new version of the generic table package based on balanced
  7. binary trees (later named AVL), as described in Knuth, "The Art of Computer Programming,
  8. Volume 3, Sorting and Searching", and refers directly to algorithms as they are presented
  9. in the second edition Copyrighted in 1973. Whereas gentable.c relys on splay.c for
  10. its tree support, this module is self-contained in that it implements the balanced
  11. binary trees directly.
  12. Author:
  13. Tom Miller [TomM] 17-March-1999
  14. (much of the non-AVL related code in this module is based on gentable.c by GaryKi
  15. and revised by TonyE)
  16. Environment:
  17. Pure Utility Routines
  18. --*/
  19. #include <nt.h>
  20. #include <ntrtl.h>
  21. #pragma pack(8)
  22. //
  23. // The checkit routine or macro may be defined to check occurrences of the link pointers for
  24. // valid pointer values, if structures are being corrupted.
  25. //
  26. #if 0
  27. PVOID
  28. checkit(PVOID p)
  29. {
  30. if (p != NULL) {
  31. ASSERT(!FlagOn((ULONG)p, 3) && FlagOn((ULONG)p, 0x80000000));
  32. }
  33. return p;
  34. }
  35. #else
  36. #define checkit(p) (p)
  37. #endif
  38. //
  39. // Build a table of the best case efficiency of a balanced binary tree, holding the
  40. // most possible nodes that can possibly be held in a binary tree with a given number
  41. // of levels. The answer is always (2**n) - 1.
  42. //
  43. // (Used for debug only.)
  44. //
  45. ULONG BestCaseFill[33] = { 0, 1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f,
  46. 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff, 0x7fff,
  47. 0xffff, 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff, 0x1fffff, 0x3fffff, 0x7fffff,
  48. 0xffffff, 0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
  49. 0xffffffff };
  50. //
  51. // Build a table of the worst case efficiency of a balanced binary tree, holding the
  52. // fewest possible nodes that can possibly be contained in a balanced binary tree with
  53. // the given number of levels. After the first two levels, each level n is obviously
  54. // occupied by a root node, plus one subtree the size of level n-1, and another subtree
  55. // which is the size of n-2, i.e.:
  56. //
  57. // WorstCaseFill[n] = 1 + WorstCaseFill[n-1] + WorstCaseFill[n-2]
  58. //
  59. // The efficiency of a typical balanced binary tree will normally fall between the two
  60. // extremes, typically closer to the best case. Note however that even with the worst
  61. // case, it only takes 32 compares to find an element in a worst case tree populated with
  62. // ~3.5M nodes. Unbalanced trees and splay trees, on the other hand, can and will sometimes
  63. // degenerate to a straight line, requiring on average n/2 compares to find a node.
  64. //
  65. // A specific case (that will frequently occur in TXF), is one where the nodes are inserted
  66. // in collated order. In this case an unbalanced or a splay tree will generate a straight
  67. // line, yet the balanced binary tree will always create a perfectly balanced tree (best-case
  68. // fill) in this situation.
  69. //
  70. // (Used for debug only.)
  71. //
  72. ULONG WorstCaseFill[33] = { 0, 1, 2, 4, 7, 12, 20, 33,
  73. 54, 88, 143, 232, 376, 609, 986, 1596,
  74. 2583, 4180, 6764, 10945, 17710, 28656, 46367, 75024,
  75. 121392, 196417, 317810, 514228, 832039, 1346268, 2178308, 3524577,
  76. 5702886 };
  77. //
  78. // This structure is the header for a generic table entry.
  79. // Align this structure on a 8 byte boundary so the user
  80. // data is correctly aligned.
  81. //
  82. typedef struct _TABLE_ENTRY_HEADER {
  83. RTL_BALANCED_LINKS BalancedLinks;
  84. LONGLONG UserData;
  85. } TABLE_ENTRY_HEADER, *PTABLE_ENTRY_HEADER;
  86. #pragma pack()
  87. //
  88. // The default matching function which matches everything.
  89. //
  90. NTSTATUS
  91. MatchAll (
  92. IN PRTL_AVL_TABLE Table,
  93. IN PVOID P1,
  94. IN PVOID P2
  95. )
  96. {
  97. return STATUS_SUCCESS;
  98. UNREFERENCED_PARAMETER(Table);
  99. UNREFERENCED_PARAMETER(P1);
  100. UNREFERENCED_PARAMETER(P2);
  101. }
  102. TABLE_SEARCH_RESULT
  103. FindNodeOrParent(
  104. IN PRTL_AVL_TABLE Table,
  105. IN PVOID Buffer,
  106. OUT PRTL_BALANCED_LINKS *NodeOrParent
  107. )
  108. /*++
  109. Routine Description:
  110. This routine is used by all of the routines of the generic
  111. table package to locate the a node in the tree. It will
  112. find and return (via the NodeOrParent parameter) the node
  113. with the given key, or if that node is not in the tree it
  114. will return (via the NodeOrParent parameter) a pointer to
  115. the parent.
  116. Arguments:
  117. Table - The generic table to search for the key.
  118. Buffer - Pointer to a buffer holding the key. The table
  119. package doesn't examine the key itself. It leaves
  120. this up to the user supplied compare routine.
  121. NodeOrParent - Will be set to point to the node containing the
  122. the key or what should be the parent of the node
  123. if it were in the tree. Note that this will *NOT*
  124. be set if the search result is TableEmptyTree.
  125. Return Value:
  126. TABLE_SEARCH_RESULT - TableEmptyTree: The tree was empty. NodeOrParent
  127. is *not* altered.
  128. TableFoundNode: A node with the key is in the tree.
  129. NodeOrParent points to that node.
  130. TableInsertAsLeft: Node with key was not found.
  131. NodeOrParent points to what would be
  132. parent. The node would be the left
  133. child.
  134. TableInsertAsRight: Node with key was not found.
  135. NodeOrParent points to what would be
  136. parent. The node would be the right
  137. child.
  138. --*/
  139. {
  140. if (RtlIsGenericTableEmptyAvl(Table)) {
  141. return TableEmptyTree;
  142. } else {
  143. //
  144. // Used as the iteration variable while stepping through
  145. // the generic table.
  146. //
  147. PRTL_BALANCED_LINKS NodeToExamine = Table->BalancedRoot.RightChild;
  148. //
  149. // Just a temporary. Hopefully a good compiler will get
  150. // rid of it.
  151. //
  152. PRTL_BALANCED_LINKS Child;
  153. //
  154. // Holds the value of the comparasion.
  155. //
  156. RTL_GENERIC_COMPARE_RESULTS Result;
  157. ULONG NumberCompares = 0;
  158. while (TRUE) {
  159. //
  160. // Compare the buffer with the key in the tree element.
  161. //
  162. Result = Table->CompareRoutine(
  163. Table,
  164. Buffer,
  165. &((PTABLE_ENTRY_HEADER) NodeToExamine)->UserData
  166. );
  167. //
  168. // Make sure the depth of tree is correct.
  169. //
  170. ASSERT(++NumberCompares <= Table->DepthOfTree);
  171. if (Result == GenericLessThan) {
  172. if (Child = NodeToExamine->LeftChild) {
  173. NodeToExamine = Child;
  174. } else {
  175. //
  176. // Node is not in the tree. Set the output
  177. // parameter to point to what would be its
  178. // parent and return which child it would be.
  179. //
  180. *NodeOrParent = NodeToExamine;
  181. return TableInsertAsLeft;
  182. }
  183. } else if (Result == GenericGreaterThan) {
  184. if (Child = NodeToExamine->RightChild) {
  185. NodeToExamine = Child;
  186. } else {
  187. //
  188. // Node is not in the tree. Set the output
  189. // parameter to point to what would be its
  190. // parent and return which child it would be.
  191. //
  192. *NodeOrParent = NodeToExamine;
  193. return TableInsertAsRight;
  194. }
  195. } else {
  196. //
  197. // Node is in the tree (or it better be because of the
  198. // assert). Set the output parameter to point to
  199. // the node and tell the caller that we found the node.
  200. //
  201. ASSERT(Result == GenericEqual);
  202. *NodeOrParent = NodeToExamine;
  203. return TableFoundNode;
  204. }
  205. }
  206. }
  207. }
  208. VOID
  209. PromoteNode (
  210. IN PRTL_BALANCED_LINKS C
  211. )
  212. /*++
  213. Routine Description:
  214. This routine performs the fundamental adjustment required for balancing
  215. the binary tree during insert and delete operations. Simply put, the designated
  216. node is promoted in such a way that it rises one level in the tree and its parent
  217. drops one level in the tree, becoming now the child of the designated node.
  218. Generally the path length to the subtree "opposite" the original parent. Balancing
  219. occurs as the caller chooses which nodes to promote according to the balanced tree
  220. algorithms from Knuth.
  221. This is not the same as a splay operation, typically a splay "promotes" a designated
  222. node twice.
  223. Note that the pointer to the root node of the tree is assumed to be contained in a
  224. RTL_BALANCED_LINK structure itself, to allow the algorithms below to change the root
  225. of the tree without checking for special cases. Note also that this is an internal
  226. routine, and the caller guarantees that it never requests to promote the root itself.
  227. This routine only updates the tree links; the caller must update the balance factors
  228. as appropriate.
  229. Arguments:
  230. C - pointer to the child node to be promoted in the tree.
  231. Return Value:
  232. None.
  233. --*/
  234. {
  235. PRTL_BALANCED_LINKS P, G;
  236. //
  237. // Capture the current parent and grandparent (may be the root).
  238. //
  239. P = C->Parent;
  240. G = P->Parent;
  241. //
  242. // Break down the promotion into two cases based upon whether C is a left or right child.
  243. //
  244. if (P->LeftChild == C) {
  245. //
  246. // This promotion looks like this:
  247. //
  248. // G G
  249. // | |
  250. // P C
  251. // / \ => / \
  252. // C z x P
  253. // / \ / \
  254. // x y y z
  255. //
  256. P->LeftChild = checkit(C->RightChild);
  257. if (P->LeftChild != NULL) {
  258. P->LeftChild->Parent = checkit(P);
  259. }
  260. C->RightChild = checkit(P);
  261. //
  262. // Fall through to update parent and G <-> C relationship in common code.
  263. //
  264. } else {
  265. ASSERT(P->RightChild == C);
  266. //
  267. // This promotion looks like this:
  268. //
  269. // G G
  270. // | |
  271. // P C
  272. // / \ => / \
  273. // x C P z
  274. // / \ / \
  275. // y z x y
  276. //
  277. P->RightChild = checkit(C->LeftChild);
  278. if (P->RightChild != NULL) {
  279. P->RightChild->Parent = checkit(P);
  280. }
  281. C->LeftChild = checkit(P);
  282. }
  283. //
  284. // Update parent of P, for either case above.
  285. //
  286. P->Parent = checkit(C);
  287. //
  288. // Finally update G <-> C links for either case above.
  289. //
  290. if (G->LeftChild == P) {
  291. G->LeftChild = checkit(C);
  292. } else {
  293. ASSERT(G->RightChild == P);
  294. G->RightChild = checkit(C);
  295. }
  296. C->Parent = checkit(G);
  297. }
  298. ULONG
  299. RebalanceNode (
  300. IN PRTL_BALANCED_LINKS S
  301. )
  302. /*++
  303. Routine Description:
  304. This routine performs a rebalance around the input node S, for which the
  305. Balance factor has just effectively become +2 or -2. When called, the
  306. Balance factor still has a value of +1 or -1, but the respective longer
  307. side has just become one longer as the result of an insert or delete operation.
  308. This routine effectively implements steps A7.iii (test for Case 1 or Case 2) and
  309. steps A8 and A9 of Knuths balanced insertion algorithm, plus it handles Case 3
  310. identified in the delete section, which can only happen on deletes.
  311. The trick is, to convince yourself that while travling from the insertion point
  312. at the bottom of the tree up, that there are only these two cases, and that when
  313. traveling up from the deletion point, that there are just these three cases.
  314. Knuth says it is obvious!
  315. Arguments:
  316. S - pointer to the node which has just become unbalanced.
  317. Return Value:
  318. TRUE if Case 3 was detected (causes delete algorithm to terminate).
  319. --*/
  320. {
  321. PRTL_BALANCED_LINKS R, P;
  322. CHAR a;
  323. //
  324. // Capture which side is unbalanced.
  325. //
  326. a = S->Balance;
  327. if (a == +1) {
  328. R = S->RightChild;
  329. } else {
  330. R = S->LeftChild;
  331. }
  332. //
  333. // If the balance of R and S are the same (Case 1 in Knuth) then a single
  334. // promotion of R will do the single rotation. (Step A8, A10)
  335. //
  336. // Here is a diagram of the Case 1 transformation, for a == +1 (a mirror
  337. // image transformation occurs when a == -1), and where the subtree
  338. // heights are h and h+1 as shown (++ indicates the node out of balance):
  339. //
  340. // | |
  341. // S++ R
  342. // / \ / \
  343. // (h) R+ ==> S (h+1)
  344. // / \ / \
  345. // (h) (h+1) (h) (h)
  346. //
  347. // Note that on an insert we can hit this case by inserting an item in the
  348. // right subtree of R. The original height of the subtree before the insert
  349. // was h+2, and it is still h+2 after the rebalance, so insert rebalancing may
  350. // terminate.
  351. //
  352. // On a delete we can hit this case by deleting a node from the left subtree
  353. // of S. The height of the subtree before the delete was h+3, and after the
  354. // rebalance it is h+2, so rebalancing must continue up the tree.
  355. //
  356. if (R->Balance == a) {
  357. PromoteNode( R );
  358. R->Balance = 0;
  359. S->Balance = 0;
  360. return FALSE;
  361. //
  362. // Otherwise, we have to promote the appropriate child of R twice (Case 2
  363. // in Knuth). (Step A9, A10)
  364. //
  365. // Here is a diagram of the Case 2 transformation, for a == +1 (a mirror
  366. // image transformation occurs when a == -1), and where the subtree
  367. // heights are h and h-1 as shown. There are actually two minor subcases,
  368. // differing only in the original balance of P (++ indicates the node out
  369. // of balance).
  370. //
  371. // | |
  372. // S++ P
  373. // / \ / \
  374. // / \ / \
  375. // / \ / \
  376. // (h) R- ==> S- R
  377. // / \ / \ / \
  378. // P+ (h) (h)(h-1)(h) (h)
  379. // / \
  380. // (h-1) (h)
  381. //
  382. //
  383. // | |
  384. // S++ P
  385. // / \ / \
  386. // / \ / \
  387. // / \ / \
  388. // (h) R- ==> S R+
  389. // / \ / \ / \
  390. // P- (h) (h) (h)(h-1)(h)
  391. // / \
  392. // (h) (h-1)
  393. //
  394. // Note that on an insert we can hit this case by inserting an item in the
  395. // left subtree of R. The original height of the subtree before the insert
  396. // was h+2, and it is still h+2 after the rebalance, so insert rebalancing may
  397. // terminate.
  398. //
  399. // On a delete we can hit this case by deleting a node from the left subtree
  400. // of S. The height of the subtree before the delete was h+3, and after the
  401. // rebalance it is h+2, so rebalancing must continue up the tree.
  402. //
  403. } else if (R->Balance == -a) {
  404. //
  405. // Pick up the appropriate child P for the double rotation (Link(-a,R)).
  406. //
  407. if (a == 1) {
  408. P = R->LeftChild;
  409. } else {
  410. P = R->RightChild;
  411. }
  412. //
  413. // Promote him twice to implement the double rotation.
  414. //
  415. PromoteNode( P );
  416. PromoteNode( P );
  417. //
  418. // Now adjust the balance factors.
  419. //
  420. S->Balance = 0;
  421. R->Balance = 0;
  422. if (P->Balance == a) {
  423. S->Balance = -a;
  424. } else if (P->Balance == -a) {
  425. R->Balance = a;
  426. }
  427. P->Balance = 0;
  428. return FALSE;
  429. //
  430. // Otherwise this is Case 3 which can only happen on Delete (identical to Case 1 except
  431. // R->Balance == 0). We do a single rotation, adjust the balance factors appropriately,
  432. // and return TRUE. Note that the balance of S stays the same.
  433. //
  434. // Here is a diagram of the Case 3 transformation, for a == +1 (a mirror
  435. // image transformation occurs when a == -1), and where the subtree
  436. // heights are h and h+1 as shown (++ indicates the node out of balance):
  437. //
  438. // | |
  439. // S++ R-
  440. // / \ / \
  441. // (h) R ==> S+ (h+1)
  442. // / \ / \
  443. // (h+1)(h+1) (h) (h+1)
  444. //
  445. // This case can not occur on an insert, because it is impossible for a single insert to
  446. // balance R, yet somehow grow the right subtree of S at the same time. As we move up
  447. // the tree adjusting balance factors after an insert, we terminate the algorithm if a
  448. // node becomes balanced, because that means the subtree length did not change!
  449. //
  450. // On a delete we can hit this case by deleting a node from the left subtree
  451. // of S. The height of the subtree before the delete was h+3, and after the
  452. // rebalance it is still h+3, so rebalancing may terminate in the delete path.
  453. //
  454. } else {
  455. PromoteNode( R );
  456. R->Balance = -a;
  457. return TRUE;
  458. }
  459. }
  460. VOID
  461. DeleteNodeFromTree (
  462. IN PRTL_AVL_TABLE Table,
  463. IN PRTL_BALANCED_LINKS NodeToDelete
  464. )
  465. /*++
  466. Routine Description:
  467. This routine deletes the specified node from the balanced tree, rebalancing
  468. as necessary. If the NodeToDelete has at least one NULL child pointers, then
  469. it is chosen as the EasyDelete, otherwise a subtree predecessor or successor
  470. is found as the EasyDelete. In either case the EasyDelete is deleted
  471. and the tree is rebalanced. Finally if the NodeToDelete was different
  472. than the EasyDelete, then the EasyDelete is linked back into the tree in
  473. place of the NodeToDelete.
  474. Arguments:
  475. Table - The generic table in which the delete is to occur.
  476. NodeToDelete - Pointer to the node which the caller wishes to delete.
  477. Return Value:
  478. None.
  479. --*/
  480. {
  481. PRTL_BALANCED_LINKS EasyDelete;
  482. PRTL_BALANCED_LINKS P;
  483. CHAR a;
  484. //
  485. // If the NodeToDelete has at least one NULL child pointer, then we can
  486. // delete it directly.
  487. //
  488. if ((NodeToDelete->LeftChild == NULL) || (NodeToDelete->RightChild == NULL)) {
  489. EasyDelete = NodeToDelete;
  490. //
  491. // Otherwise, we may as well pick the longest side to delete from (if one is
  492. // is longer), as that reduces the probability that we will have to rebalance.
  493. //
  494. } else if (NodeToDelete->Balance >= 0) {
  495. //
  496. // Pick up the subtree successor.
  497. //
  498. EasyDelete = NodeToDelete->RightChild;
  499. while (EasyDelete->LeftChild != NULL) {
  500. EasyDelete = EasyDelete->LeftChild;
  501. }
  502. } else {
  503. //
  504. // Pick up the subtree predecessor.
  505. //
  506. EasyDelete = NodeToDelete->LeftChild;
  507. while (EasyDelete->RightChild != NULL) {
  508. EasyDelete = EasyDelete->RightChild;
  509. }
  510. }
  511. //
  512. // Rebalancing must know which side of the first parent the delete occurred
  513. // on. Assume it is the left side and otherwise correct below.
  514. //
  515. a = -1;
  516. //
  517. // Now we can do the simple deletion for the no left child case.
  518. //
  519. if (EasyDelete->LeftChild == NULL) {
  520. if (RtlIsLeftChild(EasyDelete)) {
  521. EasyDelete->Parent->LeftChild = checkit(EasyDelete->RightChild);
  522. } else {
  523. EasyDelete->Parent->RightChild = checkit(EasyDelete->RightChild);
  524. a = 1;
  525. }
  526. if (EasyDelete->RightChild != NULL) {
  527. EasyDelete->RightChild->Parent = checkit(EasyDelete->Parent);
  528. }
  529. //
  530. // Now we can do the simple deletion for the no right child case,
  531. // plus we know there is a left child.
  532. //
  533. } else {
  534. if (RtlIsLeftChild(EasyDelete)) {
  535. EasyDelete->Parent->LeftChild = checkit(EasyDelete->LeftChild);
  536. } else {
  537. EasyDelete->Parent->RightChild = checkit(EasyDelete->LeftChild);
  538. a = 1;
  539. }
  540. EasyDelete->LeftChild->Parent = checkit(EasyDelete->Parent);
  541. }
  542. //
  543. // For delete rebalancing, set the balance at the root to 0 to properly
  544. // terminate the rebalance without special tests, and to be able to detect
  545. // if the depth of the tree actually decreased.
  546. //
  547. Table->BalancedRoot.Balance = 0;
  548. P = EasyDelete->Parent;
  549. //
  550. // Loop until the tree is balanced.
  551. //
  552. while (TRUE) {
  553. //
  554. // First handle the case where the tree became more balanced. Zero
  555. // the balance factor, calculate a for the next loop and move on to
  556. // the parent.
  557. //
  558. if (P->Balance == a) {
  559. P->Balance = 0;
  560. //
  561. // If this node is curently balanced, we can show it is now unbalanced
  562. // and terminate the scan since the subtree length has not changed.
  563. // (This may be the root, since we set Balance to 0 above!)
  564. //
  565. } else if (P->Balance == 0) {
  566. P->Balance = -a;
  567. //
  568. // If we shortened the depth all the way back to the root, then the tree really
  569. // has one less level.
  570. //
  571. if (Table->BalancedRoot.Balance != 0) {
  572. Table->DepthOfTree -= 1;
  573. }
  574. break;
  575. //
  576. // Otherwise we made the short side 2 levels less than the long side,
  577. // and rebalancing is required. On return, some node has been promoted
  578. // to above node P. If Case 3 from Knuth was not encountered, then we
  579. // want to effectively resume rebalancing from P's original parent which
  580. // is effectively its grandparent now.
  581. //
  582. } else {
  583. //
  584. // We are done if Case 3 was hit, i.e., the depth of this subtree is
  585. // now the same as before the delete.
  586. //
  587. if (RebalanceNode(P)) {
  588. break;
  589. }
  590. P = P->Parent;
  591. }
  592. a = -1;
  593. if (RtlIsRightChild(P)) {
  594. a = 1;
  595. }
  596. P = P->Parent;
  597. }
  598. //
  599. // Finally, if we actually deleted a predecessor/successor of the NodeToDelete,
  600. // we will link him back into the tree to replace NodeToDelete before returning.
  601. // Note that NodeToDelete did have both child links filled in, but that may no
  602. // longer be the case at this point.
  603. //
  604. if (NodeToDelete != EasyDelete) {
  605. *EasyDelete = *NodeToDelete;
  606. if (RtlIsLeftChild(NodeToDelete)) {
  607. EasyDelete->Parent->LeftChild = checkit(EasyDelete);
  608. } else {
  609. ASSERT(RtlIsRightChild(NodeToDelete));
  610. EasyDelete->Parent->RightChild = checkit(EasyDelete);
  611. }
  612. if (EasyDelete->LeftChild != NULL) {
  613. EasyDelete->LeftChild->Parent = checkit(EasyDelete);
  614. }
  615. if (EasyDelete->RightChild != NULL) {
  616. EasyDelete->RightChild->Parent = checkit(EasyDelete);
  617. }
  618. }
  619. }
  620. PRTL_BALANCED_LINKS
  621. RealSuccessor (
  622. IN PRTL_BALANCED_LINKS Links
  623. )
  624. /*++
  625. Routine Description:
  626. The RealSuccessor function takes as input a pointer to a balanced link
  627. in a tree and returns a pointer to the successor of the input node within
  628. the entire tree. If there is not a successor, the return value is NULL.
  629. Arguments:
  630. Links - Supplies a pointer to a balanced link in a tree.
  631. Return Value:
  632. PRTL_BALANCED_LINKS - returns a pointer to the successor in the entire tree
  633. --*/
  634. {
  635. PRTL_BALANCED_LINKS Ptr;
  636. /*
  637. first check to see if there is a right subtree to the input link
  638. if there is then the real successor is the left most node in
  639. the right subtree. That is find and return S in the following diagram
  640. Links
  641. \
  642. .
  643. .
  644. .
  645. /
  646. S
  647. \
  648. */
  649. if ((Ptr = Links->RightChild) != NULL) {
  650. while (Ptr->LeftChild != NULL) {
  651. Ptr = Ptr->LeftChild;
  652. }
  653. return Ptr;
  654. }
  655. /*
  656. we do not have a right child so check to see if have a parent and if
  657. so find the first ancestor that we are a left decendent of. That
  658. is find and return S in the following diagram
  659. S
  660. /
  661. .
  662. .
  663. .
  664. Links
  665. Note that this code depends on how the BalancedRoot is initialized, which is
  666. Parent points to self, and the RightChild points to an actual node which is
  667. the root of the tree, and LeftChild does not point to self.
  668. */
  669. Ptr = Links;
  670. while (RtlIsRightChild(Ptr)) {
  671. Ptr = Ptr->Parent;
  672. }
  673. if (RtlIsLeftChild(Ptr)) {
  674. return Ptr->Parent;
  675. }
  676. //
  677. // otherwise we are do not have a real successor so we simply return
  678. // NULL.
  679. //
  680. // This can only occur when we get back to the root, and we can tell
  681. // that since the Root is its own parent.
  682. //
  683. ASSERT(Ptr->Parent == Ptr);
  684. return NULL;
  685. }
  686. PRTL_BALANCED_LINKS
  687. RealPredecessor (
  688. IN PRTL_BALANCED_LINKS Links
  689. )
  690. /*++
  691. Routine Description:
  692. The RealPredecessor function takes as input a pointer to a balanced link
  693. in a tree and returns a pointer to the predecessor of the input node
  694. within the entire tree. If there is not a predecessor, the return value
  695. is NULL.
  696. Arguments:
  697. Links - Supplies a pointer to a balanced link in a tree.
  698. Return Value:
  699. PRTL_BALANCED_LINKS - returns a pointer to the predecessor in the entire tree
  700. --*/
  701. {
  702. PRTL_BALANCED_LINKS Ptr;
  703. /*
  704. first check to see if there is a left subtree to the input link
  705. if there is then the real predecessor is the right most node in
  706. the left subtree. That is find and return P in the following diagram
  707. Links
  708. /
  709. .
  710. .
  711. .
  712. P
  713. /
  714. */
  715. if ((Ptr = Links->LeftChild) != NULL) {
  716. while (Ptr->RightChild != NULL) {
  717. Ptr = Ptr->RightChild;
  718. }
  719. return Ptr;
  720. }
  721. /*
  722. we do not have a left child so check to see if have a parent and if
  723. so find the first ancestor that we are a right decendent of. That
  724. is find and return P in the following diagram
  725. P
  726. \
  727. .
  728. .
  729. .
  730. Links
  731. Note that this code depends on how the BalancedRoot is initialized, which is
  732. Parent points to self, and the RightChild points to an actual node which is
  733. the root of the tree.
  734. */
  735. Ptr = Links;
  736. while (RtlIsLeftChild(Ptr)) {
  737. Ptr = Ptr->Parent;
  738. }
  739. if (RtlIsRightChild(Ptr) && (Ptr->Parent->Parent != Ptr->Parent)) {
  740. return Ptr->Parent;
  741. }
  742. //
  743. // otherwise we are do not have a real predecessor so we simply return
  744. // NULL
  745. //
  746. return NULL;
  747. }
  748. VOID
  749. RtlInitializeGenericTableAvl (
  750. IN PRTL_AVL_TABLE Table,
  751. IN PRTL_AVL_COMPARE_ROUTINE CompareRoutine,
  752. IN PRTL_AVL_ALLOCATE_ROUTINE AllocateRoutine,
  753. IN PRTL_AVL_FREE_ROUTINE FreeRoutine,
  754. IN PVOID TableContext
  755. )
  756. /*++
  757. Routine Description:
  758. The procedure InitializeGenericTableAvl takes as input an uninitialized
  759. generic table variable and pointers to the three user supplied routines.
  760. This must be called for every individual generic table variable before
  761. it can be used.
  762. Arguments:
  763. Table - Pointer to the generic table to be initialized.
  764. CompareRoutine - User routine to be used to compare to keys in the
  765. table.
  766. AllocateRoutine - User routine to call to allocate memory for a new
  767. node in the generic table.
  768. FreeRoutine - User routine to call to deallocate memory for
  769. a node in the generic table.
  770. TableContext - Supplies user supplied context for the table.
  771. Return Value:
  772. None.
  773. --*/
  774. {
  775. #ifdef NTFS_FREE_ASSERTS
  776. ULONG i;
  777. for (i=2; i < 33; i++) {
  778. ASSERT(WorstCaseFill[i] == (1 + WorstCaseFill[i-1] + WorstCaseFill[i-2]));
  779. }
  780. #endif
  781. //
  782. // Initialize each field of the Table parameter.
  783. //
  784. RtlZeroMemory( Table, sizeof(RTL_AVL_TABLE) );
  785. Table->BalancedRoot.Parent = &Table->BalancedRoot;
  786. Table->CompareRoutine = CompareRoutine;
  787. Table->AllocateRoutine = AllocateRoutine;
  788. Table->FreeRoutine = FreeRoutine;
  789. Table->TableContext = TableContext;
  790. }
  791. PVOID
  792. RtlInsertElementGenericTableAvl (
  793. IN PRTL_AVL_TABLE Table,
  794. IN PVOID Buffer,
  795. IN CLONG BufferSize,
  796. OUT PBOOLEAN NewElement OPTIONAL
  797. )
  798. /*++
  799. Routine Description:
  800. The function InsertElementGenericTableAvl will insert a new element
  801. in a table. It does this by allocating space for the new element
  802. (this includes splay links), inserting the element in the table, and
  803. then returning to the user a pointer to the new element (which is
  804. the first available space after the splay links). If an element
  805. with the same key already exists in the table the return value is a pointer
  806. to the old element. The optional output parameter NewElement is used
  807. to indicate if the element previously existed in the table. Note: the user
  808. supplied Buffer is only used for searching the table, upon insertion its
  809. contents are copied to the newly created element. This means that
  810. pointer to the input buffer will not point to the new element.
  811. Arguments:
  812. Table - Pointer to the table in which to (possibly) insert the
  813. key buffer.
  814. Buffer - Passed to the user comparasion routine. Its contents are
  815. up to the user but one could imagine that it contains some
  816. sort of key value.
  817. BufferSize - The amount of space to allocate when the (possible)
  818. insertion is made. Note that if we actually do
  819. not find the node and we do allocate space then we
  820. will add the size of the BALANCED_LINKS to this buffer
  821. size. The user should really take care not to depend
  822. on anything in the first sizeof(BALANCED_LINKS) bytes
  823. of the memory allocated via the memory allocation
  824. routine.
  825. NewElement - Optional Flag. If present then it will be set to
  826. TRUE if the buffer was not "found" in the generic
  827. table.
  828. Return Value:
  829. PVOID - Pointer to the user defined data.
  830. --*/
  831. {
  832. //
  833. // Holds a pointer to the node in the table or what would be the
  834. // parent of the node.
  835. //
  836. PRTL_BALANCED_LINKS NodeOrParent;
  837. //
  838. // Holds the result of the table lookup.
  839. //
  840. TABLE_SEARCH_RESULT Lookup;
  841. Lookup = FindNodeOrParent(
  842. Table,
  843. Buffer,
  844. &NodeOrParent
  845. );
  846. //
  847. // Call the full routine to do the real work.
  848. //
  849. return RtlInsertElementGenericTableFullAvl(
  850. Table,
  851. Buffer,
  852. BufferSize,
  853. NewElement,
  854. NodeOrParent,
  855. Lookup
  856. );
  857. }
  858. PVOID
  859. RtlInsertElementGenericTableFullAvl (
  860. IN PRTL_AVL_TABLE Table,
  861. IN PVOID Buffer,
  862. IN CLONG BufferSize,
  863. OUT PBOOLEAN NewElement OPTIONAL,
  864. IN PVOID NodeOrParent,
  865. IN TABLE_SEARCH_RESULT SearchResult
  866. )
  867. /*++
  868. Routine Description:
  869. The function InsertElementGenericTableFullAvl will insert a new element
  870. in a table. It does this by allocating space for the new element
  871. (this includes splay links), inserting the element in the table, and
  872. then returning to the user a pointer to the new element. If an element
  873. with the same key already exists in the table the return value is a pointer
  874. to the old element. The optional output parameter NewElement is used
  875. to indicate if the element previously existed in the table. Note: the user
  876. supplied Buffer is only used for searching the table, upon insertion its
  877. contents are copied to the newly created element. This means that
  878. pointer to the input buffer will not point to the new element.
  879. This routine is passed the NodeOrParent and SearchResult from a
  880. previous RtlLookupElementGenericTableFullAvl.
  881. Arguments:
  882. Table - Pointer to the table in which to (possibly) insert the
  883. key buffer.
  884. Buffer - Passed to the user comparasion routine. Its contents are
  885. up to the user but one could imagine that it contains some
  886. sort of key value.
  887. BufferSize - The amount of space to allocate when the (possible)
  888. insertion is made. Note that if we actually do
  889. not find the node and we do allocate space then we
  890. will add the size of the BALANCED_LINKS to this buffer
  891. size. The user should really take care not to depend
  892. on anything in the first sizeof(BALANCED_LINKS) bytes
  893. of the memory allocated via the memory allocation
  894. routine.
  895. NewElement - Optional Flag. If present then it will be set to
  896. TRUE if the buffer was not "found" in the generic
  897. table.
  898. NodeOrParent - Result of prior RtlLookupElementGenericTableFullAvl.
  899. SearchResult - Result of prior RtlLookupElementGenericTableFullAvl.
  900. Return Value:
  901. PVOID - Pointer to the user defined data.
  902. --*/
  903. {
  904. //
  905. // Node will point to the splay links of what
  906. // will be returned to the user.
  907. //
  908. PTABLE_ENTRY_HEADER NodeToReturn;
  909. if (SearchResult != TableFoundNode) {
  910. //
  911. // We just check that the table isn't getting
  912. // too big.
  913. //
  914. ASSERT(Table->NumberGenericTableElements != (MAXULONG-1));
  915. //
  916. // The node wasn't in the (possibly empty) tree.
  917. // Call the user allocation routine to get space
  918. // for the new node.
  919. //
  920. NodeToReturn = Table->AllocateRoutine(
  921. Table,
  922. BufferSize+FIELD_OFFSET( TABLE_ENTRY_HEADER, UserData )
  923. );
  924. //
  925. // If the return is NULL, return NULL from here to indicate that
  926. // the entry could not be added.
  927. //
  928. if (NodeToReturn == NULL) {
  929. if (ARGUMENT_PRESENT(NewElement)) {
  930. *NewElement = FALSE;
  931. }
  932. return(NULL);
  933. }
  934. RtlZeroMemory( NodeToReturn, sizeof(RTL_BALANCED_LINKS) );
  935. Table->NumberGenericTableElements++;
  936. //
  937. // Insert the new node in the tree.
  938. //
  939. if (SearchResult == TableEmptyTree) {
  940. Table->BalancedRoot.RightChild = &NodeToReturn->BalancedLinks;
  941. NodeToReturn->BalancedLinks.Parent = &Table->BalancedRoot;
  942. ASSERT(Table->DepthOfTree == 0);
  943. Table->DepthOfTree = 1;
  944. } else {
  945. PRTL_BALANCED_LINKS R = &NodeToReturn->BalancedLinks;
  946. PRTL_BALANCED_LINKS S = (PRTL_BALANCED_LINKS)NodeOrParent;
  947. if (SearchResult == TableInsertAsLeft) {
  948. ((PRTL_BALANCED_LINKS)NodeOrParent)->LeftChild = checkit(&NodeToReturn->BalancedLinks);
  949. } else {
  950. ((PRTL_BALANCED_LINKS)NodeOrParent)->RightChild = checkit(&NodeToReturn->BalancedLinks);
  951. }
  952. NodeToReturn->BalancedLinks.Parent = NodeOrParent;
  953. //
  954. // The above completes the standard binary tree insertion, which
  955. // happens to correspond to steps A1-A5 of Knuth's "balanced tree
  956. // search and insertion" algorithm. Now comes the time to adjust
  957. // balance factors and possibly do a single or double rotation as
  958. // in steps A6-A10.
  959. //
  960. // Set the Balance factor in the root to a convenient value
  961. // to simplify loop control.
  962. //
  963. Table->BalancedRoot.Balance = -1;
  964. //
  965. // Now loop to adjust balance factors and see if any balance operations
  966. // must be performed, using NodeOrParent to ascend the tree.
  967. //
  968. while (TRUE) {
  969. CHAR a;
  970. //
  971. // Calculate the next adjustment.
  972. //
  973. a = 1;
  974. if (RtlIsLeftChild(R)) {
  975. a = -1;
  976. }
  977. //
  978. // If this node was balanced, show that it is no longer and keep looping.
  979. // This is essentially A6 of Knuth's algorithm, where he updates all of
  980. // the intermediate nodes on the insertion path which previously had
  981. // balance factors of 0. We are looping up the tree via Parent pointers
  982. // rather than down the tree as in Knuth.
  983. //
  984. if (S->Balance == 0) {
  985. S->Balance = a;
  986. R = S;
  987. S = S->Parent;
  988. //
  989. // If this node has the opposite balance, then the tree got more balanced
  990. // (or we hit the root) and we are done.
  991. //
  992. } else if (S->Balance != a) {
  993. //
  994. // Step A7.ii
  995. //
  996. S->Balance = 0;
  997. //
  998. // If S is actually the root, then this means the depth of the tree
  999. // just increased by 1! (This is essentially A7.i, but we just
  1000. // initialized the root balance to force it through here.)
  1001. //
  1002. if (Table->BalancedRoot.Balance == 0) {
  1003. Table->DepthOfTree += 1;
  1004. }
  1005. break;
  1006. //
  1007. // Otherwise the tree became unbalanced (path length differs by 2 below us)
  1008. // and we need to do one of the balancing operations, and then we are done.
  1009. // The RebalanceNode routine does steps A7.iii, A8 and A9.
  1010. //
  1011. } else {
  1012. RebalanceNode( S );
  1013. break;
  1014. }
  1015. }
  1016. }
  1017. //
  1018. // Copy the users buffer into the user data area of the table.
  1019. //
  1020. RtlCopyMemory( &NodeToReturn->UserData, Buffer, BufferSize );
  1021. } else {
  1022. NodeToReturn = NodeOrParent;
  1023. }
  1024. //
  1025. // Optionally return the NewElement boolean.
  1026. //
  1027. if (ARGUMENT_PRESENT(NewElement)) {
  1028. *NewElement = ((SearchResult == TableFoundNode)?(FALSE):(TRUE));
  1029. }
  1030. //
  1031. // Sanity check tree size and depth.
  1032. //
  1033. ASSERT((Table->NumberGenericTableElements >= WorstCaseFill[Table->DepthOfTree]) &&
  1034. (Table->NumberGenericTableElements <= BestCaseFill[Table->DepthOfTree]));
  1035. //
  1036. // Insert the element on the ordered list;
  1037. //
  1038. return &NodeToReturn->UserData;
  1039. }
  1040. BOOLEAN
  1041. RtlDeleteElementGenericTableAvl (
  1042. IN PRTL_AVL_TABLE Table,
  1043. IN PVOID Buffer
  1044. )
  1045. /*++
  1046. Routine Description:
  1047. The function DeleteElementGenericTableAvl will find and delete an element
  1048. from a generic table. If the element is located and deleted the return
  1049. value is TRUE, otherwise if the element is not located the return value
  1050. is FALSE. The user supplied input buffer is only used as a key in
  1051. locating the element in the table.
  1052. Arguments:
  1053. Table - Pointer to the table in which to (possibly) delete the
  1054. memory accessed by the key buffer.
  1055. Buffer - Passed to the user comparasion routine. Its contents are
  1056. up to the user but one could imagine that it contains some
  1057. sort of key value.
  1058. Return Value:
  1059. BOOLEAN - If the table contained the key then true, otherwise false.
  1060. --*/
  1061. {
  1062. //
  1063. // Holds a pointer to the node in the table or what would be the
  1064. // parent of the node.
  1065. //
  1066. PRTL_BALANCED_LINKS NodeOrParent;
  1067. //
  1068. // Holds the result of the table lookup.
  1069. //
  1070. TABLE_SEARCH_RESULT Lookup;
  1071. Lookup = FindNodeOrParent(
  1072. Table,
  1073. Buffer,
  1074. &NodeOrParent
  1075. );
  1076. if (Lookup != TableFoundNode) {
  1077. return FALSE;
  1078. } else {
  1079. //
  1080. // Make RtlEnumerateGenericTableAvl safe by replacing the RestartKey
  1081. // with its predecessor if it gets deleted. A NULL means return the
  1082. // first node in the tree. (The Splay routines do not always correctly
  1083. // resume from predecessor on delete!)
  1084. //
  1085. if (NodeOrParent == Table->RestartKey) {
  1086. Table->RestartKey = RealPredecessor( NodeOrParent );
  1087. }
  1088. //
  1089. // Make RtlEnumerateGenericTableLikeADirectory safe by incrementing the
  1090. // DeleteCount.
  1091. //
  1092. Table->DeleteCount += 1;
  1093. //
  1094. // Delete the node from the splay tree.
  1095. //
  1096. DeleteNodeFromTree( Table, NodeOrParent );
  1097. Table->NumberGenericTableElements--;
  1098. //
  1099. // On all deletes, reset the ordered pointer to force a recount from 0.
  1100. //
  1101. Table->WhichOrderedElement = 0;
  1102. Table->OrderedPointer = NULL;
  1103. //
  1104. // Sanity check tree size and depth.
  1105. //
  1106. ASSERT((Table->NumberGenericTableElements >= WorstCaseFill[Table->DepthOfTree]) &&
  1107. (Table->NumberGenericTableElements <= BestCaseFill[Table->DepthOfTree]));
  1108. //
  1109. // The node has been deleted from the splay table.
  1110. // Now give the node to the user deletion routine.
  1111. // NOTE: We are giving the deletion routine a pointer
  1112. // to the splay links rather then the user data. It
  1113. // is assumed that the deallocation is rather bad.
  1114. //
  1115. Table->FreeRoutine(Table,NodeOrParent);
  1116. return TRUE;
  1117. }
  1118. }
  1119. PVOID
  1120. RtlLookupElementGenericTableAvl (
  1121. IN PRTL_AVL_TABLE Table,
  1122. IN PVOID Buffer
  1123. )
  1124. /*++
  1125. Routine Description:
  1126. The function LookupElementGenericTable will find an element in a generic
  1127. table. If the element is located the return value is a pointer to
  1128. the user defined structure associated with the element, otherwise if
  1129. the element is not located the return value is NULL. The user supplied
  1130. input buffer is only used as a key in locating the element in the table.
  1131. Arguments:
  1132. Table - Pointer to the users Generic table to search for the key.
  1133. Buffer - Used for the comparasion.
  1134. Return Value:
  1135. PVOID - returns a pointer to the user data.
  1136. --*/
  1137. {
  1138. //
  1139. // Holds a pointer to the node in the table or what would be the
  1140. // parent of the node.
  1141. //
  1142. PRTL_BALANCED_LINKS NodeOrParent;
  1143. //
  1144. // Holds the result of the table lookup.
  1145. //
  1146. TABLE_SEARCH_RESULT Lookup;
  1147. return RtlLookupElementGenericTableFullAvl(
  1148. Table,
  1149. Buffer,
  1150. &NodeOrParent,
  1151. &Lookup
  1152. );
  1153. }
  1154. PVOID
  1155. NTAPI
  1156. RtlLookupElementGenericTableFullAvl (
  1157. PRTL_AVL_TABLE Table,
  1158. PVOID Buffer,
  1159. OUT PVOID *NodeOrParent,
  1160. OUT TABLE_SEARCH_RESULT *SearchResult
  1161. )
  1162. /*++
  1163. Routine Description:
  1164. The function LookupElementGenericTableFullAvl will find an element in a generic
  1165. table. If the element is located the return value is a pointer to
  1166. the user defined structure associated with the element. If the element is not
  1167. located then a pointer to the parent for the insert location is returned. The
  1168. user must look at the SearchResult value to determine which is being returned.
  1169. The user can use the SearchResult and parent for a subsequent FullInsertElement
  1170. call to optimize the insert.
  1171. Arguments:
  1172. Table - Pointer to the users Generic table to search for the key.
  1173. Buffer - Used for the comparasion.
  1174. NodeOrParent - Address to store the desired Node or parent of the desired node.
  1175. SearchResult - Describes the relationship of the NodeOrParent with the desired Node.
  1176. Return Value:
  1177. PVOID - returns a pointer to the user data.
  1178. --*/
  1179. {
  1180. //
  1181. // Lookup the element and save the result.
  1182. //
  1183. *SearchResult = FindNodeOrParent(
  1184. Table,
  1185. Buffer,
  1186. (PRTL_BALANCED_LINKS *)NodeOrParent
  1187. );
  1188. if (*SearchResult != TableFoundNode) {
  1189. return NULL;
  1190. } else {
  1191. //
  1192. // Return a pointer to the user data.
  1193. //
  1194. return &((PTABLE_ENTRY_HEADER)*NodeOrParent)->UserData;
  1195. }
  1196. }
  1197. PVOID
  1198. RtlEnumerateGenericTableAvl (
  1199. IN PRTL_AVL_TABLE Table,
  1200. IN BOOLEAN Restart
  1201. )
  1202. /*++
  1203. Routine Description:
  1204. The function EnumerateGenericTableAvl will return to the caller one-by-one
  1205. the elements of of a table. The return value is a pointer to the user
  1206. defined structure associated with the element. The input parameter
  1207. Restart indicates if the enumeration should start from the beginning
  1208. or should return the next element. If there are no more new elements to
  1209. return the return value is NULL. As an example of its use, to enumerate
  1210. all of the elements in a table the user would write:
  1211. for (ptr = EnumerateGenericTableAvl(Table,TRUE);
  1212. ptr != NULL;
  1213. ptr = EnumerateGenericTableAvl(Table, FALSE)) {
  1214. :
  1215. }
  1216. For a summary of when to use each of the four enumeration routines, see
  1217. RtlEnumerateGenericTableLikeADirectory.
  1218. Arguments:
  1219. Table - Pointer to the generic table to enumerate.
  1220. Restart - Flag that if true we should start with the least
  1221. element in the tree otherwise, return we return
  1222. a pointer to the user data for the root and make
  1223. the real successor to the root the new root.
  1224. Return Value:
  1225. PVOID - Pointer to the user data.
  1226. --*/
  1227. {
  1228. //
  1229. // If he said Restart, then zero Table->RestartKey before calling the
  1230. // common routine.
  1231. //
  1232. if (Restart) {
  1233. Table->RestartKey = NULL;
  1234. }
  1235. return RtlEnumerateGenericTableWithoutSplayingAvl( Table, &Table->RestartKey );
  1236. }
  1237. BOOLEAN
  1238. RtlIsGenericTableEmptyAvl (
  1239. IN PRTL_AVL_TABLE Table
  1240. )
  1241. /*++
  1242. Routine Description:
  1243. The function IsGenericTableEmptyAvl will return to the caller TRUE if
  1244. the input table is empty (i.e., does not contain any elements) and
  1245. FALSE otherwise.
  1246. Arguments:
  1247. Table - Supplies a pointer to the Generic Table.
  1248. Return Value:
  1249. BOOLEAN - if enabled the tree is empty.
  1250. --*/
  1251. {
  1252. //
  1253. // Table is empty if the root pointer is null.
  1254. //
  1255. return ((Table->NumberGenericTableElements)?(FALSE):(TRUE));
  1256. }
  1257. PVOID
  1258. RtlGetElementGenericTableAvl (
  1259. IN PRTL_AVL_TABLE Table,
  1260. IN ULONG I
  1261. )
  1262. /*++
  1263. Routine Description:
  1264. The function GetElementGenericTableAvl will return the i'th element in the
  1265. generic table by collation order. I = 0 implies the first/lowest element,
  1266. I = (RtlNumberGenericTableElements2(Table)-1) will return the last/highest
  1267. element in the generic table. The type of I is ULONG. Values
  1268. of I > than (NumberGenericTableElements(Table)-1) will return NULL. If
  1269. an arbitrary element is deleted from the generic table it will cause
  1270. all elements inserted after the deleted element to "move up".
  1271. For a summary of when to use each of the four enumeration routines, see
  1272. RtlEnumerateGenericTableLikeADirectory.
  1273. NOTE!!! THE ORIGINAL GENERIC TABLE PACKAGE RETURNED ITEMS FROM THIS ROUTINE
  1274. IN INSERT ORDER, BUT THIS ROUTINE RETURNS ELEMENTS IN COLLATION ORDER. MOST
  1275. CALLERS MAY NOT CARE, BUT IF INSERT ORDER IS REQUIRED, THE CALLER MUST MAINTAIN
  1276. INSERT ORDER VIA LINKS IN USERDATA, BECAUSE THIS TABLE PACKAGE DOES NOT MAINTAIN
  1277. INSERT ORDER. ALSO, LIKE THE PREVIOUS IMPLEMENTATION, THIS ROUTINE MAY SKIP OR
  1278. REPEAT NODES IF ENUMERATION OCCURS IN PARALLEL WITH INSERTS AND DELETES.
  1279. IN CONCLUSION, THIS ROUTINE IS NOT RECOMMENDED, AND IS SUPPLIED FOR BACKWARDS
  1280. COMPATIBILITY ONLY. SEE COMMENTS ABOUT WHICH ROUTINE TO CHOOSE IN THE ROUTINE
  1281. COMMENTS FOR RtlEnumerateGenericTableLikeADirectory.
  1282. Arguments:
  1283. Table - Pointer to the generic table from which to get the ith element.
  1284. I - Which element to get.
  1285. Return Value:
  1286. PVOID - Pointer to the user data.
  1287. --*/
  1288. {
  1289. //
  1290. // Current location in the table, 0-based like I.
  1291. //
  1292. ULONG CurrentLocation = Table->WhichOrderedElement;
  1293. //
  1294. // Hold the number of elements in the table.
  1295. //
  1296. ULONG NumberInTable = Table->NumberGenericTableElements;
  1297. //
  1298. // Will hold distances to travel to the desired node;
  1299. //
  1300. ULONG ForwardDistance,BackwardDistance;
  1301. //
  1302. // Will point to the current element in the linked list.
  1303. //
  1304. PRTL_BALANCED_LINKS CurrentNode = (PRTL_BALANCED_LINKS)Table->OrderedPointer;
  1305. //
  1306. // If it's out of bounds get out quick.
  1307. //
  1308. if ((I == MAXULONG) || ((I + 1) > NumberInTable)) return NULL;
  1309. //
  1310. // NULL means first node. We just loop until we find the leftmost child of the root.
  1311. // Because of the above test, we know there is at least one element in the table.
  1312. //
  1313. if (CurrentNode == NULL) {
  1314. for (
  1315. CurrentNode = Table->BalancedRoot.RightChild;
  1316. CurrentNode->LeftChild;
  1317. CurrentNode = CurrentNode->LeftChild
  1318. ) {
  1319. NOTHING;
  1320. }
  1321. CurrentLocation = 0;
  1322. //
  1323. // Update the table to save repeating this loop on a subsequent call.
  1324. //
  1325. Table->OrderedPointer = CurrentNode;
  1326. Table->WhichOrderedElement = 0;
  1327. }
  1328. //
  1329. // If we're already at the node then return it.
  1330. //
  1331. if (I == CurrentLocation) {
  1332. return &((PTABLE_ENTRY_HEADER)CurrentNode)->UserData;
  1333. }
  1334. //
  1335. // Calculate the forward and backward distance to the node.
  1336. //
  1337. if (CurrentLocation > I) {
  1338. //
  1339. // When CurrentLocation is greater than where we want to go,
  1340. // if moving forward gets us there quicker than moving backward
  1341. // then it follows that moving forward from the first node in tree is
  1342. // going to take fewer steps. (This is because, moving forward
  1343. // in this case must move *through* the listhead.)
  1344. //
  1345. // The work here is to figure out if moving backward would be quicker.
  1346. //
  1347. // Moving backward would be quicker only if the location we wish to
  1348. // go to is half way or more between the listhead and where we
  1349. // currently are.
  1350. //
  1351. if (I >= (CurrentLocation/2)) {
  1352. //
  1353. // Where we want to go is more than half way from the listhead
  1354. // We can traval backwards from our current location.
  1355. //
  1356. for (
  1357. BackwardDistance = CurrentLocation - I;
  1358. BackwardDistance;
  1359. BackwardDistance--
  1360. ) {
  1361. CurrentNode = RealPredecessor(CurrentNode);
  1362. }
  1363. } else {
  1364. //
  1365. // We just loop until we find the leftmost child of the root,
  1366. // which is the lowest entry in the tree.
  1367. //
  1368. for (
  1369. CurrentNode = Table->BalancedRoot.RightChild;
  1370. CurrentNode->LeftChild;
  1371. CurrentNode = CurrentNode->LeftChild
  1372. ) {
  1373. NOTHING;
  1374. }
  1375. //
  1376. // Where we want to go is less than halfway between the start
  1377. // and where we currently are. Start from the first node.
  1378. //
  1379. for (
  1380. ;
  1381. I;
  1382. I--
  1383. ) {
  1384. CurrentNode = RealSuccessor(CurrentNode);
  1385. }
  1386. }
  1387. } else {
  1388. //
  1389. // When CurrentLocation is less than where we want to go,
  1390. // if moving backwards gets us there quicker than moving forwards
  1391. // then it follows that moving backwards from the last node is
  1392. // going to take fewer steps.
  1393. //
  1394. ForwardDistance = I - CurrentLocation;
  1395. //
  1396. // Do the backwards calculation assuming we are starting with the
  1397. // last element in the table. (Thus BackwardDistance is 0 for the
  1398. // last element in the table.)
  1399. //
  1400. BackwardDistance = NumberInTable - (I + 1);
  1401. //
  1402. // For our heuristic check, bias BackwardDistance by 1, so that we
  1403. // do not always have to loop down the right side of the tree to
  1404. // return the last element in the table!
  1405. //
  1406. if (ForwardDistance <= (BackwardDistance + 1)) {
  1407. for (
  1408. ;
  1409. ForwardDistance;
  1410. ForwardDistance--
  1411. ) {
  1412. CurrentNode = RealSuccessor(CurrentNode);
  1413. }
  1414. } else {
  1415. //
  1416. // We just loop until we find the rightmost child of the root,
  1417. // which is the highest entry in the tree.
  1418. //
  1419. for (
  1420. CurrentNode = Table->BalancedRoot.RightChild;
  1421. CurrentNode->RightChild;
  1422. CurrentNode = CurrentNode->RightChild
  1423. ) {
  1424. NOTHING;
  1425. }
  1426. for (
  1427. ;
  1428. BackwardDistance;
  1429. BackwardDistance--
  1430. ) {
  1431. CurrentNode = RealPredecessor(CurrentNode);
  1432. }
  1433. }
  1434. }
  1435. //
  1436. // We're where we want to be. Save our current location and return
  1437. // a pointer to the data to the user.
  1438. //
  1439. Table->OrderedPointer = CurrentNode;
  1440. Table->WhichOrderedElement = I;
  1441. return &((PTABLE_ENTRY_HEADER)CurrentNode)->UserData;
  1442. }
  1443. ULONG
  1444. RtlNumberGenericTableElementsAvl (
  1445. IN PRTL_AVL_TABLE Table
  1446. )
  1447. /*++
  1448. Routine Description:
  1449. The function NumberGenericTableElements2 returns a ULONG value
  1450. which is the number of generic table elements currently inserted
  1451. in the generic table.
  1452. Arguments:
  1453. Table - Pointer to the generic table from which to find out the number
  1454. of elements.
  1455. Return Value:
  1456. ULONG - The number of elements in the generic table.
  1457. --*/
  1458. {
  1459. return Table->NumberGenericTableElements;
  1460. }
  1461. PVOID
  1462. RtlEnumerateGenericTableWithoutSplayingAvl (
  1463. IN PRTL_AVL_TABLE Table,
  1464. IN PVOID *RestartKey
  1465. )
  1466. /*++
  1467. Routine Description:
  1468. The function EnumerateGenericTableWithoutSplayingAvl will return to the
  1469. caller one-by-one the elements of of a table. The return value is a
  1470. pointer to the user defined structure associated with the element.
  1471. The input parameter RestartKey indicates if the enumeration should
  1472. start from the beginning or should return the next element. If the
  1473. are no more new elements to return the return value is NULL. As an
  1474. example of its use, to enumerate all of the elements in a table the
  1475. user would write:
  1476. *RestartKey = NULL;
  1477. for (ptr = EnumerateGenericTableWithoutSplayingAvl(Table, &RestartKey);
  1478. ptr != NULL;
  1479. ptr = EnumerateGenericTableWithoutSplayingAvl(Table, &RestartKey)) {
  1480. :
  1481. }
  1482. For a summary of when to use each of the four enumeration routines, see
  1483. RtlEnumerateGenericTableLikeADirectory.
  1484. Arguments:
  1485. Table - Pointer to the generic table to enumerate.
  1486. RestartKey - Pointer that indicates if we should restart or return the next
  1487. element. If the contents of RestartKey is NULL, the search
  1488. will be started from the beginning.
  1489. Return Value:
  1490. PVOID - Pointer to the user data.
  1491. --*/
  1492. {
  1493. if (RtlIsGenericTableEmptyAvl(Table)) {
  1494. //
  1495. // Nothing to do if the table is empty.
  1496. //
  1497. return NULL;
  1498. } else {
  1499. //
  1500. // Will be used as the "iteration" through the tree.
  1501. //
  1502. PRTL_BALANCED_LINKS NodeToReturn;
  1503. //
  1504. // If the restart flag is true then go to the least element
  1505. // in the tree.
  1506. //
  1507. if (*RestartKey == NULL) {
  1508. //
  1509. // We just loop until we find the leftmost child of the root.
  1510. //
  1511. for (
  1512. NodeToReturn = Table->BalancedRoot.RightChild;
  1513. NodeToReturn->LeftChild;
  1514. NodeToReturn = NodeToReturn->LeftChild
  1515. ) {
  1516. ;
  1517. }
  1518. *RestartKey = NodeToReturn;
  1519. } else {
  1520. //
  1521. // The caller has passed in the previous entry found
  1522. // in the table to enable us to continue the search. We call
  1523. // RealSuccessor to step to the next element in the tree.
  1524. //
  1525. NodeToReturn = RealSuccessor(*RestartKey);
  1526. if (NodeToReturn) {
  1527. *RestartKey = NodeToReturn;
  1528. }
  1529. }
  1530. //
  1531. // If there actually is a next element in the enumeration
  1532. // then the pointer to return is right after the list links.
  1533. //
  1534. return ((NodeToReturn)?
  1535. ((PVOID)&((PTABLE_ENTRY_HEADER)NodeToReturn)->UserData)
  1536. :((PVOID)(NULL)));
  1537. }
  1538. }
  1539. PVOID
  1540. NTAPI
  1541. RtlEnumerateGenericTableLikeADirectory (
  1542. IN PRTL_AVL_TABLE Table,
  1543. IN PRTL_AVL_MATCH_FUNCTION MatchFunction OPTIONAL,
  1544. IN PVOID MatchData OPTIONAL,
  1545. IN ULONG NextFlag,
  1546. IN OUT PVOID *RestartKey,
  1547. IN OUT PULONG DeleteCount,
  1548. IN PVOID Buffer
  1549. )
  1550. /*++
  1551. Routine Description:
  1552. The function EnumerateGenericTableLikeADirectory will return to the
  1553. caller one-by-one the elements of a table in collation order. The
  1554. return value is a pointer to the user defined structure associated
  1555. with the element. The in/out parameter RestartKey indicates if the
  1556. enumeration should start from a specified key or should return the
  1557. next element. If there are no more new elements to return the return
  1558. value is NULL. As an example of its use, to enumerate all *matched*
  1559. elements in a table the user would write:
  1560. NextFlag = FALSE;
  1561. RestartKey = NULL;
  1562. DeleteCount = 0;
  1563. (Initialize Buffer for start/resume point)
  1564. for (ptr = EnumerateGenericTableLikeADirectory(Table, ...);
  1565. ptr != NULL;
  1566. ptr = EnumerateGenericTableLikeADirectory(Table, ...)) {
  1567. :
  1568. }
  1569. The primary goal of this routine is to provide directory enumeration
  1570. style semantics, for components like TXF which stores lookaside information
  1571. on directories for pending create/delete operations. In addition a caller
  1572. may be interested in using the extended functionality available for
  1573. directory enumerations, such as the match function or flexible resume
  1574. semantics.
  1575. Enumerations via this routine across intermixed insert and delete operations
  1576. are safe. All names not involved in inserts and deletes will be returned
  1577. exactly once (unless explicitly resumed from an earlier point), and
  1578. all intermixed inserts and deletes will be seen or not seen based on their
  1579. state at the time the enumeration processes the respective directory range.
  1580. To summarize the four(!) enumeration routines and when to use them:
  1581. - For the simplest way for a single thread to enumerate the entire table
  1582. in collation order and safely across inserts and deletes, use
  1583. RtlEnumerateGenericTableAvl. This routine is not reentrant, and thus
  1584. requires exclusive access to the table across the entire enumeration.
  1585. (This routine often used by a caller who is deleting all elements of the
  1586. table.)
  1587. - For the simplest way for multiple threads to enumerate the entire table
  1588. in collation order and in parallel, use RtlEnumerateGenericTableWithoutSplayingAvl.
  1589. This routine is not safe across inserts and deletes, and thus should be
  1590. synchronized with shared access to lock out changes across the entire
  1591. enumeration.
  1592. - For the simplest way for multiple threads to enumerate the entire table
  1593. in collation order and in parallel, and with progress across inserts and deletes,
  1594. use RtlGetElementGenericTableAvl. This routine requires only shared access
  1595. across each individual call (rather than across the entire enumeration).
  1596. However, inserts and deletes can cause items to be repeated or dropped during
  1597. the enumeration. THEREFORE, THE ROUTINE IS NOT RECOMMENDED. Use shared access
  1598. across the entire enumeration with the previous routine, or use the LikeADirectory
  1599. routine for shared access on each call only with no repeats or drops.
  1600. - To enumerate the table in multiple threads in collation order, safely
  1601. across inserts and deletes, and with shared access only across individual
  1602. calls, use RtlEnumerateGenericTableLikeADirectory. This is the only routine
  1603. that supports collation order and synchronization only over individual calls
  1604. without erroneously dropping or repeating names due to inserts or deletes.
  1605. Use this routine also if a matching function or flexible resume semantics are
  1606. required.
  1607. Arguments:
  1608. Table - Pointer to the generic table to enumerate.
  1609. MatchFunction - A match function to determine which entries are to be returned.
  1610. If not specified, all nodes will be returned.
  1611. MatchData - Pointer to be passed to the match function - a simple example might
  1612. be a string expression with wildcards.
  1613. NextFlag - FALSE to return the first/current entry described by RestartKey or
  1614. Buffer (if matched). TRUE to return the next entry after that (if
  1615. matched).
  1616. RestartKey - Pointer that indicates if we should restart or return the next
  1617. element. If the contents of RestartKey is NULL, the enumeration
  1618. will be started/resumed from the position described in Buffer.
  1619. If not NULL, the enumeration will resume from the most recent point,
  1620. if there were no intervening deletes. If there was an intervening
  1621. delete, enumeration will resume from the position described in
  1622. Buffer. On return this field is updated to remember the key being
  1623. returned.
  1624. DeleteCount - This field is effectively ignored if RestartKey is NULL (nonresume case).
  1625. Otherwise, enumeration will resume from the RestartKey iff there
  1626. have been no intervening deletes in the table. On return this
  1627. field is always updated with the current DeleteCount from the table.
  1628. Buffer - Passed to the comparison routine if not resuming from RestartKey, to navigate
  1629. the table. This buffer must contain a key expression. To repeat a remembered
  1630. key, pass the key here, and insure RestartKey = NULL and NextFlag is FALSE.
  1631. To return the next key after a remembered key, pass the key here, and insure
  1632. RestartKey = NULL and NextFlag = TRUE - In either case, if the remembered key
  1633. is now deleted, the next matched key will be returned.
  1634. To enumerate the table from the beginning, initialize Buffer to contain
  1635. <min-key> before the first call with RestartKey = NULL. To start from an
  1636. arbitrary point in the table, initialize this key to contain the desired
  1637. starting key (or approximate key) before the first call. So, for example,
  1638. with the proper collate and match functions, to get all entries starting with
  1639. TXF, you could initialize Buffer to be TXF*. The collate routine would position
  1640. to the first key starting with TXF (as if * was 0), and the match function would
  1641. return STATUS_NO_MORE_MATCHES when the first key was encountered lexigraphically
  1642. beyond TXF* (as if * were 0xFFFF).
  1643. Return Value:
  1644. PVOID - Pointer to the user data, or NULL if there are no more matching entries
  1645. --*/
  1646. {
  1647. NTSTATUS Status;
  1648. //
  1649. // Holds a pointer to the node in the table or what would be the
  1650. // parent of the node.
  1651. //
  1652. PTABLE_ENTRY_HEADER NodeOrParent = (PTABLE_ENTRY_HEADER)*RestartKey;
  1653. //
  1654. // Holds the result of the table lookup.
  1655. //
  1656. TABLE_SEARCH_RESULT Lookup;
  1657. //
  1658. // Get out if the table is empty.
  1659. //
  1660. if (RtlIsGenericTableEmptyAvl(Table)) {
  1661. *RestartKey = NULL;
  1662. return NULL;
  1663. }
  1664. //
  1665. // If no MatchFunction was specified, then match all.
  1666. //
  1667. if (MatchFunction == NULL) {
  1668. MatchFunction = &MatchAll;
  1669. }
  1670. //
  1671. // If there was a delete since the last time DeleteCount was captured, then we
  1672. // cannot trust the RestartKey.
  1673. //
  1674. if (*DeleteCount != Table->DeleteCount) {
  1675. NodeOrParent = NULL;
  1676. }
  1677. //
  1678. // No saved key at this pointer, position ourselves in the directory by the key value.
  1679. //
  1680. ASSERT(FIELD_OFFSET(TABLE_ENTRY_HEADER, BalancedLinks) == 0);
  1681. if (NodeOrParent == NULL) {
  1682. Lookup = FindNodeOrParent(
  1683. Table,
  1684. Buffer,
  1685. (PRTL_BALANCED_LINKS *)&NodeOrParent
  1686. );
  1687. //
  1688. // If the exact key was not found, we can still use this position, but clea NextFlag
  1689. // so we do not skip over something that has not been returned yet.
  1690. //
  1691. if (Lookup != TableFoundNode) {
  1692. NextFlag = FALSE;
  1693. //
  1694. // NodeOrParent points to a parent at which our key buffer could be inserted.
  1695. // If we were to be the left child, then NodeOrParent just happens to be the correct
  1696. // successor, otherwise if we would be the right child, then the successor of the
  1697. // specified key is the successor of the current NodeOrParent.
  1698. //
  1699. if (Lookup == TableInsertAsRight) {
  1700. NodeOrParent = (PTABLE_ENTRY_HEADER)RealSuccessor((PRTL_BALANCED_LINKS)NodeOrParent);
  1701. }
  1702. }
  1703. }
  1704. //
  1705. // Now see if we are supposed to skip one.
  1706. //
  1707. if (NextFlag) {
  1708. ASSERT(NodeOrParent != NULL);
  1709. NodeOrParent = (PTABLE_ENTRY_HEADER)RealSuccessor((PRTL_BALANCED_LINKS)NodeOrParent);
  1710. }
  1711. //
  1712. // Continue to enumerate until we hit the end of the matches or get a match.
  1713. //
  1714. while ((NodeOrParent != NULL) && ((Status = (*MatchFunction)(Table, &NodeOrParent->UserData, MatchData)) == STATUS_NO_MATCH)) {
  1715. NodeOrParent = (PTABLE_ENTRY_HEADER)RealSuccessor((PRTL_BALANCED_LINKS)NodeOrParent);
  1716. }
  1717. //
  1718. // If we terminated the above loop with a pointer, it is either because we got a match, or
  1719. // because the match function knows that there will be no more matches. Fill in the OUT
  1720. // parameters the same in either case, but only return the UserData pointer if we really
  1721. // got a match.
  1722. //
  1723. if (NodeOrParent != NULL) {
  1724. ASSERT((Status == STATUS_SUCCESS) || (Status == STATUS_NO_MORE_MATCHES));
  1725. *RestartKey = NodeOrParent;
  1726. *DeleteCount = Table->DeleteCount;
  1727. if (Status == STATUS_SUCCESS) {
  1728. return &NodeOrParent->UserData;
  1729. }
  1730. }
  1731. return NULL;
  1732. }