bst refactor. comments.

This commit is contained in:
Christopher Jeffrey 2016-06-22 04:04:58 -07:00
parent 4d823742c3
commit 135fca806c
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

@ -1,7 +1,6 @@
/*! /*!
* bst.js - iterative binary search tree for bcoin * bst.js - iterative binary search tree for bcoin
* Copyright (c) 2014-2015, Fedor Indutny (MIT License) * Copyright (c) 2016, Christopher Jeffrey (MIT License).
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin * https://github.com/bcoin-org/bcoin
*/ */
@ -12,8 +11,10 @@ var assert = utils.assert;
var DUMMY = new Buffer([0]); var DUMMY = new Buffer([0]);
/** /**
* An iterative in-memory binary search tree. Used for the mempool. * An iterative binary search tree.
* Many of its options, parameters and methods mimic the leveldown interface. * Used for the mempool. Many of its
* options, parameters, and methods
* mimic the leveldown interface.
* @exports BST * @exports BST
* @constructor * @constructor
* @param {String?} location - Phony location. * @param {String?} location - Phony location.
@ -35,7 +36,7 @@ function BST(location, options) {
/** /**
* Do a key lookup. * Do a key lookup.
* @param {String|Buffer} key * @param {Buffer|String} key
* @returns {Buffer?} value * @returns {Buffer?} value
*/ */
@ -59,14 +60,14 @@ BST.prototype.search = function search(key) {
/** /**
* Insert a record. * Insert a record.
* @param {String|Buffer} key * @param {Buffer|String} key
* @param {Buffer} value * @param {Buffer} value
*/ */
BST.prototype.insert = function insert(key, value) { BST.prototype.insert = function insert(key, value) {
var current = this.root; var current = this.root;
var left = false; var left = false;
var parent, cmp; var parent, cmp, node;
if (typeof key === 'string') if (typeof key === 'string')
key = new Buffer(key, 'ascii'); key = new Buffer(key, 'ascii');
@ -93,15 +94,17 @@ BST.prototype.insert = function insert(key, value) {
} }
} }
node = new BSTNode(key, value);
if (!parent) { if (!parent) {
this.root = { key: key, value: value }; this.root = node;
return; return;
} }
if (left) if (left)
parent.left = { key: key, value: value }; parent.left = node;
else else
parent.right = { key: key, value: value }; parent.right = node;
}; };
/** /**
@ -191,7 +194,7 @@ BST.prototype.remove = function remove(key) {
/** /**
* Take a snapshot and return a cloned root node. * Take a snapshot and return a cloned root node.
* @return {Object} * @returns {BSTNode}
*/ */
BST.prototype.snapshot = function snapshot() { BST.prototype.snapshot = function snapshot() {
@ -203,13 +206,13 @@ BST.prototype.snapshot = function snapshot() {
for (;;) { for (;;) {
if (current) { if (current) {
if (left) { if (left) {
copy = clone(current); copy = current.clone();
if (parent) if (parent)
parent.left = copy; parent.left = copy;
else else
snapshot = copy; snapshot = copy;
} else { } else {
copy = clone(current); copy = current.clone();
if (parent) if (parent)
parent.right = copy; parent.right = copy;
else else
@ -237,7 +240,7 @@ BST.prototype.snapshot = function snapshot() {
/** /**
* Traverse the key and filter records. * Traverse the key and filter records.
* @param {Function} test * @param {Function} test
* @returns {Object[]} Matched records. * @returns {BSTNode[]} Records.
*/ */
BST.prototype.traverse = function traverse(test) { BST.prototype.traverse = function traverse(test) {
@ -247,12 +250,8 @@ BST.prototype.traverse = function traverse(test) {
for (;;) { for (;;) {
if (current) { if (current) {
if (test(current)) { if (test(current))
items.push({ items.push(current.copy());
key: current.key,
value: current.value
});
}
stack.push(current); stack.push(current);
current = current.left; current = current.left;
continue; continue;
@ -270,7 +269,7 @@ BST.prototype.traverse = function traverse(test) {
/** /**
* Dump all records. * Dump all records.
* @returns {Object[]} Records. * @returns {BSTNode[]} Records.
*/ */
BST.prototype.dump = function dump() { BST.prototype.dump = function dump() {
@ -281,7 +280,7 @@ BST.prototype.dump = function dump() {
* Traverse between a range of keys and collect records. * Traverse between a range of keys and collect records.
* @param {Buffer} gte * @param {Buffer} gte
* @param {Buffer} lte * @param {Buffer} lte
* @returns {Object[]} records * @returns {BSTNode[]} Records.
*/ */
BST.prototype.range = function range(gte, lte) { BST.prototype.range = function range(gte, lte) {
@ -300,10 +299,7 @@ BST.prototype.range = function range(gte, lte) {
if (current) { if (current) {
cmp = this.rangeCompare(current.key, gte, lte); cmp = this.rangeCompare(current.key, gte, lte);
if (cmp === 0) { if (cmp === 0) {
items.push({ items.push(current.copy());
key: current.key,
value: current.value
});
stack.push(current); stack.push(current);
} }
if (cmp <= 0) if (cmp <= 0)
@ -513,15 +509,22 @@ BST.prototype.getProperty = function getProperty(name) {
/** /**
* Calculate approximate database size (leveldown method). * Calculate approximate database size (leveldown method).
* @param {String} start - Start key. * @param {Buffer|String} start - Start key.
* @param {String} end - End key. * @param {Buffer|String} end - End key.
* @param {Function} callback - Returns [Error, Number]. * @param {Function} callback - Returns [Error, Number].
*/ */
BST.prototype.approximateSize = function approximateSize(start, end, callback) { BST.prototype.approximateSize = function approximateSize(start, end, callback) {
var size = this.range(start, end).reduce(function(total, item) { var items = this.range(start, end);
return total + item.key.length + item.value.length; var size = 0;
}, 0); var i, item;
for (i = 0; i < items.length; i++) {
item = items[i];
size += item.key.length;
size += item.value.length;
}
return utils.asyncify(callback)(null, size); return utils.asyncify(callback)(null, size);
}; };
@ -545,8 +548,52 @@ BST.repair = function repair(location, callback) {
return utils.nextTick(callback); return utils.nextTick(callback);
}; };
/* /**
* BST Node
* @constructor
* @private
* @param {Buffer} key
* @param {Buffer} value
* @property {Buffer} key
* @property {Buffer} value
* @property {BSTNode|null} left
* @property {BSTNode|null} right
*/
function BSTNode(key, value) {
this.key = key;
this.value = value;
this.left = null;
this.right = null;
}
/**
* Clone the node.
* @returns {BSTNode}
*/
BSTNode.prototype.clone = function clone() {
var node = new BSTNode(this.key, this.value);
node.left = this.left;
node.right = this.right;
return node;
};
/**
* Clone the node (key/value only).
* @returns {BSTNode}
*/
BSTNode.prototype.copy = function copy() {
return new BSTNode(this.key, this.value);
};
/**
* Batch * Batch
* @constructor
* @private
* @param {BST} tree
* @param {Object?} options
*/ */
function Batch(tree, options) { function Batch(tree, options) {
@ -555,32 +602,49 @@ function Batch(tree, options) {
this.tree = tree; this.tree = tree;
} }
/**
* Insert a record.
* @param {Buffer|String} key
* @param {Buffer} value
*/
Batch.prototype.put = function(key, value) { Batch.prototype.put = function(key, value) {
assert(this.tree, 'Already written.'); assert(this.tree, 'Already written.');
this.ops.push({ type: 'put', key: key, value: value }); this.ops.push({ type: 'put', key: key, value: value });
return this; return this;
}; };
/**
* Remove a record.
* @param {Buffer|String} key
*/
Batch.prototype.del = function del(key) { Batch.prototype.del = function del(key) {
assert(this.tree, 'Already written.'); assert(this.tree, 'Already written.');
this.ops.push({ type: 'del', key: key }); this.ops.push({ type: 'del', key: key });
return this; return this;
}; };
/**
* Commit the batch.
* @param {Function} callback
*/
Batch.prototype.write = function write(callback) { Batch.prototype.write = function write(callback) {
var self = this; var i, op;
if (!this.tree) if (!this.tree)
return utils.asyncify(callback)(new Error('Already written.')); return callback(new Error('Already written.'));
this.ops.forEach(function(op) { for (i = 0; i < this.ops.length; i++) {
op = this.ops[i];
if (op.type === 'put') if (op.type === 'put')
self.tree.insert(op.key, op.value); this.tree.insert(op.key, op.value);
else if (op.type === 'del') else if (op.type === 'del')
self.tree.remove(op.key); this.tree.remove(op.key);
else else
assert(false); assert(false);
}); }
this.ops.length = 0; this.ops.length = 0;
this.ops = null; this.ops = null;
@ -592,14 +656,22 @@ Batch.prototype.write = function write(callback) {
return this; return this;
}; };
/**
* Clear batch of all ops.
*/
Batch.prototype.clear = function clear() { Batch.prototype.clear = function clear() {
assert(this.tree, 'Already written.'); assert(this.tree, 'Already written.');
this.ops.length = 0; this.ops.length = 0;
return this; return this;
}; };
/* /**
* Iterator * Iterator
* @constructor
* @private
* @param {BST} tree
* @param {Object?} options
*/ */
function Iterator(tree, options) { function Iterator(tree, options) {
@ -622,11 +694,16 @@ function Iterator(tree, options) {
this.tree = tree; this.tree = tree;
this.ended = false; this.ended = false;
this.items = this.tree.range(this.options.gte, this.options.lte); this.snapshot = this.tree.range(this.options.gte, this.options.lte);
this.index = this.options.reverse ? this.items.length - 1 : 0; this.index = this.options.reverse ? this.snapshot.length - 1 : 0;
this.total = 0; this.total = 0;
} }
/**
* Seek to the next key.
* @param {Function} callback
*/
Iterator.prototype.next = function(callback) { Iterator.prototype.next = function(callback) {
var item, key, value; var item, key, value;
@ -634,9 +711,9 @@ Iterator.prototype.next = function(callback) {
return utils.asyncify(callback)(new Error('Cannot call next after end.')); return utils.asyncify(callback)(new Error('Cannot call next after end.'));
if (this.options.reverse) if (this.options.reverse)
item = this.items[this.index--]; item = this.snapshot[this.index--];
else else
item = this.items[this.index++]; item = this.snapshot[this.index++];
if (this.options.limit != null) { if (this.options.limit != null) {
if (this.total++ >= this.options.limit) { if (this.total++ >= this.options.limit) {
@ -668,6 +745,11 @@ Iterator.prototype.next = function(callback) {
utils.asyncify(callback)(null, key, value); utils.asyncify(callback)(null, key, value);
}; };
/**
* Seek to a key gte to `key`.
* @param {String|Buffer} key
*/
Iterator.prototype.seek = function seek(key) { Iterator.prototype.seek = function seek(key) {
var self = this; var self = this;
@ -676,20 +758,30 @@ Iterator.prototype.seek = function seek(key) {
if (typeof key === 'string') if (typeof key === 'string')
key = new Buffer(key, 'ascii'); key = new Buffer(key, 'ascii');
this.index = utils.binarySearch(this.items, key, function(a, b) { this.index = utils.binarySearch(this.snapshot, key, function(a, b) {
return self.tree.compare(a.key, b); return self.tree.compare(a.key, b);
}, true); }, true);
}; };
/**
* Clean up the iterator.
* @private
*/
Iterator.prototype._end = function end() { Iterator.prototype._end = function end() {
if (!this.tree) if (!this.tree)
return; return;
this.tree = null; this.tree = null;
this.items.length = 0; this.snapshot.length = 0;
this.items = null; this.snapshot = null;
}; };
/**
* End the iterator. Free up snapshot.
* @param {Buffer} callback
*/
Iterator.prototype.end = function end(callback) { Iterator.prototype.end = function end(callback) {
if (this.ended) if (this.ended)
return utils.asyncify(callback)(new Error('Already ended.')); return utils.asyncify(callback)(new Error('Already ended.'));
@ -700,19 +792,6 @@ Iterator.prototype.end = function end(callback) {
return utils.nextTick(callback); return utils.nextTick(callback);
}; };
/*
* Helpers
*/
function clone(node) {
return {
key: node.key,
value: node.value,
left: node.left,
right: node.right
};
}
/* /*
* Expose * Expose
*/ */