UNPKG

5.49 kBJavaScriptView Raw
1var inherits = require('inherits')
2 , AbstractLevelDOWN = require('abstract-leveldown').AbstractLevelDOWN
3 , AbstractIterator = require('abstract-leveldown').AbstractIterator
4 , ltgt = require('ltgt')
5 , createRBT = require('functional-red-black-tree')
6 , globalStore = {}
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')
11
12function gt(value) {
13 return ltgt.compare(value, this._end) > 0
14}
15
16function gte(value) {
17 return ltgt.compare(value, this._end) >= 0
18}
19
20function lt(value) {
21 return ltgt.compare(value, this._end) < 0
22}
23
24function lte(value) {
25 return ltgt.compare(value, this._end) <= 0
26}
27
28
29function MemIterator (db, options) {
30 AbstractIterator.call(this, db)
31 this._limit = options.limit
32
33 if (this._limit === -1)
34 this._limit = Infinity
35
36 var tree = db._store[db._location]
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._start = ltgt.lowerBound(options)
47 this._end = ltgt.upperBound(options)
48
49 if (typeof this._start === 'undefined')
50 this._tree = tree.begin
51 else if (ltgt.lowerBoundInclusive(options))
52 this._tree = tree.ge(this._start)
53 else
54 this._tree = tree.gt(this._start)
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 if (this._end) {
76 if (ltgt.lowerBoundInclusive(options))
77 this._test = gte
78 else
79 this._test = gt
80 }
81
82 }
83
84}
85
86inherits(MemIterator, AbstractIterator)
87
88MemIterator.prototype._next = function (callback) {
89 var key
90 , value
91
92 if (this._done++ >= this._limit)
93 return setImmediate(callback)
94
95 if (!this._tree.valid)
96 return setImmediate(callback)
97
98 key = this._tree.key
99 value = this._tree.value
100
101 if (!this._test(key))
102 return setImmediate(callback)
103
104 if (this.keyAsBuffer)
105 key = new Buffer(key)
106
107 if (this.valueAsBuffer)
108 value = new Buffer(value)
109
110 this._tree[this._incr]()
111
112 setImmediate(function callNext() {
113 callback(null, key, value)
114 })
115}
116
117MemIterator.prototype._test = function () {return true}
118
119function MemDOWN (location) {
120 if (!(this instanceof MemDOWN))
121 return new MemDOWN(location)
122
123 AbstractLevelDOWN.call(this, typeof location == 'string' ? location : '')
124
125 this._location = this.location ? ('$' + this.location) : '_tree'
126 this._store = this.location ? globalStore: this
127 this._store[this._location] = this._store[this._location] || createRBT(ltgt.compare)
128}
129
130MemDOWN.clearGlobalStore = function (strict) {
131 if (strict) {
132 Object.keys(globalStore).forEach(function (key) {
133 delete globalStore[key]
134 })
135 } else {
136 globalStore = {}
137 }
138}
139
140inherits(MemDOWN, AbstractLevelDOWN)
141
142MemDOWN.prototype._open = function (options, callback) {
143 var self = this
144 setImmediate(function callNext() { callback(null, self) })
145}
146
147MemDOWN.prototype._put = function (key, value, options, callback) {
148 if (typeof value === 'undefined' || value === null) value = ''
149
150 var iter = this._store[this._location].find(key)
151
152 if (iter.valid) {
153 this._store[this._location] = iter.update(value)
154 } else {
155 this._store[this._location] = this._store[this._location].insert(key, value)
156 }
157
158 setImmediate(callback)
159}
160
161MemDOWN.prototype._get = function (key, options, callback) {
162 var value = this._store[this._location].get(key)
163
164 if (typeof value === 'undefined') {
165 // 'NotFound' error, consistent with LevelDOWN API
166 return setImmediate(function callNext() { callback(new Error('NotFound')) })
167 }
168
169 if (options.asBuffer !== false && !this._isBuffer(value))
170 value = new Buffer(String(value))
171
172 setImmediate(function callNext () {
173 callback(null, value)
174 })
175
176}
177
178MemDOWN.prototype._del = function (key, options, callback) {
179 this._store[this._location] = this._store[this._location].remove(key)
180 setImmediate(callback)
181}
182
183MemDOWN.prototype._batch = function (array, options, callback) {
184 var i = -1
185 , key
186 , value
187 , iter
188 , len = array.length
189 , tree = this._store[this._location]
190
191 while (++i < len) {
192 if (!array[i])
193 continue
194
195 key = this._isBuffer(array[i].key) ? array[i].key : String(array[i].key)
196 iter = tree.find(key)
197
198 if (array[i].type === 'put') {
199 value = this._isBuffer(array[i].value) ? array[i].value : 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 setImmediate(callback)
226}
227
228module.exports = MemDOWN