tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 */