UNPKG

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