AvlTree.h (36205B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 // The methods 'AvlTreeImpl::insert_worker' and 'AvlTreeImpl::delete_worker', 8 // and all supporting methods reachable from them, are derived from a public 9 // domain implementation by Georg Kraml. The public domain implementation in 10 // C was translated into Rust and the Rust translation was later translated 11 // into this C++ implementation. 12 // 13 // Unfortunately the relevant web site for the original C version is long 14 // gone, and can only be found on the Wayback Machine: 15 // 16 // https://web.archive.org/web/20010419134337/ 17 // http://www.kraml.at/georg/avltree/index.html 18 // 19 // https://web.archive.org/web/20030926063347/ 20 // http://www.kraml.at/georg/avltree/avlmonolithic.c 21 // 22 // https://web.archive.org/web/20030401124003/http://www.kraml.at/src/howto/ 23 // 24 // The intermediate Rust translation can be found at 25 // 26 // https://github.com/bytecodealliance/regalloc.rs/blob/main/lib/src/avl_tree.rs 27 // 28 // For relicensing clearances, see Mozilla bugs 1620332 and 1769261: 29 // 30 // https://bugzilla.mozilla.org/show_bug.cgi?id=1620332 31 // https://bugzilla.mozilla.org/show_bug.cgi?id=1769261 32 // 33 // All other code in this file originates from Mozilla. 34 35 #ifndef ds_AvlTree_h 36 #define ds_AvlTree_h 37 38 #include "mozilla/Attributes.h" 39 #include "mozilla/Likely.h" 40 #include "mozilla/MathAlgorithms.h" 41 #include "mozilla/Maybe.h" 42 #include "ds/LifoAlloc.h" 43 44 namespace js { 45 46 //////////////////////////////////////////////////////////////////////// 47 // // 48 // AvlTree implementation. For interface see `class AvlTree` below. // 49 // // 50 //////////////////////////////////////////////////////////////////////// 51 52 // An AVL tree class, with private allocator and node-recycling. `T` is the 53 // class of elements in the tree. `C` must provide a method 54 // 55 // static int compare(const T&, const T&) 56 // 57 // to provide a total ordering on values `T` that are put into the tree, 58 // returning -1 for less-than, 0 for equal, and 1 for greater-than. 59 // 60 // `C::compare` does not have to be a total ordering for *all* values of `T`, 61 // but it must be so for the `T` values in the tree. Requests to insert 62 // duplicate `T` values, as determined equal by `C::compare`, are valid but 63 // will be ignored in this implementation class: the stored data is unchanged. 64 // The interface class `AvlTree` however will MOZ_CRASH() on such requests. 65 // 66 // `T` values stored in the tree will not be explicitly freed or destroyed. 67 // 68 // For best cache-friendlyness, try to put the fields of `T` that are read by 69 // your compare function at the end of `T`. See comment on `struct Node` 70 // below. 71 // 72 // Some operations require (internally) building a stack of tree nodes from 73 // the root to some leaf. The maximum stack size, and hence the maximum tree 74 // depth, is currently bounded at 48. The max depth of an AVL tree is roughly 75 // 1.44 * log2(# nodes), so providing the tree-balancing machinery works 76 // correctly, the max number of nodes is at least 2^(48 / 1.44), somewhat over 77 // 2^33 (= 8 G). On a 32-bit target we'll run out of address space long 78 // before reaching that. On a 64-bit target, the minimum imaginable 79 // sizeof(Node) is 16 (for the two pointers), so a tree with 2^33 nodes would 80 // occupy at least 2^37 bytes, viz, around 137GB. So this seems unlikely to 81 // be a limitation. 82 // 83 // All stack-pushing operations are release-asserted to not overflow the stack. 84 85 template <class T, class C> 86 class AvlTreeImpl { 87 // This is the implementation of AVL trees. If you want to know how to use 88 // them in your code, don't read this; instead look below at the public 89 // interface, that is, `class AvlTree`. 90 // 91 // All of `AvlTreeImpl`, apart from the iterator code at the bottom, is 92 // protected. Public facilities are provided by child class `AvlTree`. 93 protected: 94 // Tree node tags. 95 enum class Tag { 96 Free, // Node not in use -- is on the freelist. 97 None, // Node in use. Neither subtree is deeper. 98 Left, // Node in use. The left subtree is deeper. 99 Right, // Node in use. The right subtree is deeper. 100 101 Count, // Not used as an actual tag - should remain last in this list 102 }; 103 104 // Tree nodes. The tag is stored in the lower bits of the right Node pointer 105 // rather than as a separate field. For types T with alignment no more than 106 // twice the size of a pointer (ie, most types), this reduces the size of Node 107 // and enables them to pack more tightly, reducing memory requirements and 108 // improving cache behavior. (See bug 1847616.) 109 struct Node { 110 T item; 111 Node* left; 112 113 // This is the mask to use to extract the tag from `rightAndTag`. 114 static constexpr uintptr_t kTagMask = 3; 115 static_assert(mozilla::IsPowerOfTwo(kTagMask + 1), 116 "kTagMask must only have a consecutive sequence of its " 117 "lowest bits set"); 118 static_assert( 119 kTagMask >= static_cast<uintptr_t>(Tag::Count) - 1, 120 "kTagMask must be sufficient to cover largest value in 'Tag'"); 121 122 private: 123 uintptr_t rightAndTag; 124 125 public: 126 explicit Node(const T& item) 127 : item(item), 128 left(nullptr), 129 rightAndTag(static_cast<uintptr_t>(Tag::None)) {} 130 131 [[nodiscard]] Node* getRight() const { 132 return reinterpret_cast<Node*>(rightAndTag & ~kTagMask); 133 } 134 [[nodiscard]] Tag getTag() const { 135 return static_cast<Tag>(rightAndTag & kTagMask); 136 } 137 138 void setRight(Node* right) { 139 rightAndTag = 140 reinterpret_cast<uintptr_t>(right) | static_cast<uintptr_t>(getTag()); 141 } 142 void setTag(const Tag tag) { 143 rightAndTag = (rightAndTag & ~kTagMask) | static_cast<uintptr_t>(tag); 144 } 145 void setRightAndTag(Node* right, const Tag tag) { 146 const uintptr_t rightAsUint = reinterpret_cast<uintptr_t>(right); 147 rightAndTag = rightAsUint | static_cast<uintptr_t>(tag); 148 } 149 }; 150 151 // Ensure that we can use the needed lower bits of a Node pointer to store the 152 // tag. 153 static_assert(alignof(Node) >= Node::kTagMask + 1); 154 155 // Once-per-tree components. 156 Node* root_; 157 Node* freeList_; 158 LifoAlloc* alloc_; 159 160 // As a modest but easy optimisation, ::allocateNode will allocate one node 161 // at the first call that sees an empty `freeList_`, two on the next such 162 // call and four on subsequent such calls. This has the effect of reducing 163 // the number of calls to the underlying allocator `alloc_` by a factor of 4 164 // for all but the smallest trees. It also helps pack more nodes into each 165 // cache line. The limit of 4 exists for three reasons: 166 // 167 // (1) It gains the majority (75%) of the available benefit from reducing 168 // the number of calls to `alloc_`, as the allocation size tends to 169 // infinity. 170 // 171 // (2) Similarly, 4 `struct Node`s will surely be greater than 128 bytes, 172 // hence there is minimal chance to use even fewer cache lines by increasing 173 // the group size further. In any case most machines have cache lines of 174 // size 64 bytes, not 128. 175 // 176 // (3) Most importantly, it limits the maximum potentially wasted space, 177 // which is the case where a request causes an allocation of N nodes, of 178 // which one is used immediately and the N-1 are put on the freelist, but 179 // then -- because the tree never grows larger -- are never used. Given 180 // that N=4 here, the worst case lossage is 3 nodes, which seems tolerable. 181 uint32_t nextAllocSize_; // 1, 2 or 4 only 182 183 // The expected maximum tree depth. See comments above. 184 static const size_t MAX_TREE_DEPTH = 48; 185 186 AvlTreeImpl(const AvlTreeImpl&) = delete; 187 AvlTreeImpl& operator=(const AvlTreeImpl&) = delete; 188 189 // ---- Preliminaries --------------------------------------- // 190 191 explicit AvlTreeImpl(LifoAlloc* alloc = nullptr) 192 : root_(nullptr), freeList_(nullptr), alloc_(alloc), nextAllocSize_(1) {} 193 194 void setAllocator(LifoAlloc* alloc) { alloc_ = alloc; } 195 196 // Put `node` onto the free list, for possible later reuse. 197 inline void addToFreeList(Node* node) { 198 node->left = freeList_; 199 node->setRightAndTag(nullptr, Tag::Free); // for safety 200 freeList_ = node; 201 } 202 203 // A safer version of `addToFreeList`. 204 inline void freeNode(Node* node) { 205 MOZ_ASSERT(node->getTag() != Tag::Free); 206 addToFreeList(node); 207 } 208 209 // This is the slow path for ::allocateNode below. Allocate 1, 2 or 4 nodes 210 // as a block, return the first one properly initialised, and put the rest 211 // on the freelist, in increasing order of address. 212 MOZ_NEVER_INLINE Node* allocateNodeOOL(const T& v) { 213 switch (nextAllocSize_) { 214 case 1: { 215 nextAllocSize_ = 2; 216 Node* node = alloc_->new_<Node>(v); 217 // `node` is either fully initialized, or nullptr on OOM. 218 return node; 219 } 220 case 2: { 221 nextAllocSize_ = 4; 222 Node* nodes = alloc_->newArrayUninitialized<Node>(2); 223 if (!nodes) { 224 return nullptr; 225 } 226 Node* node0 = &nodes[0]; 227 addToFreeList(&nodes[1]); 228 new (node0) Node(v); 229 return node0; 230 } 231 case 4: { 232 Node* nodes = alloc_->newArrayUninitialized<Node>(4); 233 if (!nodes) { 234 return nullptr; 235 } 236 Node* node0 = &nodes[0]; 237 addToFreeList(&nodes[3]); 238 addToFreeList(&nodes[2]); 239 addToFreeList(&nodes[1]); 240 new (node0) Node(v); 241 return node0; 242 } 243 default: { 244 MOZ_CRASH(); 245 } 246 } 247 } 248 249 // Allocate a Node holding `v`, or return nullptr on OOM. All of the fields 250 // are initialized. 251 inline Node* allocateNode(const T& v) { 252 Node* node = freeList_; 253 if (MOZ_LIKELY(node)) { 254 MOZ_ASSERT(node->getTag() == Tag::Free); 255 freeList_ = node->left; 256 new (node) Node(v); 257 return node; 258 } 259 return allocateNodeOOL(v); 260 } 261 262 // These exist only transiently, to aid rebalancing. They indicate whether 263 // an insertion/deletion succeeded, whether subsequent rebalancing is 264 // needed. 265 enum class Result { Error, OK, Balance }; 266 267 using NodeAndResult = std::pair<Node*, Result>; 268 269 // Standard AVL single-rotate-left 270 Node* rotate_left(Node* old_root) { 271 Node* new_root = old_root->getRight(); 272 old_root->setRight(new_root->left); 273 new_root->left = old_root; 274 return new_root; 275 } 276 277 // Standard AVL single-rotate-right 278 Node* rotate_right(Node* old_root) { 279 Node* new_root = old_root->left; 280 old_root->left = new_root->getRight(); 281 new_root->setRight(old_root); 282 return new_root; 283 } 284 285 // ---- Helpers for insertion ------------------------------- // 286 287 // `leftgrown`: a helper function for `insert_worker` 288 // 289 // Parameters: 290 // 291 // root Root of a tree. This node's left subtree has just grown due to 292 // item insertion; its "tag" flag needs adjustment, and the local 293 // tree (the subtree of which this node is the root node) may have 294 // become unbalanced. 295 // 296 // Return values: 297 // 298 // The new root of the subtree, plus either: 299 // 300 // OK The local tree could be rebalanced or was balanced from the 301 // start. The caller, insert_worker, may assume the entire tree 302 // is valid. 303 // or 304 // Balance The local tree was balanced, but has grown in height. 305 // Do not assume the entire tree is valid. 306 // 307 // This function has been split into two pieces: `leftgrown`, which is small 308 // and hot, and is marked always-inline, and `leftgrown_left`, which handles 309 // a more complex and less frequent case, and is marked never-inline. The 310 // intent is to have the common case always inlined without having to deal 311 // with the extra register pressure from inlining the less frequent code. 312 // The dual function `rightgrown` is split similarly. 313 314 MOZ_NEVER_INLINE Node* leftgrown_left(Node* root) { 315 if (root->left->getTag() == Tag::Left) { 316 root->setTag(Tag::None); 317 root->left->setTag(Tag::None); 318 root = rotate_right(root); 319 } else { 320 switch (root->left->getRight()->getTag()) { 321 case Tag::Left: 322 root->setTag(Tag::Right); 323 root->left->setTag(Tag::None); 324 break; 325 case Tag::Right: 326 root->setTag(Tag::None); 327 root->left->setTag(Tag::Left); 328 break; 329 case Tag::None: 330 root->setTag(Tag::None); 331 root->left->setTag(Tag::None); 332 break; 333 case Tag::Free: 334 default: 335 MOZ_CRASH(); 336 } 337 root->left->getRight()->setTag(Tag::None); 338 root->left = rotate_left(root->left); 339 root = rotate_right(root); 340 } 341 return root; 342 } 343 344 inline NodeAndResult leftgrown(Node* root) { 345 switch (root->getTag()) { 346 case Tag::Left: 347 return NodeAndResult(leftgrown_left(root), Result::OK); 348 case Tag::Right: 349 root->setTag(Tag::None); 350 return NodeAndResult(root, Result::OK); 351 case Tag::None: 352 root->setTag(Tag::Left); 353 return NodeAndResult(root, Result::Balance); 354 case Tag::Free: 355 default: 356 break; 357 } 358 MOZ_CRASH(); 359 } 360 361 // `rightgrown`: a helper function for `insert_worker`. See `leftgrown` for 362 // details. 363 364 MOZ_NEVER_INLINE Node* rightgrown_right(Node* root) { 365 if (root->getRight()->getTag() == Tag::Right) { 366 root->setTag(Tag::None); 367 root->getRight()->setTag(Tag::None); 368 root = rotate_left(root); 369 } else { 370 switch (root->getRight()->left->getTag()) { 371 case Tag::Right: 372 root->setTag(Tag::Left); 373 root->getRight()->setTag(Tag::None); 374 break; 375 case Tag::Left: 376 root->setTag(Tag::None); 377 root->getRight()->setTag(Tag::Right); 378 break; 379 case Tag::None: 380 root->setTag(Tag::None); 381 root->getRight()->setTag(Tag::None); 382 break; 383 case Tag::Free: 384 default: 385 MOZ_CRASH(); 386 } 387 root->getRight()->left->setTag(Tag::None); 388 root->setRight(rotate_right(root->getRight())); 389 root = rotate_left(root); 390 } 391 return root; 392 } 393 394 inline NodeAndResult rightgrown(Node* root) { 395 switch (root->getTag()) { 396 case Tag::Left: 397 root->setTag(Tag::None); 398 return NodeAndResult(root, Result::OK); 399 case Tag::Right: 400 return NodeAndResult(rightgrown_right(root), Result::OK); 401 case Tag::None: 402 root->setTag(Tag::Right); 403 return NodeAndResult(root, Result::Balance); 404 case Tag::Free: 405 default: 406 break; 407 } 408 MOZ_CRASH(); 409 } 410 411 // ---- Insertion ------------------------------------------- // 412 413 // Worker for insertion. Allocates a node for `v` and inserts it into the 414 // tree. Returns: nullptr for OOM; (Node*)1 if `v` is a duplicate (per 415 // `C::compare`), in which case the tree is unchanged; otherwise (successful 416 // insertion) the new root. In the latter case, the new `item` field is 417 // initialised from `v`. 418 Node* insert_worker(const T& v) { 419 // Insertion is a two pass process. In the first pass, we descend from 420 // the root, looking for the place in the tree where the new node will go, 421 // and at the same time storing the sequence of visited nodes in a stack. 422 // In the second phase we re-ascend the tree, as guided by the stack, 423 // rebalancing as we go. 424 // 425 // Note, we start from `root_`, but that isn't updated at the end. Instead 426 // the new value is returned to the caller, which has to do the update. 427 428 Node* stack[MAX_TREE_DEPTH]; 429 size_t stackPtr = 0; // points to next available slot 430 431 #define STACK_ENTRY_SET_IS_LEFT(_nodePtr) \ 432 ((Node*)(uintptr_t(_nodePtr) | uintptr_t(1))) 433 #define STACK_ENTRY_GET_IS_LEFT(_ent) ((bool)(uintptr_t(_ent) & uintptr_t(1))) 434 #define STACK_ENTRY_GET_NODE(_ent) ((Node*)(uintptr_t(_ent) & ~uintptr_t(1))) 435 436 // In the first phase, walk down the tree to find the place where the new 437 // node should be inserted, recording our path in `stack`. This loop has 438 // a factor-of-2 unrolling (the loop body contains two logical iterations) 439 // in order to reduce the overall cost of the stack-overflow check at the 440 // bottom. 441 Node* node = root_; 442 while (true) { 443 // First logical iteration 444 if (!node) { 445 break; 446 } 447 int cmpRes1 = C::compare(v, node->item); 448 if (cmpRes1 < 0) { 449 stack[stackPtr++] = STACK_ENTRY_SET_IS_LEFT(node); 450 node = node->left; 451 } else if (cmpRes1 > 0) { 452 stack[stackPtr++] = node; 453 node = node->getRight(); 454 } else { 455 // `v` is already in the tree. Inform the caller, and don't change 456 // the tree. 457 return (Node*)(uintptr_t(1)); 458 } 459 // Second logical iteration 460 if (!node) { 461 break; 462 } 463 int cmpRes2 = C::compare(v, node->item); 464 if (cmpRes2 < 0) { 465 stack[stackPtr++] = STACK_ENTRY_SET_IS_LEFT(node); 466 node = node->left; 467 } else if (cmpRes2 > 0) { 468 stack[stackPtr++] = node; 469 node = node->getRight(); 470 } else { 471 return (Node*)(uintptr_t(1)); 472 } 473 // We're going around again. Ensure there are at least two available 474 // stack slots. 475 MOZ_RELEASE_ASSERT(stackPtr < MAX_TREE_DEPTH - 2); 476 } 477 MOZ_ASSERT(!node); 478 479 // Now allocate the new node. 480 Node* new_node = allocateNode(v); 481 if (!new_node) { 482 return nullptr; // OOM 483 } 484 485 // And unwind the stack, back to the root, rebalancing as we go. Once get 486 // to a place where the new subtree doesn't need to be rebalanced, we can 487 // stop this upward scan, because no nodes above it will need to be 488 // rebalanced either. 489 Node* curr_node = new_node; 490 Result curr_node_action = Result::Balance; 491 492 while (stackPtr > 0) { 493 Node* parent_node_tagged = stack[--stackPtr]; 494 Node* parent_node = STACK_ENTRY_GET_NODE(parent_node_tagged); 495 if (STACK_ENTRY_GET_IS_LEFT(parent_node_tagged)) { 496 parent_node->left = curr_node; 497 if (curr_node_action == Result::Balance) { 498 auto pair = leftgrown(parent_node); 499 curr_node = pair.first; 500 curr_node_action = pair.second; 501 } else { 502 curr_node = parent_node; 503 break; 504 } 505 } else { 506 parent_node->setRight(curr_node); 507 if (curr_node_action == Result::Balance) { 508 auto pair = rightgrown(parent_node); 509 curr_node = pair.first; 510 curr_node_action = pair.second; 511 } else { 512 curr_node = parent_node; 513 break; 514 } 515 } 516 } 517 518 if (stackPtr > 0) { 519 curr_node = STACK_ENTRY_GET_NODE(stack[0]); 520 } 521 MOZ_ASSERT(curr_node); 522 523 #undef STACK_ENTRY_SET_IS_LEFT 524 #undef STACK_ENTRY_GET_IS_LEFT 525 #undef STACK_ENTRY_GET_NODE 526 return curr_node; 527 } 528 529 // ---- Helpers for deletion -------------------------------- // 530 531 // `leftshrunk`: a helper function for `delete_worker` and `findlowest` 532 // 533 // Parameters: 534 // 535 // n Pointer to a node. The node's left subtree has just shrunk due to 536 // item removal; its "skew" flag needs adjustment, and the local tree 537 // (the subtree of which this node is the root node) may have become 538 // unbalanced. 539 // 540 // Return values: 541 // 542 // (jseward: apparently some node, but what is it?), plus either: 543 // 544 // OK The parent activation of the delete activation that called 545 // this function may assume the entire tree is valid. 546 // 547 // Balance Do not assume the entire tree is valid. 548 549 NodeAndResult leftshrunk(Node* n) { 550 switch (n->getTag()) { 551 case Tag::Left: { 552 n->setTag(Tag::None); 553 return NodeAndResult(n, Result::Balance); 554 } 555 case Tag::Right: { 556 if (n->getRight()->getTag() == Tag::Right) { 557 n->setTag(Tag::None); 558 n->getRight()->setTag(Tag::None); 559 n = rotate_left(n); 560 return NodeAndResult(n, Result::Balance); 561 } else if (n->getRight()->getTag() == Tag::None) { 562 n->setTag(Tag::Right); 563 n->getRight()->setTag(Tag::Left); 564 n = rotate_left(n); 565 return NodeAndResult(n, Result::OK); 566 } else { 567 switch (n->getRight()->left->getTag()) { 568 case Tag::Left: 569 n->setTag(Tag::None); 570 n->getRight()->setTag(Tag::Right); 571 break; 572 case Tag::Right: 573 n->setTag(Tag::Left); 574 n->getRight()->setTag(Tag::None); 575 break; 576 case Tag::None: 577 n->setTag(Tag::None); 578 n->getRight()->setTag(Tag::None); 579 break; 580 case Tag::Free: 581 default: 582 MOZ_CRASH(); 583 } 584 n->getRight()->left->setTag(Tag::None); 585 n->setRight(rotate_right(n->getRight())); 586 ; 587 n = rotate_left(n); 588 return NodeAndResult(n, Result::Balance); 589 } 590 /*NOTREACHED*/ MOZ_CRASH(); 591 } 592 case Tag::None: { 593 n->setTag(Tag::Right); 594 return NodeAndResult(n, Result::OK); 595 } 596 case Tag::Free: 597 default: { 598 MOZ_CRASH(); 599 } 600 } 601 MOZ_CRASH(); 602 } 603 604 // rightshrunk: a helper function for `delete` and `findhighest`. See 605 // `leftshrunk` for details. 606 607 NodeAndResult rightshrunk(Node* n) { 608 switch (n->getTag()) { 609 case Tag::Right: { 610 n->setTag(Tag::None); 611 return NodeAndResult(n, Result::Balance); 612 } 613 case Tag::Left: { 614 if (n->left->getTag() == Tag::Left) { 615 n->setTag(Tag::None); 616 n->left->setTag(Tag::None); 617 n = rotate_right(n); 618 return NodeAndResult(n, Result::Balance); 619 } else if (n->left->getTag() == Tag::None) { 620 n->setTag(Tag::Left); 621 n->left->setTag(Tag::Right); 622 n = rotate_right(n); 623 return NodeAndResult(n, Result::OK); 624 } else { 625 switch (n->left->getRight()->getTag()) { 626 case Tag::Left: 627 n->setTag(Tag::Right); 628 n->left->setTag(Tag::None); 629 break; 630 case Tag::Right: 631 n->setTag(Tag::None); 632 n->left->setTag(Tag::Left); 633 break; 634 case Tag::None: 635 n->setTag(Tag::None); 636 n->left->setTag(Tag::None); 637 break; 638 case Tag::Free: 639 default: 640 MOZ_CRASH(); 641 } 642 n->left->getRight()->setTag(Tag::None); 643 n->left = rotate_left(n->left); 644 n = rotate_right(n); 645 return NodeAndResult(n, Result::Balance); 646 } 647 /*NOTREACHED*/ MOZ_CRASH(); 648 } 649 case Tag::None: { 650 n->setTag(Tag::Left); 651 return NodeAndResult(n, Result::OK); 652 } 653 case Tag::Free: 654 default: { 655 MOZ_CRASH(); 656 } 657 } 658 MOZ_CRASH(); 659 } 660 661 // `findhighest`: helper function for `delete_worker`. It replaces a node 662 // with a subtree's greatest (per C::compare) item. 663 // 664 // Parameters: 665 // 666 // target Pointer to node to be replaced. 667 // 668 // n Address of pointer to subtree. 669 // 670 // Return value: 671 // 672 // Nothing The target node could not be replaced because the subtree 673 // provided was empty. 674 // 675 // Some(Node*,Result) jseward: it's pretty unclear, but I *think* it 676 // is a pair that has the same meaning as the 677 // pair returned by `leftgrown`, as described above. 678 679 mozilla::Maybe<NodeAndResult> findhighest(Node* target, Node* n) { 680 if (n == nullptr) { 681 return mozilla::Nothing(); 682 } 683 auto res = Result::Balance; 684 if (n->getRight() != nullptr) { 685 auto fhi = findhighest(target, n->getRight()); 686 if (fhi.isSome()) { 687 n->setRight(fhi.value().first); 688 res = fhi.value().second; 689 if (res == Result::Balance) { 690 auto pair = rightshrunk(n); 691 n = pair.first; 692 res = pair.second; 693 } 694 return mozilla::Some(NodeAndResult(n, res)); 695 } else { 696 return mozilla::Nothing(); 697 } 698 } 699 target->item = n->item; 700 Node* tmp = n; 701 n = n->left; 702 freeNode(tmp); 703 return mozilla::Some(NodeAndResult(n, res)); 704 } 705 706 // `findhighest`: helper function for `delete_worker`. It replaces a node 707 // with a subtree's greatest (per C::compare) item. See `findhighest` for 708 // details. 709 710 mozilla::Maybe<NodeAndResult> findlowest(Node* target, Node* n) { 711 if (n == nullptr) { 712 return mozilla::Nothing(); 713 } 714 Result res = Result::Balance; 715 if (n->left != nullptr) { 716 auto flo = findlowest(target, n->left); 717 if (flo.isSome()) { 718 n->left = flo.value().first; 719 res = flo.value().second; 720 if (res == Result::Balance) { 721 auto pair = leftshrunk(n); 722 n = pair.first; 723 res = pair.second; 724 } 725 return mozilla::Some(NodeAndResult(n, res)); 726 } else { 727 return mozilla::Nothing(); 728 } 729 } 730 target->item = n->item; 731 Node* tmp = n; 732 n = n->getRight(); 733 freeNode(tmp); 734 return mozilla::Some(NodeAndResult(n, res)); 735 } 736 737 // ---- Deletion -------------------------------------------- // 738 739 // Deletes the node matching `item` from an arbitrary subtree rooted at 740 // `node`. Returns the root of the new subtree (if any), a `Result` that 741 // indicates that either, the tree containing `node` does or does not need 742 // rebalancing (::Balance, ::OK) or that the item was not found (::Error). 743 744 NodeAndResult delete_worker(Node* node, const T& item) { 745 Result tmp = Result::Balance; 746 if (node == nullptr) { 747 return NodeAndResult(node, Result::Error); 748 } 749 750 int cmp_res = C::compare(item, node->item); 751 if (cmp_res < 0) { 752 auto pair1 = delete_worker(node->left, item); 753 node->left = pair1.first; 754 tmp = pair1.second; 755 if (tmp == Result::Balance) { 756 auto pair2 = leftshrunk(node); 757 node = pair2.first; 758 tmp = pair2.second; 759 } 760 return NodeAndResult(node, tmp); 761 } else if (cmp_res > 0) { 762 auto pair1 = delete_worker(node->getRight(), item); 763 node->setRight(pair1.first); 764 tmp = pair1.second; 765 if (tmp == Result::Balance) { 766 auto pair2 = rightshrunk(node); 767 node = pair2.first; 768 tmp = pair2.second; 769 } 770 return NodeAndResult(node, tmp); 771 } else { 772 if (node->left != nullptr) { 773 auto fhi = findhighest(node, node->left); 774 if (fhi.isSome()) { 775 node->left = fhi.value().first; 776 tmp = fhi.value().second; 777 if (tmp == Result::Balance) { 778 auto pair = leftshrunk(node); 779 node = pair.first; 780 tmp = pair.second; 781 } 782 } 783 return NodeAndResult(node, tmp); 784 } 785 if (node->getRight() != nullptr) { 786 auto flo = findlowest(node, node->getRight()); 787 if (flo.isSome()) { 788 node->setRight(flo.value().first); 789 tmp = flo.value().second; 790 if (tmp == Result::Balance) { 791 auto pair = rightshrunk(node); 792 node = pair.first; 793 tmp = pair.second; 794 } 795 } 796 return NodeAndResult(node, tmp); 797 } 798 freeNode(node); 799 return NodeAndResult(nullptr, Result::Balance); 800 } 801 } 802 803 // ---- Lookup ---------------------------------------------- // 804 805 // Find the node matching `v`, or return nullptr if not found. 806 Node* find_worker(const T& v) const { 807 Node* node = root_; 808 while (node) { 809 int cmpRes = C::compare(v, node->item); 810 if (cmpRes < 0) { 811 node = node->left; 812 } else if (cmpRes > 0) { 813 node = node->getRight(); 814 } else { 815 return node; 816 } 817 } 818 return nullptr; 819 } 820 821 // ---- Iteration ------------------------------------------- // 822 823 public: 824 // This provides iteration forwards over the tree. You can either iterate 825 // over the whole tree or specify a start point. To iterate over the whole 826 // tree: 827 // 828 // AvlTree<MyT,MyC> tree; 829 // .. put stuff into `tree` .. 830 // 831 // AvlTree<MyT,MyC>::Iter iter(&tree); 832 // while (iter.hasMore) { 833 // MyT item = iter.next(); 834 // } 835 // 836 // Alternatively you can initialize the iterator with some value `startAt`, 837 // so that the first value you get is greater than or equal to `startAt`, 838 // per `MyC::compare`: 839 // 840 // AvlTree<MyT,MyC>::Iter iter(&tree, startAt); 841 // 842 // Starting the iterator at a particular value requires finding the value in 843 // the tree and recording the path to it. So it's nearly as cheap as a call 844 // to `AvlTree::contains` and you can use it as a plausible substitute for 845 // `::contains` if you want. 846 // 847 // Note that `class Iter` is quite large -- around 50 machine words -- so 848 // you might want to think twice before allocating instances on the heap. 849 class Iter { 850 const AvlTreeImpl<T, C>* tree_; 851 Node* stack_[MAX_TREE_DEPTH]; 852 size_t stackPtr_; 853 854 // This sets up the iterator stack so that the first value it produces 855 // will be the smallest value that is greater than or equal to `v`. Note 856 // the structural similarity to ::find_worker above. 857 // 858 // The logic for pushing nodes on the stack looks strange at first. Once 859 // set up, the stack contains a root-to-some-node path, and the 860 // top-of-stack value is the next value the iterator will emit. If the 861 // stack becomes empty then the iteration is complete. 862 // 863 // It's not quite accurate to say that the stack contains a complete 864 // root-to-some-node path. Rather, the stack contains such a path, except 865 // it omits nodes at which the path goes to the right child. Eg: 866 // 867 // 5 868 // 3 8 869 // 1 4 7 9 870 // 871 // If the next item to be emitted is 4, then the stack will be [5, 4] and 872 // not [5, 3, 4], because at 3 we go right. This explains why the 873 // `cmpRes > 0` case in `setupIteratorStack` doesn't push an item on the 874 // stack. It also explains why the single-argument `Iter::Iter` below, 875 // which sets up for iteration starting at the lowest element, simply 876 // calls `visitLeftChildren` to do its work. 877 void setupIteratorStack(Node* node, const T& v) { 878 // Ensure stackPtr_ is cached in a register, since this function can be 879 // hot. 880 MOZ_ASSERT(stackPtr_ == 0); 881 size_t stackPtr = 0; 882 while (node) { 883 int cmpRes = C::compare(v, node->item); 884 if (cmpRes < 0) { 885 stack_[stackPtr++] = node; 886 MOZ_RELEASE_ASSERT(stackPtr < MAX_TREE_DEPTH); 887 node = node->left; 888 } else if (cmpRes > 0) { 889 node = node->getRight(); 890 } else { 891 stack_[stackPtr++] = node; 892 MOZ_RELEASE_ASSERT(stackPtr < MAX_TREE_DEPTH); 893 break; 894 } 895 } 896 stackPtr_ = stackPtr; 897 } 898 899 void visitLeftChildren(Node* node) { 900 while (true) { 901 Node* left = node->left; 902 if (left == nullptr) { 903 break; 904 } 905 stack_[stackPtr_++] = left; 906 MOZ_RELEASE_ASSERT(stackPtr_ < MAX_TREE_DEPTH); 907 node = left; 908 } 909 } 910 911 public: 912 explicit Iter(const AvlTreeImpl<T, C>* tree) { 913 tree_ = tree; 914 stackPtr_ = 0; 915 if (tree->root_ != nullptr) { 916 stack_[stackPtr_++] = tree->root_; 917 MOZ_RELEASE_ASSERT(stackPtr_ < MAX_TREE_DEPTH); 918 visitLeftChildren(tree->root_); 919 } 920 } 921 Iter(const AvlTreeImpl<T, C>* tree, const T& startAt) { 922 tree_ = tree; 923 stackPtr_ = 0; 924 setupIteratorStack(tree_->root_, startAt); 925 } 926 bool hasMore() const { return stackPtr_ > 0; } 927 T next() { 928 MOZ_RELEASE_ASSERT(stackPtr_ > 0); 929 Node* ret = stack_[--stackPtr_]; 930 Node* right = ret->getRight(); 931 if (right != nullptr) { 932 stack_[stackPtr_++] = right; 933 MOZ_RELEASE_ASSERT(stackPtr_ < MAX_TREE_DEPTH); 934 visitLeftChildren(right); 935 } 936 return ret->item; 937 } 938 }; 939 }; 940 941 //////////////////////////////////////////////////////////////////////// 942 // // 943 // AvlTree public interface, for SpiderMonkey. // 944 // // 945 //////////////////////////////////////////////////////////////////////// 946 947 // This public interface is fairly limited and restrictive. If you need to 948 // add more functionality, consider copying code from `class AvlTreeTestIF` in 949 // js/src/jsapi-tests/testAvlTree.cpp rather than rolling your own. See 950 // comments there. 951 952 template <class T, class C> 953 class AvlTree : public AvlTreeImpl<T, C> { 954 // Shorthands for names in the implementation (parent) class. 955 using Impl = AvlTreeImpl<T, C>; 956 using ImplNode = typename AvlTreeImpl<T, C>::Node; 957 using ImplResult = typename AvlTreeImpl<T, C>::Result; 958 using ImplNodeAndResult = typename AvlTreeImpl<T, C>::NodeAndResult; 959 960 public: 961 explicit AvlTree(LifoAlloc* alloc = nullptr) : Impl(alloc) {} 962 963 // You'll need to tell the tree how to allocate nodes, either here or in 964 // `AvlTree::AvlTree`. 965 void setAllocator(LifoAlloc* alloc) { Impl::setAllocator(alloc); } 966 967 // Is the tree empty? 968 bool empty() const { return Impl::root_ == nullptr; } 969 970 // Insert `v` in the tree. Returns false to indicate OOM. `v` may not be 971 // equal to any existing value in the tree, per `C::compare`; if it is, this 972 // routine will MOZ_CRASH(). It would be trivial to change this to replace 973 // an existing value instead, if needed. 974 [[nodiscard]] bool insert(const T& v) { 975 ImplNode* new_root = Impl::insert_worker(v); 976 // Take out both unlikely cases with a single comparison. 977 if (MOZ_UNLIKELY(uintptr_t(new_root) <= uintptr_t(1))) { 978 // OOM (new_root == 0) or duplicate (new_root == 1) 979 if (!new_root) { 980 // OOM 981 return false; 982 } 983 // Duplicate; tree is unchanged. 984 MOZ_CRASH(); 985 } 986 Impl::root_ = new_root; 987 return true; 988 } 989 990 // Remove `v` from the tree. `v` must actually be in the tree, per 991 // `C::compare`. If it is not, this routine will MOZ_CRASH(). 992 // Superficially it looks like we could change it to return without doing 993 // anything in that case, if needed, except we'd need to first verify that 994 // `delete_worker` doesn't change the tree in that case. 995 void remove(const T& v) { 996 ImplNodeAndResult pair = Impl::delete_worker(Impl::root_, v); 997 ImplNode* new_root = pair.first; 998 ImplResult res = pair.second; 999 if (MOZ_UNLIKELY(res == ImplResult::Error)) { 1000 // `v` isn't in the tree. 1001 MOZ_CRASH(); 1002 } else { 1003 Impl::root_ = new_root; 1004 } 1005 } 1006 1007 // Determine whether the tree contains `v` and if so return, in `res`, a 1008 // copy of the stored version. Note that the determination is done using 1009 // `C::compare` and you should consider carefully the consequences of 1010 // passing in `v` for which `C::compare` indicates "equal" for more than one 1011 // value in the tree. This is not invalid, but it does mean that you may be 1012 // returned, via `res`, *any* of the values in the tree that `compare` deems 1013 // equal to `v`, and which you get is arbitrary -- it depends on which is 1014 // closest to the root. 1015 bool contains(const T& v, T* res) const { 1016 ImplNode* node = Impl::find_worker(v); 1017 if (node) { 1018 *res = node->item; 1019 return true; 1020 } 1021 return false; 1022 } 1023 1024 // Determine whether the tree contains `v` and if so return the address of 1025 // the stored version. The comments on `::contains` about the meaning of 1026 // `C::compare` apply here too. 1027 T* maybeLookup(const T& v) { 1028 ImplNode* node = Impl::find_worker(v); 1029 if (node) { 1030 return &(node->item); 1031 } 1032 return nullptr; 1033 } 1034 1035 // AvlTree::Iter is also public; it's just pass-through from AvlTreeImpl. 1036 // See documentation above on AvlTree::Iter on how to use it. 1037 }; 1038 1039 } /* namespace js */ 1040 1041 #endif /* ds_AvlTree_h */