forked from Level/memdown
-
Notifications
You must be signed in to change notification settings - Fork 2
/
memdb.coffee
159 lines (140 loc) · 4.34 KB
/
memdb.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
createRBT = require('functional-red-black-tree')
inherits = require('inherits-ex')
AbstractNoSQL = require('abstract-nosql')
AbstractIterator = require('abstract-iterator')
Errors = require('abstract-error')
NotFoundError = Errors.NotFoundError
ltgt = require('ltgt')
setImmediate = global.setImmediate or process.nextTick
globalStore = {}
toKey = (key) ->
if typeof key == 'string' then '$' + key else JSON.stringify(key)
gt = (value) -> value > @_endValue
gte = (value) -> value >= @_endValue
lt = (value) -> value < @_endValue
lte = (value) -> value <= @_endValue
class MemIterator
inherits MemIterator, AbstractIterator
constructor: (db, options) ->
super db, options
options = @options
@_limit = options.limit
if @_limit == -1
@_limit = Infinity
tree = db._store[db._location]
@keyAsBuffer = options.keyAsBuffer == true
@valueAsBuffer = options.valueAsBuffer == true
@_reverse = options.reverse
#this._options = options
@_done = 0
if !@_reverse
@_incr = 'next'
@_start = ltgt.lowerBound(options)
@_endValue = ltgt.upperBound(options)
if @_start == null
@_tree = tree.begin
# if options.gt == null
# @_tree.next()
else if ltgt.lowerBoundInclusive(options)
@_tree = tree.ge(@_start)
else
@_tree = tree.gt(@_start)
if @_endValue
if ltgt.upperBoundInclusive(options)
@_test = lte
else
@_test = lt
else
@_incr = 'prev'
@_start = ltgt.upperBound(options)
@_endValue = ltgt.lowerBound(options)
if @_start == null
@_tree = tree.end
# if options.lt == null
# @_tree.prev()
else if ltgt.upperBoundInclusive(options)
@_tree = tree.le(@_start)
else
@_tree = tree.lt(@_start)
if @_endValue
if ltgt.lowerBoundInclusive(options)
@_test = gte
else
@_test = gt
return
_nextSync: () ->
return false if @_done++ >= @_limit
return false if !@_tree.valid
key = @_tree.key
value = @_tree.value
return false unless @_test(key)
key = new Buffer(key) if @keyAsBuffer
value = new Buffer(value) if @valueAsBuffer
@_tree[@_incr]()
return [
key
value
]
_test: -> true
module.exports = class MemDB
inherits MemDB, AbstractNoSQL
constructor: (location) ->
return new MemDB(location) unless this instanceof MemDB
super if typeof location == 'string' then location else ''
@_location = if @location then toKey(@location) else '_tree'
@_store = if @location then globalStore else this
@_store[@_location] = @_store[@_location] or createRBT()
return
finalize: ->
if @_store != this
delete globalStore[@_location]
return
_openSync: -> true
_putSync: (key, value, options) ->
@_store[@_location] = @_store[@_location].remove(key).insert(key, value)
true
_isExistsSync: (key, options) ->
result = @_store[@_location].get(key)
if result == undefined
result = false
else
result = true
result
_getSync: (key, options) ->
value = @_store[@_location].get(key)
# 'NotFound' error, consistent with LevelDOWN API
throw new NotFoundError if value == undefined
if options.asBuffer == true and !@_isBuffer(value)
value = new Buffer(String(value))
value
_delSync: (key, options) ->
@_store[@_location] = @_store[@_location].remove(key)
true
_batchSync: (array, options) ->
i = -1
len = array.length
tree = @_store[@_location]
while ++i < len
if !array[i]
continue
key = if @_isBuffer(array[i].key) then array[i].key else String(array[i].key)
err = @_checkKey(key, 'key')
if err
throw err
tree = tree.remove(array[i].key)
# we always remove as insert doesn't replace
if array[i].type == 'put'
value = if @_isBuffer(array[i].value) then array[i].value else String(array[i].value)
err = @_checkKey(value, 'value')
if err
throw err
tree = tree.insert(key, value)
@_store[@_location] = tree
true
IteratorClass: MemIterator
_isBuffer: (obj) -> Buffer.isBuffer obj
@destroy: (name, callback) ->
key = toKey(name)
delete globalStore[key] if key of globalStore
setImmediate callback
return