Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
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 #ifndef ds_SplayTree_h
8 #define ds_SplayTree_h
10 #include "ds/LifoAlloc.h"
12 namespace js {
14 /*
15 * Class which represents a splay tree with nodes allocated from a LifoAlloc.
16 * Splay trees are balanced binary search trees for which search, insert and
17 * remove are all amortized O(log n).
18 *
19 * T indicates the type of tree elements, C must have a static
20 * compare(const T&, const T&) method ordering the elements. As for LifoAlloc
21 * objects, T objects stored in the tree will not be explicitly destroyed.
22 */
23 template <class T, class C>
24 class SplayTree
25 {
26 struct Node {
27 T item;
28 Node *left, *right, *parent;
30 Node(const T &item)
31 : item(item), left(nullptr), right(nullptr), parent(nullptr)
32 {}
33 };
35 LifoAlloc *alloc;
36 Node *root, *freeList;
38 SplayTree(const SplayTree &) MOZ_DELETE;
39 SplayTree &operator=(const SplayTree &) MOZ_DELETE;
41 public:
43 SplayTree(LifoAlloc *alloc = nullptr)
44 : alloc(alloc), root(nullptr), freeList(nullptr)
45 {}
47 void setAllocator(LifoAlloc *alloc) {
48 this->alloc = alloc;
49 }
51 bool empty() const {
52 return !root;
53 }
55 bool contains(const T &v, T *res)
56 {
57 if (!root)
58 return false;
59 Node *last = lookup(v);
60 splay(last);
61 checkCoherency(root, nullptr);
62 if (C::compare(v, last->item) == 0) {
63 *res = last->item;
64 return true;
65 }
66 return false;
67 }
69 bool insert(const T &v)
70 {
71 Node *element = allocateNode(v);
72 if (!element)
73 return false;
75 if (!root) {
76 root = element;
77 return true;
78 }
79 Node *last = lookup(v);
80 int cmp = C::compare(v, last->item);
82 // Don't tolerate duplicate elements.
83 JS_ASSERT(cmp);
85 Node *&parentPointer = (cmp < 0) ? last->left : last->right;
86 JS_ASSERT(!parentPointer);
87 parentPointer = element;
88 element->parent = last;
90 splay(element);
91 checkCoherency(root, nullptr);
92 return true;
93 }
95 void remove(const T &v)
96 {
97 Node *last = lookup(v);
98 JS_ASSERT(last && C::compare(v, last->item) == 0);
100 splay(last);
101 JS_ASSERT(last == root);
103 // Find another node which can be swapped in for the root: either the
104 // rightmost child of the root's left, or the leftmost child of the
105 // root's right.
106 Node *swap, *swapChild;
107 if (root->left) {
108 swap = root->left;
109 while (swap->right)
110 swap = swap->right;
111 swapChild = swap->left;
112 } else if (root->right) {
113 swap = root->right;
114 while (swap->left)
115 swap = swap->left;
116 swapChild = swap->right;
117 } else {
118 freeNode(root);
119 root = nullptr;
120 return;
121 }
123 // The selected node has at most one child, in swapChild. Detach it
124 // from the subtree by replacing it with that child.
125 if (swap == swap->parent->left)
126 swap->parent->left = swapChild;
127 else
128 swap->parent->right = swapChild;
129 if (swapChild)
130 swapChild->parent = swap->parent;
132 root->item = swap->item;
133 freeNode(swap);
135 checkCoherency(root, nullptr);
136 }
138 template <class Op>
139 void forEach(Op op)
140 {
141 forEachInner(op, root);
142 }
144 private:
146 Node *lookup(const T &v)
147 {
148 JS_ASSERT(root);
149 Node *node = root, *parent;
150 do {
151 parent = node;
152 int c = C::compare(v, node->item);
153 if (c == 0)
154 return node;
155 else if (c < 0)
156 node = node->left;
157 else
158 node = node->right;
159 } while (node);
160 return parent;
161 }
163 Node *allocateNode(const T &v)
164 {
165 Node *node = freeList;
166 if (node) {
167 freeList = node->left;
168 new(node) Node(v);
169 return node;
170 }
171 return alloc->new_<Node>(v);
172 }
174 void freeNode(Node *node)
175 {
176 node->left = freeList;
177 freeList = node;
178 }
180 void splay(Node *node)
181 {
182 // Rotate the element until it is at the root of the tree. Performing
183 // the rotations in this fashion preserves the amortized balancing of
184 // the tree.
185 JS_ASSERT(node);
186 while (node != root) {
187 Node *parent = node->parent;
188 if (parent == root) {
189 // Zig rotation.
190 rotate(node);
191 JS_ASSERT(node == root);
192 return;
193 }
194 Node *grandparent = parent->parent;
195 if ((parent->left == node) == (grandparent->left == parent)) {
196 // Zig-zig rotation.
197 rotate(parent);
198 rotate(node);
199 } else {
200 // Zig-zag rotation.
201 rotate(node);
202 rotate(node);
203 }
204 }
205 }
207 void rotate(Node *node)
208 {
209 // Rearrange nodes so that node becomes the parent of its current
210 // parent, while preserving the sortedness of the tree.
211 Node *parent = node->parent;
212 if (parent->left == node) {
213 // x y
214 // y c ==> a x
215 // a b b c
216 parent->left = node->right;
217 if (node->right)
218 node->right->parent = parent;
219 node->right = parent;
220 } else {
221 JS_ASSERT(parent->right == node);
222 // x y
223 // a y ==> x c
224 // b c a b
225 parent->right = node->left;
226 if (node->left)
227 node->left->parent = parent;
228 node->left = parent;
229 }
230 node->parent = parent->parent;
231 parent->parent = node;
232 if (Node *grandparent = node->parent) {
233 if (grandparent->left == parent)
234 grandparent->left = node;
235 else
236 grandparent->right = node;
237 } else {
238 root = node;
239 }
240 }
242 template <class Op>
243 void forEachInner(Op op, Node *node)
244 {
245 if (!node)
246 return;
248 forEachInner(op, node->left);
249 op(node->item);
250 forEachInner(op, node->right);
251 }
253 Node *checkCoherency(Node *node, Node *minimum)
254 {
255 #ifdef DEBUG
256 if (!node) {
257 JS_ASSERT(!root);
258 return nullptr;
259 }
260 JS_ASSERT_IF(!node->parent, node == root);
261 JS_ASSERT_IF(minimum, C::compare(minimum->item, node->item) < 0);
262 if (node->left) {
263 JS_ASSERT(node->left->parent == node);
264 Node *leftMaximum = checkCoherency(node->left, minimum);
265 JS_ASSERT(C::compare(leftMaximum->item, node->item) < 0);
266 }
267 if (node->right) {
268 JS_ASSERT(node->right->parent == node);
269 return checkCoherency(node->right, node);
270 }
271 return node;
272 #else
273 return nullptr;
274 #endif
275 }
276 };
278 } /* namespace js */
280 #endif /* ds_SplayTree_h */