UNPKG

5.57 kBJavaScriptView Raw
1var inherits = require('inherits')
2 , AbstractLevelDOWN = require('abstract-leveldown').AbstractLevelDOWN
3 , AbstractIterator = require('abstract-leveldown').AbstractIterator
4 , ltgt = require('ltgt')
5 , setImmediate = global.setImmediate || process.nextTick
6 , createRBT = require('functional-red-black-tree')
7 , globalStore = {}
8
9function toKey (key) {
10 return typeof key == 'string' ? '$' + key : JSON.stringify(key)
11}
12
13function gt(value) {
14 return ltgt.compare(value, this._end) > 0
15}
16
17function gte(value) {
18 return ltgt.compare(value, this._end) >= 0
19}
20
21function lt(value) {
22 return ltgt.compare(value, this._end) < 0
23}
24
25function lte(value) {
26 return ltgt.compare(value, this._end) <= 0
27}
28
29
30function MemIterator (db, options) {
31 AbstractIterator.call(this, db)
32 this._limit = options.limit
33
34 if (this._limit === -1)
35 this._limit = Infinity
36
37 var tree = db._store[db._location];
38
39 this.keyAsBuffer = options.keyAsBuffer !== false
40 this.valueAsBuffer = options.valueAsBuffer !== false
41 this._reverse = options.reverse
42 this._options = options
43 this._done = 0
44
45 if (!this._reverse) {
46 this._incr = 'next';
47 this._start = ltgt.lowerBound(options);
48 this._end = ltgt.upperBound(options)
49
50 if (typeof this._start === 'undefined')
51 this._tree = tree.begin;
52 else if (ltgt.lowerBoundInclusive(options))
53 this._tree = tree.ge(this._start);
54 else
55 this._tree = tree.gt(this._start);
56
57 if (this._end) {
58 if (ltgt.upperBoundInclusive(options))
59 this._test = lte
60 else
61 this._test = lt
62 }
63
64 } else {
65 this._incr = 'prev';
66 this._start = ltgt.upperBound(options)
67 this._end = ltgt.lowerBound(options)
68
69 if (typeof this._start === 'undefined')
70 this._tree = tree.end;
71 else if (ltgt.upperBoundInclusive(options))
72 this._tree = tree.le(this._start)
73 else
74 this._tree = tree.lt(this._start)
75
76 if (this._end) {
77 if (ltgt.lowerBoundInclusive(options))
78 this._test = gte
79 else
80 this._test = gt
81 }
82
83 }
84
85}
86
87inherits(MemIterator, AbstractIterator)
88
89MemIterator.prototype._next = function (callback) {
90 var key
91 , value
92
93 if (this._done++ >= this._limit)
94 return setImmediate(callback)
95
96 if (!this._tree.valid)
97 return setImmediate(callback)
98
99 key = this._tree.key
100 value = this._tree.value
101
102 if (!this._test(key))
103 return setImmediate(callback)
104
105 if (this.keyAsBuffer)
106 key = new Buffer(key)
107
108 if (this.valueAsBuffer)
109 value = new Buffer(value)
110
111 this._tree[this._incr]()
112
113 setImmediate(function callNext() {
114 callback(null, key, value)
115 })
116}
117
118MemIterator.prototype._test = function () {return true}
119
120function MemDOWN (location) {
121 if (!(this instanceof MemDOWN))
122 return new MemDOWN(location)
123
124 AbstractLevelDOWN.call(this, typeof location == 'string' ? location : '')
125
126 this._location = this.location ? toKey(this.location) : '_tree'
127 this._store = this.location ? globalStore: this
128 this._store[this._location] = this._store[this._location] || createRBT()
129}
130
131MemDOWN.clearGlobalStore = function (strict) {
132 if (strict) {
133 Object.keys(globalStore).forEach(function (key) {
134 delete globalStore[key];
135 })
136 } else {
137 globalStore = {}
138 }
139}
140
141inherits(MemDOWN, AbstractLevelDOWN)
142
143MemDOWN.prototype._open = function (options, callback) {
144 var self = this
145 setImmediate(function callNext() { callback(null, self) })
146}
147
148MemDOWN.prototype._put = function (key, value, options, callback) {
149 if (typeof value === 'undefined' || value === null) value = ''
150 this._store[this._location] = this._store[this._location].remove(key).insert(key, value)
151 setImmediate(callback)
152}
153
154MemDOWN.prototype._get = function (key, options, callback) {
155 var value = this._store[this._location].get(key)
156
157 if (value === undefined) {
158 // 'NotFound' error, consistent with LevelDOWN API
159 var err = new Error('NotFound')
160 return setImmediate(function callNext() { callback(err) })
161 }
162
163 if (options.asBuffer !== false && !this._isBuffer(value))
164 value = new Buffer(String(value))
165
166 setImmediate(function callNext () {
167 callback(null, value)
168 })
169
170}
171
172MemDOWN.prototype._del = function (key, options, callback) {
173 this._store[this._location] = this._store[this._location].remove(key)
174 setImmediate(callback)
175}
176
177MemDOWN.prototype._batch = function (array, options, callback) {
178 var err
179 , i = -1
180 , key
181 , value
182 , len = array.length
183 , tree = this._store[this._location]
184
185 while (++i < len) {
186 if (!array[i])
187 continue;
188
189 key = this._isBuffer(array[i].key) ? array[i].key : String(array[i].key)
190 err = this._checkKey(key, 'key')
191 if (err)
192 return setImmediate(function errorCall() { callback(err) })
193
194 tree = tree.remove(array[i].key)
195 // we always remove as insert doesn't replace
196
197 if (array[i].type === 'put') {
198
199 value = this._isBuffer(array[i].value) ? array[i].value : String(array[i].value)
200 err = this._checkKey(value, 'value')
201
202 if (err)
203 return setImmediate(function errorCall() { callback(err) })
204
205 tree = tree.insert(key, value)
206 }
207
208 }
209
210 this._store[this._location] = tree;
211
212 setImmediate(callback)
213}
214
215MemDOWN.prototype._iterator = function (options) {
216 return new MemIterator(this, options)
217}
218
219MemDOWN.prototype._isBuffer = function (obj) {
220 return Buffer.isBuffer(obj)
221}
222
223MemDOWN.destroy = function (name, callback) {
224 var key = toKey(name)
225
226 if (key in globalStore)
227 delete globalStore[key]
228
229 setImmediate(callback)
230}
231
232module.exports = MemDOWN