js/src/v8/splay.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 // Copyright 2009 the V8 project authors. All rights reserved.
     2 // Redistribution and use in source and binary forms, with or without
     3 // modification, are permitted provided that the following conditions are
     4 // met:
     5 //
     6 //     * Redistributions of source code must retain the above copyright
     7 //       notice, this list of conditions and the following disclaimer.
     8 //     * Redistributions in binary form must reproduce the above
     9 //       copyright notice, this list of conditions and the following
    10 //       disclaimer in the documentation and/or other materials provided
    11 //       with the distribution.
    12 //     * Neither the name of Google Inc. nor the names of its
    13 //       contributors may be used to endorse or promote products derived
    14 //       from this software without specific prior written permission.
    15 //
    16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    28 // This benchmark is based on a JavaScript log processing module used
    29 // by the V8 profiler to generate execution time profiles for runs of
    30 // JavaScript applications, and it effectively measures how fast the
    31 // JavaScript engine is at allocating nodes and reclaiming the memory
    32 // used for old nodes. Because of the way splay trees work, the engine
    33 // also has to deal with a lot of changes to the large tree object
    34 // graph.
    36 var Splay = new BenchmarkSuite('Splay', 81491, [
    37   new Benchmark("Splay", SplayRun, SplaySetup, SplayTearDown)
    38 ]);
    41 // Configuration.
    42 var kSplayTreeSize = 8000;
    43 var kSplayTreeModifications = 80;
    44 var kSplayTreePayloadDepth = 5;
    46 var splayTree = null;
    49 function GeneratePayloadTree(depth, tag) {
    50   if (depth == 0) {
    51     return {
    52       array  : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
    53       string : 'String for key ' + tag + ' in leaf node'
    54     };
    55   } else {
    56     return {
    57       left:  GeneratePayloadTree(depth - 1, tag),
    58       right: GeneratePayloadTree(depth - 1, tag)
    59     };
    60   }
    61 }
    64 function GenerateKey() {
    65   // The benchmark framework guarantees that Math.random is
    66   // deterministic; see base.js.
    67   return Math.random();
    68 }
    71 function InsertNewNode() {
    72   // Insert new node with a unique key.
    73   var key;
    74   do {
    75     key = GenerateKey();
    76   } while (splayTree.find(key) != null);
    77   var payload = GeneratePayloadTree(kSplayTreePayloadDepth, String(key));
    78   splayTree.insert(key, payload);
    79   return key;
    80 }
    84 function SplaySetup() {
    85   splayTree = new SplayTree();
    86   for (var i = 0; i < kSplayTreeSize; i++) InsertNewNode();
    87 }
    90 function SplayTearDown() {
    91   // Allow the garbage collector to reclaim the memory
    92   // used by the splay tree no matter how we exit the
    93   // tear down function.
    94   var keys = splayTree.exportKeys();
    95   splayTree = null;
    97   // Verify that the splay tree has the right size.
    98   var length = keys.length;
    99   if (length != kSplayTreeSize) {
   100     throw new Error("Splay tree has wrong size");
   101   }
   103   // Verify that the splay tree has sorted, unique keys.
   104   for (var i = 0; i < length - 1; i++) {
   105     if (keys[i] >= keys[i + 1]) {
   106       throw new Error("Splay tree not sorted");
   107     }
   108   }
   109 }
   112 function SplayRun() {
   113   // Replace a few nodes in the splay tree.
   114   for (var i = 0; i < kSplayTreeModifications; i++) {
   115     var key = InsertNewNode();
   116     var greatest = splayTree.findGreatestLessThan(key);
   117     if (greatest == null) splayTree.remove(key);
   118     else splayTree.remove(greatest.key);
   119   }
   120 }
   123 /**
   124  * Constructs a Splay tree.  A splay tree is a self-balancing binary
   125  * search tree with the additional property that recently accessed
   126  * elements are quick to access again. It performs basic operations
   127  * such as insertion, look-up and removal in O(log(n)) amortized time.
   128  *
   129  * @constructor
   130  */
   131 function SplayTree() {
   132 };
   135 /**
   136  * Pointer to the root node of the tree.
   137  *
   138  * @type {SplayTree.Node}
   139  * @private
   140  */
   141 SplayTree.prototype.root_ = null;
   144 /**
   145  * @return {boolean} Whether the tree is empty.
   146  */
   147 SplayTree.prototype.isEmpty = function() {
   148   return !this.root_;
   149 };
   152 /**
   153  * Inserts a node into the tree with the specified key and value if
   154  * the tree does not already contain a node with the specified key. If
   155  * the value is inserted, it becomes the root of the tree.
   156  *
   157  * @param {number} key Key to insert into the tree.
   158  * @param {*} value Value to insert into the tree.
   159  */
   160 SplayTree.prototype.insert = function(key, value) {
   161   if (this.isEmpty()) {
   162     this.root_ = new SplayTree.Node(key, value);
   163     return;
   164   }
   165   // Splay on the key to move the last node on the search path for
   166   // the key to the root of the tree.
   167   this.splay_(key);
   168   if (this.root_.key == key) {
   169     return;
   170   }
   171   var node = new SplayTree.Node(key, value);
   172   if (key > this.root_.key) {
   173     node.left = this.root_;
   174     node.right = this.root_.right;
   175     this.root_.right = null;
   176   } else {
   177     node.right = this.root_;
   178     node.left = this.root_.left;
   179     this.root_.left = null;
   180   }
   181   this.root_ = node;
   182 };
   185 /**
   186  * Removes a node with the specified key from the tree if the tree
   187  * contains a node with this key. The removed node is returned. If the
   188  * key is not found, an exception is thrown.
   189  *
   190  * @param {number} key Key to find and remove from the tree.
   191  * @return {SplayTree.Node} The removed node.
   192  */
   193 SplayTree.prototype.remove = function(key) {
   194   if (this.isEmpty()) {
   195     throw Error('Key not found: ' + key);
   196   }
   197   this.splay_(key);
   198   if (this.root_.key != key) {
   199     throw Error('Key not found: ' + key);
   200   }
   201   var removed = this.root_;
   202   if (!this.root_.left) {
   203     this.root_ = this.root_.right;
   204   } else {
   205     var right = this.root_.right;
   206     this.root_ = this.root_.left;
   207     // Splay to make sure that the new root has an empty right child.
   208     this.splay_(key);
   209     // Insert the original right child as the right child of the new
   210     // root.
   211     this.root_.right = right;
   212   }
   213   return removed;
   214 };
   217 /**
   218  * Returns the node having the specified key or null if the tree doesn't contain
   219  * a node with the specified key.
   220  *
   221  * @param {number} key Key to find in the tree.
   222  * @return {SplayTree.Node} Node having the specified key.
   223  */
   224 SplayTree.prototype.find = function(key) {
   225   if (this.isEmpty()) {
   226     return null;
   227   }
   228   this.splay_(key);
   229   return this.root_.key == key ? this.root_ : null;
   230 };
   233 /**
   234  * @return {SplayTree.Node} Node having the maximum key value.
   235  */
   236 SplayTree.prototype.findMax = function(opt_startNode) {
   237   if (this.isEmpty()) {
   238     return null;
   239   }
   240   var current = opt_startNode || this.root_;
   241   while (current.right) {
   242     current = current.right;
   243   }
   244   return current;
   245 };
   248 /**
   249  * @return {SplayTree.Node} Node having the maximum key value that
   250  *     is less than the specified key value.
   251  */
   252 SplayTree.prototype.findGreatestLessThan = function(key) {
   253   if (this.isEmpty()) {
   254     return null;
   255   }
   256   // Splay on the key to move the node with the given key or the last
   257   // node on the search path to the top of the tree.
   258   this.splay_(key);
   259   // Now the result is either the root node or the greatest node in
   260   // the left subtree.
   261   if (this.root_.key < key) {
   262     return this.root_;
   263   } else if (this.root_.left) {
   264     return this.findMax(this.root_.left);
   265   } else {
   266     return null;
   267   }
   268 };
   271 /**
   272  * @return {Array<*>} An array containing all the keys of tree's nodes.
   273  */
   274 SplayTree.prototype.exportKeys = function() {
   275   var result = [];
   276   if (!this.isEmpty()) {
   277     this.root_.traverse_(function(node) { result.push(node.key); });
   278   }
   279   return result;
   280 };
   283 /**
   284  * Perform the splay operation for the given key. Moves the node with
   285  * the given key to the top of the tree.  If no node has the given
   286  * key, the last node on the search path is moved to the top of the
   287  * tree. This is the simplified top-down splaying algorithm from:
   288  * "Self-adjusting Binary Search Trees" by Sleator and Tarjan
   289  *
   290  * @param {number} key Key to splay the tree on.
   291  * @private
   292  */
   293 SplayTree.prototype.splay_ = function(key) {
   294   if (this.isEmpty()) {
   295     return;
   296   }
   297   // Create a dummy node.  The use of the dummy node is a bit
   298   // counter-intuitive: The right child of the dummy node will hold
   299   // the L tree of the algorithm.  The left child of the dummy node
   300   // will hold the R tree of the algorithm.  Using a dummy node, left
   301   // and right will always be nodes and we avoid special cases.
   302   var dummy, left, right;
   303   dummy = left = right = new SplayTree.Node(null, null);
   304   var current = this.root_;
   305   while (true) {
   306     if (key < current.key) {
   307       if (!current.left) {
   308         break;
   309       }
   310       if (key < current.left.key) {
   311         // Rotate right.
   312         var tmp = current.left;
   313         current.left = tmp.right;
   314         tmp.right = current;
   315         current = tmp;
   316         if (!current.left) {
   317           break;
   318         }
   319       }
   320       // Link right.
   321       right.left = current;
   322       right = current;
   323       current = current.left;
   324     } else if (key > current.key) {
   325       if (!current.right) {
   326         break;
   327       }
   328       if (key > current.right.key) {
   329         // Rotate left.
   330         var tmp = current.right;
   331         current.right = tmp.left;
   332         tmp.left = current;
   333         current = tmp;
   334         if (!current.right) {
   335           break;
   336         }
   337       }
   338       // Link left.
   339       left.right = current;
   340       left = current;
   341       current = current.right;
   342     } else {
   343       break;
   344     }
   345   }
   346   // Assemble.
   347   left.right = current.left;
   348   right.left = current.right;
   349   current.left = dummy.right;
   350   current.right = dummy.left;
   351   this.root_ = current;
   352 };
   355 /**
   356  * Constructs a Splay tree node.
   357  *
   358  * @param {number} key Key.
   359  * @param {*} value Value.
   360  */
   361 SplayTree.Node = function(key, value) {
   362   this.key = key;
   363   this.value = value;
   364 };
   367 /**
   368  * @type {SplayTree.Node}
   369  */
   370 SplayTree.Node.prototype.left = null;
   373 /**
   374  * @type {SplayTree.Node}
   375  */
   376 SplayTree.Node.prototype.right = null;
   379 /**
   380  * Performs an ordered traversal of the subtree starting at
   381  * this SplayTree.Node.
   382  *
   383  * @param {function(SplayTree.Node)} f Visitor function.
   384  * @private
   385  */
   386 SplayTree.Node.prototype.traverse_ = function(f) {
   387   var current = this;
   388   while (current) {
   389     var left = current.left;
   390     if (left) left.traverse_(f);
   391     f(current);
   392     current = current.right;
   393   }
   394 };

mercurial