Home | History | Annotate | Download | only in v8
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 'use strict';
      6 
      7 /**
      8  * @fileoverview Splay tree used by CodeMap.
      9  */
     10 base.exportTo('tracing.importer.v8', function() {
     11   /**
     12    * Constructs a Splay tree.  A splay tree is a self-balancing binary
     13    * search tree with the additional property that recently accessed
     14    * elements are quick to access again. It performs basic operations
     15    * such as insertion, look-up and removal in O(log(n)) amortized time.
     16    *
     17    * @constructor
     18    */
     19   function SplayTree() {
     20   };
     21 
     22 
     23   /**
     24    * Pointer to the root node of the tree.
     25    *
     26    * @type {SplayTree.Node}
     27    * @private
     28    */
     29   SplayTree.prototype.root_ = null;
     30 
     31 
     32   /**
     33    * @return {boolean} Whether the tree is empty.
     34    */
     35   SplayTree.prototype.isEmpty = function() {
     36     return !this.root_;
     37   };
     38 
     39 
     40 
     41   /**
     42    * Inserts a node into the tree with the specified key and value if
     43    * the tree does not already contain a node with the specified key. If
     44    * the value is inserted, it becomes the root of the tree.
     45    *
     46    * @param {number} key Key to insert into the tree.
     47    * @param {*} value Value to insert into the tree.
     48    */
     49   SplayTree.prototype.insert = function(key, value) {
     50     if (this.isEmpty()) {
     51       this.root_ = new SplayTree.Node(key, value);
     52       return;
     53     }
     54     // Splay on the key to move the last node on the search path for
     55     // the key to the root of the tree.
     56     this.splay_(key);
     57     if (this.root_.key == key) {
     58       return;
     59     }
     60     var node = new SplayTree.Node(key, value);
     61     if (key > this.root_.key) {
     62       node.left = this.root_;
     63       node.right = this.root_.right;
     64       this.root_.right = null;
     65     } else {
     66       node.right = this.root_;
     67       node.left = this.root_.left;
     68       this.root_.left = null;
     69     }
     70     this.root_ = node;
     71   };
     72 
     73 
     74   /**
     75    * Removes a node with the specified key from the tree if the tree
     76    * contains a node with this key. The removed node is returned. If the
     77    * key is not found, an exception is thrown.
     78    *
     79    * @param {number} key Key to find and remove from the tree.
     80    * @return {SplayTree.Node} The removed node.
     81    */
     82   SplayTree.prototype.remove = function(key) {
     83     if (this.isEmpty()) {
     84       throw Error('Key not found: ' + key);
     85     }
     86     this.splay_(key);
     87     if (this.root_.key != key) {
     88       throw Error('Key not found: ' + key);
     89     }
     90     var removed = this.root_;
     91     if (!this.root_.left) {
     92       this.root_ = this.root_.right;
     93     } else {
     94       var right = this.root_.right;
     95       this.root_ = this.root_.left;
     96       // Splay to make sure that the new root has an empty right child.
     97       this.splay_(key);
     98       // Insert the original right child as the right child of the new
     99       // root.
    100       this.root_.right = right;
    101     }
    102     return removed;
    103   };
    104 
    105 
    106   /**
    107    * Returns the node having the specified key or null if the tree doesn't
    108    * contain a node with the specified key.
    109    *
    110    *
    111    * @param {number} key Key to find in the tree.
    112    * @return {SplayTree.Node} Node having the specified key.
    113    */
    114   SplayTree.prototype.find = function(key) {
    115     if (this.isEmpty()) {
    116       return null;
    117     }
    118     this.splay_(key);
    119     return this.root_.key == key ? this.root_ : null;
    120   };
    121 
    122 
    123   /**
    124    * @return {SplayTree.Node} Node having the minimum key value.
    125    */
    126   SplayTree.prototype.findMin = function() {
    127     if (this.isEmpty()) {
    128       return null;
    129     }
    130     var current = this.root_;
    131     while (current.left) {
    132       current = current.left;
    133     }
    134     return current;
    135   };
    136 
    137 
    138   /**
    139    * @return {SplayTree.Node} Node having the maximum key value.
    140    */
    141   SplayTree.prototype.findMax = function(opt_startNode) {
    142     if (this.isEmpty()) {
    143       return null;
    144     }
    145     var current = opt_startNode || this.root_;
    146     while (current.right) {
    147       current = current.right;
    148     }
    149     return current;
    150   };
    151 
    152 
    153   /**
    154    * @return {SplayTree.Node} Node having the maximum key value that
    155    *     is less or equal to the specified key value.
    156    */
    157   SplayTree.prototype.findGreatestLessThan = function(key) {
    158     if (this.isEmpty()) {
    159       return null;
    160     }
    161     // Splay on the key to move the node with the given key or the last
    162     // node on the search path to the top of the tree.
    163     this.splay_(key);
    164     // Now the result is either the root node or the greatest node in
    165     // the left subtree.
    166     if (this.root_.key <= key) {
    167       return this.root_;
    168     } else if (this.root_.left) {
    169       return this.findMax(this.root_.left);
    170     } else {
    171       return null;
    172     }
    173   };
    174 
    175 
    176   /**
    177    * @return {Array<*>} An array containing all the values of tree's nodes
    178    * paired with keys.
    179    *
    180    */
    181   SplayTree.prototype.exportKeysAndValues = function() {
    182     var result = [];
    183     this.traverse_(function(node) { result.push([node.key, node.value]); });
    184     return result;
    185   };
    186 
    187 
    188   /**
    189    * @return {Array<*>} An array containing all the values of tree's nodes.
    190    */
    191   SplayTree.prototype.exportValues = function() {
    192     var result = [];
    193     this.traverse_(function(node) { result.push(node.value); });
    194     return result;
    195   };
    196 
    197 
    198   /**
    199    * Perform the splay operation for the given key. Moves the node with
    200    * the given key to the top of the tree.  If no node has the given
    201    * key, the last node on the search path is moved to the top of the
    202    * tree. This is the simplified top-down splaying algorithm from:
    203    * "Self-adjusting Binary Search Trees" by Sleator and Tarjan
    204    *
    205    * @param {number} key Key to splay the tree on.
    206    * @private
    207    */
    208   SplayTree.prototype.splay_ = function(key) {
    209     if (this.isEmpty()) {
    210       return;
    211     }
    212     // Create a dummy node.  The use of the dummy node is a bit
    213     // counter-intuitive: The right child of the dummy node will hold
    214     // the L tree of the algorithm.  The left child of the dummy node
    215     // will hold the R tree of the algorithm.  Using a dummy node, left
    216     // and right will always be nodes and we avoid special cases.
    217     var dummy, left, right;
    218     dummy = left = right = new SplayTree.Node(null, null);
    219     var current = this.root_;
    220     while (true) {
    221       if (key < current.key) {
    222         if (!current.left) {
    223           break;
    224         }
    225         if (key < current.left.key) {
    226           // Rotate right.
    227           var tmp = current.left;
    228           current.left = tmp.right;
    229           tmp.right = current;
    230           current = tmp;
    231           if (!current.left) {
    232             break;
    233           }
    234         }
    235         // Link right.
    236         right.left = current;
    237         right = current;
    238         current = current.left;
    239       } else if (key > current.key) {
    240         if (!current.right) {
    241           break;
    242         }
    243         if (key > current.right.key) {
    244           // Rotate left.
    245           var tmp = current.right;
    246           current.right = tmp.left;
    247           tmp.left = current;
    248           current = tmp;
    249           if (!current.right) {
    250             break;
    251           }
    252         }
    253         // Link left.
    254         left.right = current;
    255         left = current;
    256         current = current.right;
    257       } else {
    258         break;
    259       }
    260     }
    261     // Assemble.
    262     left.right = current.left;
    263     right.left = current.right;
    264     current.left = dummy.right;
    265     current.right = dummy.left;
    266     this.root_ = current;
    267   };
    268 
    269 
    270   /**
    271    * Performs a preorder traversal of the tree.
    272    *
    273    * @param {function(SplayTree.Node)} f Visitor function.
    274    * @private
    275    */
    276   SplayTree.prototype.traverse_ = function(f) {
    277     var nodesToVisit = [this.root_];
    278     while (nodesToVisit.length > 0) {
    279       var node = nodesToVisit.shift();
    280       if (node == null) {
    281         continue;
    282       }
    283       f(node);
    284       nodesToVisit.push(node.left);
    285       nodesToVisit.push(node.right);
    286     }
    287   };
    288 
    289 
    290   /**
    291    * Constructs a Splay tree node.
    292    *
    293    * @param {number} key Key.
    294    * @param {*} value Value.
    295    */
    296   SplayTree.Node = function(key, value) {
    297     this.key = key;
    298     this.value = value;
    299   };
    300 
    301 
    302   /**
    303    * @type {SplayTree.Node}
    304    */
    305   SplayTree.Node.prototype.left = null;
    306 
    307 
    308   /**
    309    * @type {SplayTree.Node}
    310    */
    311   SplayTree.Node.prototype.right = null;
    312 
    313   return {
    314     SplayTree: SplayTree
    315   };
    316 });
    317