mfbt/SplayTree.h

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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/. */
     7 /**
     8  * A sorted tree with optimal access times, where recently-accessed elements
     9  * are faster to access again.
    10  */
    12 #ifndef mozilla_SplayTree_h
    13 #define mozilla_SplayTree_h
    15 #include "mozilla/Assertions.h"
    16 #include "mozilla/NullPtr.h"
    18 namespace mozilla {
    20 template<class T, class C>
    21 class SplayTree;
    23 template<typename T>
    24 class SplayTreeNode
    25 {
    26   public:
    27     template<class A, class B>
    28     friend class SplayTree;
    30     SplayTreeNode()
    31       : left(nullptr), right(nullptr), parent(nullptr)
    32     {}
    34   private:
    35     T* left;
    36     T* right;
    37     T* parent;
    38 };
    41 /**
    42  * Class which represents a splay tree.
    43  * Splay trees are balanced binary search trees for which search, insert and
    44  * remove are all amortized O(log n), but where accessing a node makes it
    45  * faster to access that node in the future.
    46  *
    47  * T indicates the type of tree elements, Comparator must have a static
    48  * compare(const T&, const T&) method ordering the elements. The compare
    49  * method must be free from side effects.
    50  */
    51 template<typename T, class Comparator>
    52 class SplayTree
    53 {
    54     T* root;
    56   public:
    57     SplayTree()
    58       : root(nullptr)
    59     {}
    61     bool empty() const {
    62       return !root;
    63     }
    65     T* find(const T& v)
    66     {
    67       if (empty())
    68         return nullptr;
    70       T* last = lookup(v);
    71       splay(last);
    72       checkCoherency(root, nullptr);
    73       return Comparator::compare(v, *last) == 0 ? last : nullptr;
    74     }
    76     bool insert(T* v)
    77     {
    78       MOZ_ASSERT(!find(*v), "Duplicate elements are not allowed.");
    80       if (!root) {
    81         root = v;
    82         return true;
    83       }
    84       T* last = lookup(*v);
    85       int cmp = Comparator::compare(*v, *last);
    87       T** parentPointer = (cmp < 0) ? &last->left : &last->right;
    88       MOZ_ASSERT(!*parentPointer);
    89       *parentPointer = v;
    90       v->parent = last;
    92       splay(v);
    93       checkCoherency(root, nullptr);
    94       return true;
    95     }
    97     T* remove(const T& v)
    98     {
    99       T* last = lookup(v);
   100       MOZ_ASSERT(last, "This tree must contain the element being removed.");
   101       MOZ_ASSERT(Comparator::compare(v, *last) == 0);
   103       // Splay the tree so that the item to remove is the root.
   104       splay(last);
   105       MOZ_ASSERT(last == root);
   107       // Find another node which can be swapped in for the root: either the
   108       // rightmost child of the root's left, or the leftmost child of the
   109       // root's right.
   110       T* swap;
   111       T* swapChild;
   112       if (root->left) {
   113         swap = root->left;
   114         while (swap->right)
   115           swap = swap->right;
   116         swapChild = swap->left;
   117       } else if (root->right) {
   118         swap = root->right;
   119         while (swap->left)
   120           swap = swap->left;
   121         swapChild = swap->right;
   122       } else {
   123         T* result = root;
   124         root = nullptr;
   125         return result;
   126       }
   128       // The selected node has at most one child, in swapChild. Detach it
   129       // from the subtree by replacing it with that child.
   130       if (swap == swap->parent->left)
   131         swap->parent->left = swapChild;
   132       else
   133         swap->parent->right = swapChild;
   134       if (swapChild)
   135         swapChild->parent = swap->parent;
   137       // Make the selected node the new root.
   138       root = swap;
   139       root->parent = nullptr;
   140       root->left = last->left;
   141       root->right = last->right;
   142       if (root->left) {
   143         root->left->parent = root;
   144       }
   145       if (root->right) {
   146         root->right->parent = root;
   147       }
   149       checkCoherency(root, nullptr);
   150       return last;
   151     }
   153     T* removeMin()
   154     {
   155       MOZ_ASSERT(root, "No min to remove!");
   157       T* min = root;
   158       while (min->left)
   159         min = min->left;
   160       return remove(*min);
   161     }
   163   private:
   164     /**
   165      * Returns the node in this comparing equal to |v|, or a node just greater or
   166      * just less than |v| if there is no such node.
   167      */
   168     T* lookup(const T& v)
   169     {
   170       MOZ_ASSERT(!empty());
   172       T* node = root;
   173       T* parent;
   174       do {
   175         parent = node;
   176         int c = Comparator::compare(v, *node);
   177         if (c == 0)
   178           return node;
   179         else if (c < 0)
   180           node = node->left;
   181         else
   182           node = node->right;
   183       } while (node);
   184       return parent;
   185     }
   187     /**
   188      * Rotate the tree until |node| is at the root of the tree. Performing
   189      * the rotations in this fashion preserves the amortized balancing of
   190      * the tree.
   191      */
   192     void splay(T* node)
   193     {
   194       MOZ_ASSERT(node);
   196       while (node != root) {
   197         T* parent = node->parent;
   198         if (parent == root) {
   199           // Zig rotation.
   200           rotate(node);
   201           MOZ_ASSERT(node == root);
   202           return;
   203         }
   204         T* grandparent = parent->parent;
   205         if ((parent->left == node) == (grandparent->left == parent)) {
   206           // Zig-zig rotation.
   207           rotate(parent);
   208           rotate(node);
   209         } else {
   210           // Zig-zag rotation.
   211           rotate(node);
   212           rotate(node);
   213         }
   214       }
   215     }
   217     void rotate(T* node)
   218     {
   219       // Rearrange nodes so that node becomes the parent of its current
   220       // parent, while preserving the sortedness of the tree.
   221       T* parent = node->parent;
   222       if (parent->left == node) {
   223         //     x          y
   224         //   y  c  ==>  a  x
   225         //  a b           b c
   226         parent->left = node->right;
   227         if (node->right)
   228           node->right->parent = parent;
   229         node->right = parent;
   230       } else {
   231         MOZ_ASSERT(parent->right == node);
   232         //   x             y
   233         //  a  y   ==>   x  c
   234         //    b c       a b
   235         parent->right = node->left;
   236         if (node->left)
   237           node->left->parent = parent;
   238         node->left = parent;
   239       }
   240       node->parent = parent->parent;
   241       parent->parent = node;
   242       if (T* grandparent = node->parent) {
   243         if (grandparent->left == parent)
   244           grandparent->left = node;
   245         else
   246           grandparent->right = node;
   247       } else {
   248         root = node;
   249       }
   250     }
   252     T* checkCoherency(T* node, T* minimum)
   253     {
   254 #ifdef DEBUG
   255       MOZ_ASSERT_IF(root, !root->parent);
   256       if (!node) {
   257         MOZ_ASSERT(!root);
   258         return nullptr;
   259       }
   260       MOZ_ASSERT_IF(!node->parent, node == root);
   261       MOZ_ASSERT_IF(minimum, Comparator::compare(*minimum, *node) < 0);
   262       if (node->left) {
   263         MOZ_ASSERT(node->left->parent == node);
   264         T* leftMaximum = checkCoherency(node->left, minimum);
   265         MOZ_ASSERT(Comparator::compare(*leftMaximum, *node) < 0);
   266       }
   267       if (node->right) {
   268         MOZ_ASSERT(node->right->parent == node);
   269         return checkCoherency(node->right, node);
   270       }
   271       return node;
   272 #else
   273       return nullptr;
   274 #endif
   275     }
   277     SplayTree(const SplayTree&) MOZ_DELETE;
   278     void operator=(const SplayTree&) MOZ_DELETE;
   279 };
   281 }  /* namespace mozilla */
   283 #endif /* mozilla_SplayTree_h */

mercurial