UNPKG

5.97 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
7
8// In Node, use global.setImmediate. In the browser, use a consistent
9// microtask library to give consistent microtask experience to all browsers
10var setImmediate = require('./immediate')
11var NONE = {}
12
13// TODO (perf): replace ltgt.compare with a simpler, buffer-only comparator
14function gt (value) {
15 return ltgt.compare(value, this._upperBound) > 0
16}
17
18function gte (value) {
19 return ltgt.compare(value, this._upperBound) >= 0
20}
21
22function lt (value) {
23 return ltgt.compare(value, this._upperBound) < 0
24}
25
26function lte (value) {
27 return ltgt.compare(value, this._upperBound) <= 0
28}
29
30function MemIterator (db, options) {
31 AbstractIterator.call(this, db)
32 this._limit = options.limit
33
34 if (this._limit === -1) this._limit = Infinity
35
36 var tree = db._store
37
38 this.keyAsBuffer = options.keyAsBuffer !== false
39 this.valueAsBuffer = options.valueAsBuffer !== false
40 this._reverse = options.reverse
41 this._options = options
42 this._done = 0
43
44 if (!this._reverse) {
45 this._incr = 'next'
46 this._lowerBound = ltgt.lowerBound(options, NONE)
47 this._upperBound = ltgt.upperBound(options, NONE)
48
49 if (this._lowerBound === NONE) {
50 this._tree = tree.begin
51 } else if (ltgt.lowerBoundInclusive(options)) {
52 this._tree = tree.ge(this._lowerBound)
53 } else {
54 this._tree = tree.gt(this._lowerBound)
55 }
56
57 if (this._upperBound !== NONE) {
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._lowerBound = ltgt.upperBound(options, NONE)
67 this._upperBound = ltgt.lowerBound(options, NONE)
68
69 if (this._lowerBound === NONE) {
70 this._tree = tree.end
71 } else if (ltgt.upperBoundInclusive(options)) {
72 this._tree = tree.le(this._lowerBound)
73 } else {
74 this._tree = tree.lt(this._lowerBound)
75 }
76
77 if (this._upperBound !== NONE) {
78 if (ltgt.lowerBoundInclusive(options)) {
79 this._test = gte
80 } else {
81 this._test = gt
82 }
83 }
84 }
85}
86
87inherits(MemIterator, AbstractIterator)
88
89MemIterator.prototype._next = function (callback) {
90 var key
91 var value
92
93 if (this._done++ >= this._limit) return setImmediate(callback)
94 if (!this._tree.valid) return setImmediate(callback)
95
96 key = this._tree.key
97 value = this._tree.value
98
99 if (!this._test(key)) return setImmediate(callback)
100
101 if (!this.keyAsBuffer) {
102 key = key.toString()
103 }
104
105 if (!this.valueAsBuffer) {
106 value = value.toString()
107 }
108
109 this._tree[this._incr]()
110
111 setImmediate(function callNext () {
112 callback(null, key, value)
113 })
114}
115
116MemIterator.prototype._test = function () {
117 return true
118}
119
120MemIterator.prototype._outOfRange = function (target) {
121 if (!this._test(target)) {
122 return true
123 } else if (this._lowerBound === NONE) {
124 return false
125 } else if (!this._reverse) {
126 if (ltgt.lowerBoundInclusive(this._options)) {
127 return ltgt.compare(target, this._lowerBound) < 0
128 } else {
129 return ltgt.compare(target, this._lowerBound) <= 0
130 }
131 } else {
132 if (ltgt.upperBoundInclusive(this._options)) {
133 return ltgt.compare(target, this._lowerBound) > 0
134 } else {
135 return ltgt.compare(target, this._lowerBound) >= 0
136 }
137 }
138}
139
140MemIterator.prototype._seek = function (target) {
141 if (target.length === 0) {
142 throw new Error('cannot seek() to an empty target')
143 }
144
145 if (this._outOfRange(target)) {
146 this._tree = this.db._store.end
147 this._tree.next()
148 } else if (this._reverse) {
149 this._tree = this.db._store.le(target)
150 } else {
151 this._tree = this.db._store.ge(target)
152 }
153}
154
155function MemDOWN () {
156 if (!(this instanceof MemDOWN)) return new MemDOWN()
157
158 AbstractLevelDOWN.call(this)
159
160 this._store = createRBT(ltgt.compare)
161}
162
163inherits(MemDOWN, AbstractLevelDOWN)
164
165MemDOWN.prototype._open = function (options, callback) {
166 var self = this
167 setImmediate(function callNext () {
168 callback(null, self)
169 })
170}
171
172MemDOWN.prototype._serializeKey = function (key) {
173 return Buffer.isBuffer(key) ? key : Buffer.from(String(key))
174}
175
176MemDOWN.prototype._serializeValue = function (value) {
177 return Buffer.isBuffer(value) ? value : Buffer.from(String(value))
178}
179
180MemDOWN.prototype._put = function (key, value, options, callback) {
181 var iter = this._store.find(key)
182
183 if (iter.valid) {
184 this._store = iter.update(value)
185 } else {
186 this._store = this._store.insert(key, value)
187 }
188
189 setImmediate(callback)
190}
191
192MemDOWN.prototype._get = function (key, options, callback) {
193 var value = this._store.get(key)
194
195 if (typeof value === 'undefined') {
196 // 'NotFound' error, consistent with LevelDOWN API
197 return setImmediate(function callNext () {
198 callback(new Error('NotFound'))
199 })
200 }
201
202 if (!options.asBuffer) {
203 value = value.toString()
204 }
205
206 setImmediate(function callNext () {
207 callback(null, value)
208 })
209}
210
211MemDOWN.prototype._del = function (key, options, callback) {
212 this._store = this._store.remove(key)
213 setImmediate(callback)
214}
215
216MemDOWN.prototype._batch = function (array, options, callback) {
217 var i = -1
218 var key
219 var value
220 var iter
221 var len = array.length
222 var tree = this._store
223
224 while (++i < len) {
225 key = array[i].key
226 iter = tree.find(key)
227
228 if (array[i].type === 'put') {
229 value = array[i].value
230 tree = iter.valid ? iter.update(value) : tree.insert(key, value)
231 } else {
232 tree = iter.remove()
233 }
234 }
235
236 this._store = tree
237
238 setImmediate(callback)
239}
240
241MemDOWN.prototype._iterator = function (options) {
242 return new MemIterator(this, options)
243}
244
245module.exports = MemDOWN.default = MemDOWN
246// Exposed for unit tests only
247module.exports.MemIterator = MemIterator