1 | var 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 |
|
9 | function toKey (key) {
|
10 | return typeof key == 'string' ? '$' + key : JSON.stringify(key)
|
11 | }
|
12 |
|
13 | function gt(value) {
|
14 | return ltgt.compare(value, this._end) > 0
|
15 | }
|
16 |
|
17 | function gte(value) {
|
18 | return ltgt.compare(value, this._end) >= 0
|
19 | }
|
20 |
|
21 | function lt(value) {
|
22 | return ltgt.compare(value, this._end) < 0
|
23 | }
|
24 |
|
25 | function lte(value) {
|
26 | return ltgt.compare(value, this._end) <= 0
|
27 | }
|
28 |
|
29 |
|
30 | function 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 |
|
87 | inherits(MemIterator, AbstractIterator)
|
88 |
|
89 | MemIterator.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 |
|
118 | MemIterator.prototype._test = function () {return true}
|
119 |
|
120 | function 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 |
|
131 | MemDOWN.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 |
|
141 | inherits(MemDOWN, AbstractLevelDOWN)
|
142 |
|
143 | MemDOWN.prototype._open = function (options, callback) {
|
144 | var self = this
|
145 | setImmediate(function callNext() { callback(null, self) })
|
146 | }
|
147 |
|
148 | MemDOWN.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 |
|
154 | MemDOWN.prototype._get = function (key, options, callback) {
|
155 | var value = this._store[this._location].get(key)
|
156 |
|
157 | if (value === undefined) {
|
158 |
|
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 |
|
172 | MemDOWN.prototype._del = function (key, options, callback) {
|
173 | this._store[this._location] = this._store[this._location].remove(key)
|
174 | setImmediate(callback)
|
175 | }
|
176 |
|
177 | MemDOWN.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 |
|
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 |
|
215 | MemDOWN.prototype._iterator = function (options) {
|
216 | return new MemIterator(this, options)
|
217 | }
|
218 |
|
219 | MemDOWN.prototype._isBuffer = function (obj) {
|
220 | return Buffer.isBuffer(obj)
|
221 | }
|
222 |
|
223 | MemDOWN.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 |
|
232 | module.exports = MemDOWN
|