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.
241 lines
5.6 KiB
241 lines
5.6 KiB
5 years ago
|
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
|