tor-browser

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

splay.js (11557B)


      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.
     27 
     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.
     35 
     36 var Splay = new BenchmarkSuite('Splay', [81491, 2739514], [
     37  new Benchmark("Splay", true, false, 1400,
     38    SplayRun, SplaySetup, SplayTearDown, SplayRMS)
     39 ]);
     40 
     41 
     42 // Configuration.
     43 var kSplayTreeSize = 8000;
     44 var kSplayTreeModifications = 80;
     45 var kSplayTreePayloadDepth = 5;
     46 
     47 var splayTree = null;
     48 var splaySampleTimeStart = 0.0;
     49 
     50 function GeneratePayloadTree(depth, tag) {
     51  if (depth == 0) {
     52    return {
     53      array  : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
     54      string : 'String for key ' + tag + ' in leaf node'
     55    };
     56  } else {
     57    return {
     58      left:  GeneratePayloadTree(depth - 1, tag),
     59      right: GeneratePayloadTree(depth - 1, tag)
     60    };
     61  }
     62 }
     63 
     64 
     65 function GenerateKey() {
     66  // The benchmark framework guarantees that Math.random is
     67  // deterministic; see base.js.
     68  return Math.random();
     69 }
     70 
     71 var splaySamples = 0;
     72 var splaySumOfSquaredPauses = 0;
     73 
     74 function SplayRMS() {
     75  return Math.round(Math.sqrt(splaySumOfSquaredPauses / splaySamples) * 10000);
     76 }
     77 
     78 function SplayUpdateStats(time) {
     79  var pause = time - splaySampleTimeStart;
     80  splaySampleTimeStart = time;
     81  splaySamples++;
     82  splaySumOfSquaredPauses += pause * pause;
     83 }
     84 
     85 function InsertNewNode() {
     86  // Insert new node with a unique key.
     87  var key;
     88  do {
     89    key = GenerateKey();
     90  } while (splayTree.find(key) != null);
     91  var payload = GeneratePayloadTree(kSplayTreePayloadDepth, String(key));
     92  splayTree.insert(key, payload);
     93  return key;
     94 }
     95 
     96 
     97 function SplaySetup() {
     98  // Check if the platform has the performance.now high resolution timer.
     99  // If not, throw exception and quit.
    100  if (!performance.now) {
    101    throw "PerformanceNowUnsupported";
    102  }
    103 
    104  splayTree = new SplayTree();
    105  splaySampleTimeStart = performance.now()
    106  for (var i = 0; i < kSplayTreeSize; i++) {
    107    InsertNewNode();
    108    if ((i+1) % 20 == 19) {
    109      SplayUpdateStats(performance.now());
    110    }
    111  }
    112 }
    113 
    114 
    115 function SplayTearDown() {
    116  // Allow the garbage collector to reclaim the memory
    117  // used by the splay tree no matter how we exit the
    118  // tear down function.
    119  var keys = splayTree.exportKeys();
    120  splayTree = null;
    121 
    122  splaySamples = 0;
    123  splaySumOfSquaredPauses = 0;
    124 
    125  // Verify that the splay tree has the right size.
    126  var length = keys.length;
    127  if (length != kSplayTreeSize) {
    128    throw new Error("Splay tree has wrong size");
    129  }
    130 
    131  // Verify that the splay tree has sorted, unique keys.
    132  for (var i = 0; i < length - 1; i++) {
    133    if (keys[i] >= keys[i + 1]) {
    134      throw new Error("Splay tree not sorted");
    135    }
    136  }
    137 }
    138 
    139 
    140 function SplayRun() {
    141  // Replace a few nodes in the splay tree.
    142  for (var i = 0; i < kSplayTreeModifications; i++) {
    143    var key = InsertNewNode();
    144    var greatest = splayTree.findGreatestLessThan(key);
    145    if (greatest == null) splayTree.remove(key);
    146    else splayTree.remove(greatest.key);
    147  }
    148  SplayUpdateStats(performance.now());
    149 }
    150 
    151 
    152 /**
    153 * Constructs a Splay tree.  A splay tree is a self-balancing binary
    154 * search tree with the additional property that recently accessed
    155 * elements are quick to access again. It performs basic operations
    156 * such as insertion, look-up and removal in O(log(n)) amortized time.
    157 *
    158 * @constructor
    159 */
    160 function SplayTree() {
    161 };
    162 
    163 
    164 /**
    165 * Pointer to the root node of the tree.
    166 *
    167 * @type {SplayTree.Node}
    168 * @private
    169 */
    170 SplayTree.prototype.root_ = null;
    171 
    172 
    173 /**
    174 * @return {boolean} Whether the tree is empty.
    175 */
    176 SplayTree.prototype.isEmpty = function() {
    177  return !this.root_;
    178 };
    179 
    180 
    181 /**
    182 * Inserts a node into the tree with the specified key and value if
    183 * the tree does not already contain a node with the specified key. If
    184 * the value is inserted, it becomes the root of the tree.
    185 *
    186 * @param {number} key Key to insert into the tree.
    187 * @param {*} value Value to insert into the tree.
    188 */
    189 SplayTree.prototype.insert = function(key, value) {
    190  if (this.isEmpty()) {
    191    this.root_ = new SplayTree.Node(key, value);
    192    return;
    193  }
    194  // Splay on the key to move the last node on the search path for
    195  // the key to the root of the tree.
    196  this.splay_(key);
    197  if (this.root_.key == key) {
    198    return;
    199  }
    200  var node = new SplayTree.Node(key, value);
    201  if (key > this.root_.key) {
    202    node.left = this.root_;
    203    node.right = this.root_.right;
    204    this.root_.right = null;
    205  } else {
    206    node.right = this.root_;
    207    node.left = this.root_.left;
    208    this.root_.left = null;
    209  }
    210  this.root_ = node;
    211 };
    212 
    213 
    214 /**
    215 * Removes a node with the specified key from the tree if the tree
    216 * contains a node with this key. The removed node is returned. If the
    217 * key is not found, an exception is thrown.
    218 *
    219 * @param {number} key Key to find and remove from the tree.
    220 * @return {SplayTree.Node} The removed node.
    221 */
    222 SplayTree.prototype.remove = function(key) {
    223  if (this.isEmpty()) {
    224    throw Error('Key not found: ' + key);
    225  }
    226  this.splay_(key);
    227  if (this.root_.key != key) {
    228    throw Error('Key not found: ' + key);
    229  }
    230  var removed = this.root_;
    231  if (!this.root_.left) {
    232    this.root_ = this.root_.right;
    233  } else {
    234    var right = this.root_.right;
    235    this.root_ = this.root_.left;
    236    // Splay to make sure that the new root has an empty right child.
    237    this.splay_(key);
    238    // Insert the original right child as the right child of the new
    239    // root.
    240    this.root_.right = right;
    241  }
    242  return removed;
    243 };
    244 
    245 
    246 /**
    247 * Returns the node having the specified key or null if the tree doesn't contain
    248 * a node with the specified key.
    249 *
    250 * @param {number} key Key to find in the tree.
    251 * @return {SplayTree.Node} Node having the specified key.
    252 */
    253 SplayTree.prototype.find = function(key) {
    254  if (this.isEmpty()) {
    255    return null;
    256  }
    257  this.splay_(key);
    258  return this.root_.key == key ? this.root_ : null;
    259 };
    260 
    261 
    262 /**
    263 * @return {SplayTree.Node} Node having the maximum key value.
    264 */
    265 SplayTree.prototype.findMax = function(opt_startNode) {
    266  if (this.isEmpty()) {
    267    return null;
    268  }
    269  var current = opt_startNode || this.root_;
    270  while (current.right) {
    271    current = current.right;
    272  }
    273  return current;
    274 };
    275 
    276 
    277 /**
    278 * @return {SplayTree.Node} Node having the maximum key value that
    279 *     is less than the specified key value.
    280 */
    281 SplayTree.prototype.findGreatestLessThan = function(key) {
    282  if (this.isEmpty()) {
    283    return null;
    284  }
    285  // Splay on the key to move the node with the given key or the last
    286  // node on the search path to the top of the tree.
    287  this.splay_(key);
    288  // Now the result is either the root node or the greatest node in
    289  // the left subtree.
    290  if (this.root_.key < key) {
    291    return this.root_;
    292  } else if (this.root_.left) {
    293    return this.findMax(this.root_.left);
    294  } else {
    295    return null;
    296  }
    297 };
    298 
    299 
    300 /**
    301 * @return {Array<*>} An array containing all the keys of tree's nodes.
    302 */
    303 SplayTree.prototype.exportKeys = function() {
    304  var result = [];
    305  if (!this.isEmpty()) {
    306    this.root_.traverse_(function(node) { result.push(node.key); });
    307  }
    308  return result;
    309 };
    310 
    311 
    312 /**
    313 * Perform the splay operation for the given key. Moves the node with
    314 * the given key to the top of the tree.  If no node has the given
    315 * key, the last node on the search path is moved to the top of the
    316 * tree. This is the simplified top-down splaying algorithm from:
    317 * "Self-adjusting Binary Search Trees" by Sleator and Tarjan
    318 *
    319 * @param {number} key Key to splay the tree on.
    320 * @private
    321 */
    322 SplayTree.prototype.splay_ = function(key) {
    323  if (this.isEmpty()) {
    324    return;
    325  }
    326  // Create a dummy node.  The use of the dummy node is a bit
    327  // counter-intuitive: The right child of the dummy node will hold
    328  // the L tree of the algorithm.  The left child of the dummy node
    329  // will hold the R tree of the algorithm.  Using a dummy node, left
    330  // and right will always be nodes and we avoid special cases.
    331  var dummy, left, right;
    332  dummy = left = right = new SplayTree.Node(null, null);
    333  var current = this.root_;
    334  while (true) {
    335    if (key < current.key) {
    336      if (!current.left) {
    337        break;
    338      }
    339      if (key < current.left.key) {
    340        // Rotate right.
    341        var tmp = current.left;
    342        current.left = tmp.right;
    343        tmp.right = current;
    344        current = tmp;
    345        if (!current.left) {
    346          break;
    347        }
    348      }
    349      // Link right.
    350      right.left = current;
    351      right = current;
    352      current = current.left;
    353    } else if (key > current.key) {
    354      if (!current.right) {
    355        break;
    356      }
    357      if (key > current.right.key) {
    358        // Rotate left.
    359        var tmp = current.right;
    360        current.right = tmp.left;
    361        tmp.left = current;
    362        current = tmp;
    363        if (!current.right) {
    364          break;
    365        }
    366      }
    367      // Link left.
    368      left.right = current;
    369      left = current;
    370      current = current.right;
    371    } else {
    372      break;
    373    }
    374  }
    375  // Assemble.
    376  left.right = current.left;
    377  right.left = current.right;
    378  current.left = dummy.right;
    379  current.right = dummy.left;
    380  this.root_ = current;
    381 };
    382 
    383 
    384 /**
    385 * Constructs a Splay tree node.
    386 *
    387 * @param {number} key Key.
    388 * @param {*} value Value.
    389 */
    390 SplayTree.Node = function(key, value) {
    391  this.key = key;
    392  this.value = value;
    393 };
    394 
    395 
    396 /**
    397 * @type {SplayTree.Node}
    398 */
    399 SplayTree.Node.prototype.left = null;
    400 
    401 
    402 /**
    403 * @type {SplayTree.Node}
    404 */
    405 SplayTree.Node.prototype.right = null;
    406 
    407 
    408 /**
    409 * Performs an ordered traversal of the subtree starting at
    410 * this SplayTree.Node.
    411 *
    412 * @param {function(SplayTree.Node)} f Visitor function.
    413 * @private
    414 */
    415 SplayTree.Node.prototype.traverse_ = function(f) {
    416  var current = this;
    417  while (current) {
    418    var left = current.left;
    419    if (left) left.traverse_(f);
    420    f(current);
    421    current = current.right;
    422  }
    423 };