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

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