You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
5.6 KiB
240 lines
5.6 KiB
var inherits = require('inherits') |
|
, AbstractLevelDOWN = require('abstract-leveldown').AbstractLevelDOWN |
|
, AbstractIterator = require('abstract-leveldown').AbstractIterator |
|
, ltgt = require('ltgt') |
|
, setImmediate = global.setImmediate || process.nextTick |
|
, createRBT = require('functional-red-black-tree') |
|
, globalStore = {} |
|
|
|
function toKey (key) { |
|
return typeof key == 'string' ? '$' + key : JSON.stringify(key) |
|
} |
|
|
|
function gt(value) { |
|
return ltgt.compare(value, this._end) > 0 |
|
} |
|
|
|
function gte(value) { |
|
return ltgt.compare(value, this._end) >= 0 |
|
} |
|
|
|
function lt(value) { |
|
return ltgt.compare(value, this._end) < 0 |
|
} |
|
|
|
function lte(value) { |
|
return ltgt.compare(value, this._end) <= 0 |
|
} |
|
|
|
|
|
function MemIterator (db, options) { |
|
AbstractIterator.call(this, db) |
|
this._limit = options.limit |
|
|
|
if (this._limit === -1) |
|
this._limit = Infinity |
|
|
|
var tree = db._store[db._location]; |
|
|
|
this.keyAsBuffer = options.keyAsBuffer !== false |
|
this.valueAsBuffer = options.valueAsBuffer !== false |
|
this._reverse = options.reverse |
|
this._options = options |
|
this._done = 0 |
|
|
|
if (!this._reverse) { |
|
this._incr = 'next'; |
|
this._start = ltgt.lowerBound(options); |
|
this._end = ltgt.upperBound(options) |
|
|
|
if (typeof this._start === 'undefined') |
|
this._tree = tree.begin; |
|
else if (ltgt.lowerBoundInclusive(options)) |
|
this._tree = tree.ge(this._start); |
|
else |
|
this._tree = tree.gt(this._start); |
|
|
|
if (this._end) { |
|
if (ltgt.upperBoundInclusive(options)) |
|
this._test = lte |
|
else |
|
this._test = lt |
|
} |
|
|
|
} else { |
|
this._incr = 'prev'; |
|
this._start = ltgt.upperBound(options) |
|
this._end = ltgt.lowerBound(options) |
|
|
|
if (typeof this._start === 'undefined') |
|
this._tree = tree.end; |
|
else if (ltgt.upperBoundInclusive(options)) |
|
this._tree = tree.le(this._start) |
|
else |
|
this._tree = tree.lt(this._start) |
|
|
|
if (this._end) { |
|
if (ltgt.lowerBoundInclusive(options)) |
|
this._test = gte |
|
else |
|
this._test = gt |
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
inherits(MemIterator, AbstractIterator) |
|
|
|
MemIterator.prototype._next = function (callback) { |
|
var key |
|
, value |
|
|
|
if (this._done++ >= this._limit) |
|
return setImmediate(callback) |
|
|
|
if (!this._tree.valid) |
|
return setImmediate(callback) |
|
|
|
key = this._tree.key |
|
value = this._tree.value |
|
|
|
if (!this._test(key)) |
|
return setImmediate(callback) |
|
|
|
if (this.keyAsBuffer) |
|
key = new Buffer(key) |
|
|
|
if (this.valueAsBuffer) |
|
value = new Buffer(value) |
|
|
|
this._tree[this._incr]() |
|
|
|
setImmediate(function callNext() { |
|
callback(null, key, value) |
|
}) |
|
} |
|
|
|
MemIterator.prototype._test = function () {return true} |
|
|
|
function MemDOWN (location) { |
|
if (!(this instanceof MemDOWN)) |
|
return new MemDOWN(location) |
|
|
|
AbstractLevelDOWN.call(this, typeof location == 'string' ? location : '') |
|
|
|
this._location = this.location ? toKey(this.location) : '_tree' |
|
this._store = this.location ? globalStore: this |
|
this._store[this._location] = this._store[this._location] || createRBT(ltgt.compare) |
|
} |
|
|
|
MemDOWN.clearGlobalStore = function (strict) { |
|
if (strict) { |
|
Object.keys(globalStore).forEach(function (key) { |
|
delete globalStore[key]; |
|
}) |
|
} else { |
|
globalStore = {} |
|
} |
|
} |
|
|
|
inherits(MemDOWN, AbstractLevelDOWN) |
|
|
|
MemDOWN.prototype._open = function (options, callback) { |
|
var self = this |
|
setImmediate(function callNext() { callback(null, self) }) |
|
} |
|
|
|
MemDOWN.prototype._put = function (key, value, options, callback) { |
|
if (typeof value === 'undefined' || value === null) value = '' |
|
|
|
var iter = this._store[this._location].find(key) |
|
|
|
if (iter.valid) { |
|
this._store[this._location] = iter.update(value) |
|
} else { |
|
this._store[this._location] = this._store[this._location].insert(key, value) |
|
} |
|
|
|
setImmediate(callback) |
|
} |
|
|
|
MemDOWN.prototype._get = function (key, options, callback) { |
|
var value = this._store[this._location].get(key) |
|
|
|
if (value === undefined) { |
|
// 'NotFound' error, consistent with LevelDOWN API |
|
var err = new Error('NotFound') |
|
return setImmediate(function callNext() { callback(err) }) |
|
} |
|
|
|
if (options.asBuffer !== false && !this._isBuffer(value)) |
|
value = new Buffer(String(value)) |
|
|
|
setImmediate(function callNext () { |
|
callback(null, value) |
|
}) |
|
|
|
} |
|
|
|
MemDOWN.prototype._del = function (key, options, callback) { |
|
this._store[this._location] = this._store[this._location].remove(key) |
|
setImmediate(callback) |
|
} |
|
|
|
MemDOWN.prototype._batch = function (array, options, callback) { |
|
var err |
|
, i = -1 |
|
, key |
|
, value |
|
, iter |
|
, len = array.length |
|
, tree = this._store[this._location] |
|
|
|
while (++i < len) { |
|
if (!array[i]) |
|
continue; |
|
|
|
key = this._isBuffer(array[i].key) ? array[i].key : String(array[i].key) |
|
err = this._checkKey(key, 'key') |
|
if (err) |
|
return setImmediate(function errorCall() { callback(err) }) |
|
|
|
iter = tree.find(key) |
|
|
|
if (array[i].type === 'put') { |
|
value = this._isBuffer(array[i].value) ? array[i].value : String(array[i].value) |
|
err = this._checkKey(value, 'value') |
|
|
|
if (err) |
|
return setImmediate(function errorCall() { callback(err) }) |
|
|
|
tree = iter.valid ? iter.update(value) : tree.insert(key, value) |
|
} else { |
|
tree = iter.remove() |
|
} |
|
} |
|
|
|
this._store[this._location] = tree; |
|
|
|
setImmediate(callback) |
|
} |
|
|
|
MemDOWN.prototype._iterator = function (options) { |
|
return new MemIterator(this, options) |
|
} |
|
|
|
MemDOWN.prototype._isBuffer = function (obj) { |
|
return Buffer.isBuffer(obj) |
|
} |
|
|
|
MemDOWN.destroy = function (name, callback) { |
|
var key = toKey(name) |
|
|
|
if (key in globalStore) |
|
delete globalStore[key] |
|
|
|
setImmediate(callback) |
|
} |
|
|
|
module.exports = MemDOWN
|
|
|