hyperbee2 1.1.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +8 -3
- package/lib/{changes-stream.js → changes.js} +5 -2
- package/lib/compat.js +183 -4
- package/lib/context.js +12 -0
- package/lib/diff.js +132 -0
- package/lib/encoding.js +7 -2
- package/lib/ranges.js +139 -0
- package/lib/write.js +49 -21
- package/package.json +1 -1
- package/lib/key-value-stream.js +0 -134
package/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const b4a = require('b4a')
|
|
2
2
|
const Hypercore = require('hypercore')
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const { RangeStream } = require('./lib/ranges.js')
|
|
4
|
+
const { DiffStream } = require('./lib/diff.js')
|
|
5
|
+
const { ChangesStream } = require('./lib/changes.js')
|
|
5
6
|
const NodeCache = require('./lib/cache.js')
|
|
6
7
|
const WriteBatch = require('./lib/write.js')
|
|
7
8
|
const CoreContext = require('./lib/context.js')
|
|
@@ -111,7 +112,11 @@ class Hyperbee {
|
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
createReadStream(range) {
|
|
114
|
-
return new
|
|
115
|
+
return new RangeStream(this, range)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
createDiffStream(right, range) {
|
|
119
|
+
return new DiffStream(this, right, range)
|
|
115
120
|
}
|
|
116
121
|
|
|
117
122
|
createChangesStream(options) {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
const { Readable } = require('streamx')
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
class ChangesStream extends Readable {
|
|
4
4
|
constructor(tree, options = {}) {
|
|
5
|
-
|
|
5
|
+
const { highWaterMark } = options
|
|
6
|
+
super({ eagerOpen: true, highWaterMark })
|
|
6
7
|
|
|
7
8
|
const { head = null, activeRequests = tree.activeRequests } = options
|
|
8
9
|
|
|
@@ -76,3 +77,5 @@ module.exports = class ChangesStream extends Readable {
|
|
|
76
77
|
cb(null)
|
|
77
78
|
}
|
|
78
79
|
}
|
|
80
|
+
|
|
81
|
+
exports.ChangesStream = ChangesStream
|
package/lib/compat.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
// ported from the old protocol buffer impl in old bee
|
|
2
2
|
|
|
3
3
|
const encodings = require('protocol-buffers-encodings')
|
|
4
|
+
const b4a = require('b4a')
|
|
4
5
|
const varint = encodings.varint
|
|
5
6
|
const skip = encodings.skip
|
|
6
7
|
|
|
7
|
-
|
|
8
|
+
exports.decode = decodeCompat
|
|
9
|
+
exports.encode = encodeCompat
|
|
8
10
|
|
|
9
11
|
function decodeCompat(buffer, seq) {
|
|
10
|
-
const node = decodeNode(buffer)
|
|
11
|
-
const index = decodeYoloIndex(node.index)
|
|
12
|
+
const node = buffer.length > 1 ? decodeNode(buffer) : null
|
|
13
|
+
const index = node ? decodeYoloIndex(node.index) : { levels: [] }
|
|
12
14
|
const morphed = {
|
|
13
15
|
type: 0,
|
|
14
16
|
checkpoint: 0,
|
|
15
17
|
batch: { start: 0, end: 0 },
|
|
16
18
|
previous: seq > 1 ? { core: 0, seq: seq - 1 } : null,
|
|
17
19
|
tree: [],
|
|
18
|
-
data: [{ key: node.key, value: node.value }],
|
|
20
|
+
data: node ? [{ key: node.key, value: node.value }] : [],
|
|
19
21
|
cores: null
|
|
20
22
|
}
|
|
21
23
|
|
|
@@ -38,6 +40,53 @@ function decodeCompat(buffer, seq) {
|
|
|
38
40
|
return morphed
|
|
39
41
|
}
|
|
40
42
|
|
|
43
|
+
function encodeCompat(node) {
|
|
44
|
+
const index = { levels: [] }
|
|
45
|
+
|
|
46
|
+
if (node.tree) {
|
|
47
|
+
for (const t of node.tree) {
|
|
48
|
+
const lvl = { keys: [], children: [] }
|
|
49
|
+
for (const k of t.keys) lvl.keys.push(k.seq)
|
|
50
|
+
for (const c of t.children) lvl.children.push(c.seq, c.offset)
|
|
51
|
+
index.levels.push(lvl)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const bufIndex = b4a.allocUnsafe(encodingLengthYoloIndex(index))
|
|
56
|
+
encodeYoloIndex(index, bufIndex, 0)
|
|
57
|
+
|
|
58
|
+
const n = {
|
|
59
|
+
key: node.data[0].key,
|
|
60
|
+
value: node.data[0].value,
|
|
61
|
+
index: bufIndex
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const buf = b4a.allocUnsafe(encodingLengthNode(n))
|
|
65
|
+
return encodeNode(n, buf, 0)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function deflateIndex(index) {
|
|
69
|
+
const levels = index.map((l) => {
|
|
70
|
+
const keys = []
|
|
71
|
+
const children = []
|
|
72
|
+
|
|
73
|
+
for (let i = 0; i < l.value.keys.length; i++) {
|
|
74
|
+
keys.push(l.value.keys[i].seq)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
for (let i = 0; i < l.value.children.length; i++) {
|
|
78
|
+
children.push(l.value.children[i].seq, l.value.children[i].offset)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return { keys, children }
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const idx = { levels }
|
|
85
|
+
const buf = b4a.allocUnsafe(encodingLengthYoloIndex(idx))
|
|
86
|
+
|
|
87
|
+
return encodeYoloIndex(idx, buf, 0)
|
|
88
|
+
}
|
|
89
|
+
|
|
41
90
|
function decodeLevel(buf, offset, end) {
|
|
42
91
|
if (!offset) offset = 0
|
|
43
92
|
if (!end) end = buf.length
|
|
@@ -86,6 +135,74 @@ function decodeLevel(buf, offset, end) {
|
|
|
86
135
|
}
|
|
87
136
|
}
|
|
88
137
|
|
|
138
|
+
function encodingLengthLevel(obj) {
|
|
139
|
+
let length = 0
|
|
140
|
+
if (obj.keys) {
|
|
141
|
+
let packedLen = 0
|
|
142
|
+
for (let i = 0; i < obj.keys.length; i++) {
|
|
143
|
+
if (!obj.keys[i]) continue
|
|
144
|
+
let len = encodings.varint.encodingLength(obj.keys[i])
|
|
145
|
+
packedLen += len
|
|
146
|
+
}
|
|
147
|
+
if (packedLen) {
|
|
148
|
+
length += 1 + packedLen + varint.encodingLength(packedLen)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (obj.children) {
|
|
152
|
+
let packedLen = 0
|
|
153
|
+
for (let i = 0; i < obj.children.length; i++) {
|
|
154
|
+
if (!obj.children[i]) continue
|
|
155
|
+
let len = encodings.varint.encodingLength(obj.children[i])
|
|
156
|
+
packedLen += len
|
|
157
|
+
}
|
|
158
|
+
if (packedLen) {
|
|
159
|
+
length += 1 + packedLen + varint.encodingLength(packedLen)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return length
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function encodeLevel(obj, buf, offset) {
|
|
166
|
+
if (!offset) offset = 0
|
|
167
|
+
let oldOffset = offset
|
|
168
|
+
if (obj.keys) {
|
|
169
|
+
let packedLen = 0
|
|
170
|
+
for (let i = 0; i < obj.keys.length; i++) {
|
|
171
|
+
if (!obj.keys[i]) continue
|
|
172
|
+
packedLen += encodings.varint.encodingLength(obj.keys[i])
|
|
173
|
+
}
|
|
174
|
+
if (packedLen) {
|
|
175
|
+
buf[offset++] = 10
|
|
176
|
+
varint.encode(packedLen, buf, offset)
|
|
177
|
+
offset += varint.encode.bytes
|
|
178
|
+
}
|
|
179
|
+
for (let i = 0; i < obj.keys.length; i++) {
|
|
180
|
+
if (!obj.keys[i]) continue
|
|
181
|
+
encodings.varint.encode(obj.keys[i], buf, offset)
|
|
182
|
+
offset += encodings.varint.encode.bytes
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (obj.children) {
|
|
186
|
+
let packedLen = 0
|
|
187
|
+
for (let i = 0; i < obj.children.length; i++) {
|
|
188
|
+
if (!obj.children[i]) continue
|
|
189
|
+
packedLen += encodings.varint.encodingLength(obj.children[i])
|
|
190
|
+
}
|
|
191
|
+
if (packedLen) {
|
|
192
|
+
buf[offset++] = 18
|
|
193
|
+
varint.encode(packedLen, buf, offset)
|
|
194
|
+
offset += varint.encode.bytes
|
|
195
|
+
}
|
|
196
|
+
for (let i = 0; i < obj.children.length; i++) {
|
|
197
|
+
if (!obj.children[i]) continue
|
|
198
|
+
encodings.varint.encode(obj.children[i], buf, offset)
|
|
199
|
+
offset += encodings.varint.encode.bytes
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
encodeLevel.bytes = offset - oldOffset
|
|
203
|
+
return buf
|
|
204
|
+
}
|
|
205
|
+
|
|
89
206
|
function decodeYoloIndex(buf, offset, end) {
|
|
90
207
|
if (!offset) offset = 0
|
|
91
208
|
if (!end) end = buf.length
|
|
@@ -117,6 +234,37 @@ function decodeYoloIndex(buf, offset, end) {
|
|
|
117
234
|
}
|
|
118
235
|
}
|
|
119
236
|
|
|
237
|
+
function encodingLengthYoloIndex(obj) {
|
|
238
|
+
let length = 0
|
|
239
|
+
if (obj.levels) {
|
|
240
|
+
for (let i = 0; i < obj.levels.length; i++) {
|
|
241
|
+
if (!obj.levels[i]) continue
|
|
242
|
+
let len = encodingLengthLevel(obj.levels[i])
|
|
243
|
+
length += varint.encodingLength(len)
|
|
244
|
+
length += 1 + len
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return length
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function encodeYoloIndex(obj, buf, offset) {
|
|
251
|
+
if (!offset) offset = 0
|
|
252
|
+
let oldOffset = offset
|
|
253
|
+
if (obj.levels) {
|
|
254
|
+
for (let i = 0; i < obj.levels.length; i++) {
|
|
255
|
+
if (!obj.levels[i]) continue
|
|
256
|
+
buf[offset++] = 10
|
|
257
|
+
const len = encodingLengthLevel(obj.levels[i])
|
|
258
|
+
varint.encode(len, buf, offset)
|
|
259
|
+
offset += varint.encode.bytes
|
|
260
|
+
encodeLevel(obj.levels[i], buf, offset)
|
|
261
|
+
offset += encodeLevel.bytes
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
encodeYoloIndex.bytes = offset - oldOffset
|
|
265
|
+
return buf
|
|
266
|
+
}
|
|
267
|
+
|
|
120
268
|
function decodeNode(buf, offset, end) {
|
|
121
269
|
if (!offset) offset = 0
|
|
122
270
|
if (!end) end = buf.length
|
|
@@ -162,3 +310,34 @@ function decodeNode(buf, offset, end) {
|
|
|
162
310
|
}
|
|
163
311
|
}
|
|
164
312
|
}
|
|
313
|
+
|
|
314
|
+
function encodingLengthNode(obj) {
|
|
315
|
+
let length = 0
|
|
316
|
+
let len = encodings.bytes.encodingLength(obj.index)
|
|
317
|
+
length += 1 + len
|
|
318
|
+
len = encodings.bytes.encodingLength(obj.key)
|
|
319
|
+
length += 1 + len
|
|
320
|
+
if (obj.value) {
|
|
321
|
+
let len = encodings.bytes.encodingLength(obj.value)
|
|
322
|
+
length += 1 + len
|
|
323
|
+
}
|
|
324
|
+
return length
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function encodeNode(obj, buf, offset) {
|
|
328
|
+
if (!offset) offset = 0
|
|
329
|
+
let oldOffset = offset
|
|
330
|
+
buf[offset++] = 10
|
|
331
|
+
encodings.bytes.encode(obj.index, buf, offset)
|
|
332
|
+
offset += encodings.bytes.encode.bytes
|
|
333
|
+
buf[offset++] = 18
|
|
334
|
+
encodings.bytes.encode(obj.key, buf, offset)
|
|
335
|
+
offset += encodings.bytes.encode.bytes
|
|
336
|
+
if (obj.value) {
|
|
337
|
+
buf[offset++] = 26
|
|
338
|
+
encodings.bytes.encode(obj.value, buf, offset)
|
|
339
|
+
offset += encodings.bytes.encode.bytes
|
|
340
|
+
}
|
|
341
|
+
encodeNode.bytes = offset - oldOffset
|
|
342
|
+
return buf
|
|
343
|
+
}
|
package/lib/context.js
CHANGED
|
@@ -60,6 +60,8 @@ class CoreContext {
|
|
|
60
60
|
|
|
61
61
|
this.changed = true
|
|
62
62
|
this.cores.push({ key, fork: 0, length: 0, treeHash: null })
|
|
63
|
+
this.opened.push(null)
|
|
64
|
+
|
|
63
65
|
return this.cores.length
|
|
64
66
|
}
|
|
65
67
|
|
|
@@ -86,6 +88,7 @@ class CoreContext {
|
|
|
86
88
|
|
|
87
89
|
this.changed = true
|
|
88
90
|
this.cores.push({ key, fork: 0, length: 0, treeHash: null })
|
|
91
|
+
this.opened.push(null)
|
|
89
92
|
return this.cores.length
|
|
90
93
|
}
|
|
91
94
|
|
|
@@ -99,6 +102,15 @@ class CoreContext {
|
|
|
99
102
|
return this.opened[index - 1]
|
|
100
103
|
}
|
|
101
104
|
|
|
105
|
+
getCoreKey(index) {
|
|
106
|
+
if (index === 0) return this.core.key
|
|
107
|
+
if (index > this.cores.length) throw new Error('Bad core index: ' + index)
|
|
108
|
+
if (this.opened[index - 1] !== null && this.opened[index - 1].key) {
|
|
109
|
+
return this.opened[index - 1].key
|
|
110
|
+
}
|
|
111
|
+
return this.cores[index - 1].key
|
|
112
|
+
}
|
|
113
|
+
|
|
102
114
|
async getBlock(seq, core, activeRequests) {
|
|
103
115
|
if (core !== 0) await this.update(activeRequests)
|
|
104
116
|
const hc = this.getCore(core)
|
package/lib/diff.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
const { Readable } = require('streamx')
|
|
2
|
+
const b4a = require('b4a')
|
|
3
|
+
const { RangeIterator } = require('./ranges.js')
|
|
4
|
+
|
|
5
|
+
class DiffIterator {
|
|
6
|
+
constructor(left, right, { limit = -1 } = {}) {
|
|
7
|
+
this.left = left
|
|
8
|
+
this.right = right
|
|
9
|
+
|
|
10
|
+
this.left.limit = this.right.limit = -1
|
|
11
|
+
this.limit = limit
|
|
12
|
+
|
|
13
|
+
this.nextLeft = null
|
|
14
|
+
this.nextRight = null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async open() {
|
|
18
|
+
await Promise.all([this.left.open(), this.right.open()])
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
_shiftRight() {
|
|
22
|
+
const right = this.nextRight
|
|
23
|
+
this.nextRight = null
|
|
24
|
+
if (this.limit > 0) this.limit--
|
|
25
|
+
return { left: null, right }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
_shiftLeft() {
|
|
29
|
+
const left = this.nextLeft
|
|
30
|
+
this.nextLeft = null
|
|
31
|
+
if (this.limit > 0) this.limit--
|
|
32
|
+
return { left, right: null }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_fastForward() {
|
|
36
|
+
while (this.left.stack.length && this.right.stack.length) {
|
|
37
|
+
const a = this.left.stack[this.left.stack.length - 1]
|
|
38
|
+
const b = this.right.stack[this.right.stack.length - 1]
|
|
39
|
+
|
|
40
|
+
if (a.offset !== b.offset) return
|
|
41
|
+
if (!isSame(a.node, b.node)) return
|
|
42
|
+
|
|
43
|
+
this.left.stack.pop()
|
|
44
|
+
this.right.stack.pop()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async next() {
|
|
49
|
+
while (this.limit === -1 || this.limit > 0) {
|
|
50
|
+
const leftPromise = this.nextLeft ? null : this.left.next()
|
|
51
|
+
const rightPromise = this.nextRight ? null : this.right.next()
|
|
52
|
+
|
|
53
|
+
const [l, r] = await Promise.all([leftPromise, rightPromise])
|
|
54
|
+
|
|
55
|
+
if (leftPromise) this.nextLeft = l
|
|
56
|
+
if (rightPromise) this.nextRight = r
|
|
57
|
+
|
|
58
|
+
if (!this.nextLeft && !this.nextRight) return null
|
|
59
|
+
if (!this.nextLeft) return this._shiftRight()
|
|
60
|
+
if (!this.nextRight) return this._shiftLeft()
|
|
61
|
+
|
|
62
|
+
const cmp = b4a.compare(this.nextLeft.key, this.nextRight.key)
|
|
63
|
+
|
|
64
|
+
if (cmp === 0) {
|
|
65
|
+
const left = this.nextLeft
|
|
66
|
+
const right = this.nextRight
|
|
67
|
+
|
|
68
|
+
this.nextRight = this.nextLeft = null
|
|
69
|
+
|
|
70
|
+
if (isSame(left, right)) {
|
|
71
|
+
this._fastForward()
|
|
72
|
+
continue
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (this.limit > 0) this.limit--
|
|
76
|
+
return { left, right }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (cmp < 0) return this._shiftLeft()
|
|
80
|
+
return this._shiftRight()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return null
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
class DiffStream extends Readable {
|
|
88
|
+
constructor(left, right, options = {}) {
|
|
89
|
+
const { highWaterMark } = options
|
|
90
|
+
super({ eagerOpen: true, highWaterMark })
|
|
91
|
+
|
|
92
|
+
this.left = left
|
|
93
|
+
this.right = right
|
|
94
|
+
this.iterator = new DiffIterator(
|
|
95
|
+
new RangeIterator(left, options),
|
|
96
|
+
new RangeIterator(right, options),
|
|
97
|
+
options
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async _open(cb) {
|
|
102
|
+
try {
|
|
103
|
+
await this.iterator.open()
|
|
104
|
+
} catch (err) {
|
|
105
|
+
cb(err)
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
cb(null)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async _read(cb) {
|
|
112
|
+
try {
|
|
113
|
+
this.push(await this.iterator.next())
|
|
114
|
+
} catch (err) {
|
|
115
|
+
cb(err)
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
cb(null)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
exports.DiffIterator = DiffIterator
|
|
123
|
+
exports.DiffStream = DiffStream
|
|
124
|
+
|
|
125
|
+
function isSame(a, b) {
|
|
126
|
+
if (a.seq !== b.seq || a.offset !== b.offset) return false
|
|
127
|
+
|
|
128
|
+
const k1 = a.context.getCoreKey(a.core)
|
|
129
|
+
const k2 = b.context.getCoreKey(b.core)
|
|
130
|
+
|
|
131
|
+
return k1 === k2 || b4a.equals(k1, k2) ? true : false
|
|
132
|
+
}
|
package/lib/encoding.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
const c = require('compact-encoding')
|
|
2
2
|
const { getEncoding } = require('../spec/hyperschema')
|
|
3
|
-
const
|
|
3
|
+
const compat = require('./compat.js')
|
|
4
4
|
|
|
5
5
|
const Block = getEncoding('@bee/block')
|
|
6
6
|
|
|
7
7
|
exports.Block = Block
|
|
8
|
+
exports.encodeBlock = encodeBlock
|
|
8
9
|
exports.decodeBlock = decodeBlock
|
|
9
10
|
|
|
11
|
+
function encodeBlock(block, format) {
|
|
12
|
+
return format === 2 ? compat.encode(block) : c.encode(Block, block)
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
function decodeBlock(buffer, seq) {
|
|
11
16
|
const isCompat = buffer.length > 0 && buffer[0] === 0x0a
|
|
12
|
-
const block = isCompat ?
|
|
17
|
+
const block = isCompat ? compat.decode(buffer, seq) : c.decode(Block, buffer)
|
|
13
18
|
return block
|
|
14
19
|
}
|
package/lib/ranges.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
const { Readable } = require('streamx')
|
|
2
|
+
const b4a = require('b4a')
|
|
3
|
+
|
|
4
|
+
class RangeIterator {
|
|
5
|
+
constructor(tree, { activeRequests = [], reverse = false, limit = -1, gte, gt, lte, lt } = {}) {
|
|
6
|
+
this.tree = tree
|
|
7
|
+
this.root = null
|
|
8
|
+
this.stack = []
|
|
9
|
+
this.activeRequests = activeRequests
|
|
10
|
+
this.reverse = reverse
|
|
11
|
+
this.limit = limit
|
|
12
|
+
this.start = gte || gt || null
|
|
13
|
+
this.end = lte || lt || null
|
|
14
|
+
this.inclusive = !!(reverse ? lte : gte)
|
|
15
|
+
this.compare = (reverse ? gte : lte) ? 0 : -1
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async open() {
|
|
19
|
+
if (!this.root) this.root = await this.tree.bootstrap(this.activeRequests)
|
|
20
|
+
if (!this.root) return
|
|
21
|
+
|
|
22
|
+
if (this.limit === 0) return
|
|
23
|
+
|
|
24
|
+
this.stack.push({ node: this.root, offset: 0 })
|
|
25
|
+
|
|
26
|
+
if (this.reverse ? !this.end : !this.start) return
|
|
27
|
+
|
|
28
|
+
const offset = this.inclusive ? 0 : 1
|
|
29
|
+
|
|
30
|
+
while (true) {
|
|
31
|
+
const top = this.stack[this.stack.length - 1]
|
|
32
|
+
const v = top.node.value
|
|
33
|
+
? this.tree.bump(top.node)
|
|
34
|
+
: await this.tree.inflate(top.node, this.activeRequests)
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < v.keys.length; i++) {
|
|
37
|
+
const j = this.reverse ? v.keys.length - 1 - i : i
|
|
38
|
+
|
|
39
|
+
const c = this.reverse
|
|
40
|
+
? b4a.compare(v.keys[j].key, this.end)
|
|
41
|
+
: b4a.compare(this.start, v.keys[j].key)
|
|
42
|
+
|
|
43
|
+
if (c < 0) break
|
|
44
|
+
top.offset = 2 * i + 1 + (c === 0 ? offset : 1)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const child = (top.offset & 1) === 0
|
|
48
|
+
const k = top.offset >> 1
|
|
49
|
+
|
|
50
|
+
if (!child || k >= v.children.length) break
|
|
51
|
+
|
|
52
|
+
const j = this.reverse ? v.children.length - 1 - k : k
|
|
53
|
+
|
|
54
|
+
this.stack.push({
|
|
55
|
+
node: v.children[j],
|
|
56
|
+
offset: 0
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
top.offset++
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async next() {
|
|
64
|
+
while (this.stack.length && (this.limit === -1 || this.limit > 0)) {
|
|
65
|
+
const top = this.stack.pop()
|
|
66
|
+
const v = top.node.value
|
|
67
|
+
? this.tree.bump(top.node)
|
|
68
|
+
: await this.tree.inflate(top.node, this.activeRequests)
|
|
69
|
+
|
|
70
|
+
const offset = top.offset++
|
|
71
|
+
const child = (offset & 1) === 0
|
|
72
|
+
const k = offset >> 1
|
|
73
|
+
|
|
74
|
+
if (child) {
|
|
75
|
+
this.stack.push(top)
|
|
76
|
+
if (k < v.children.length) {
|
|
77
|
+
const j = this.reverse ? v.children.length - 1 - k : k
|
|
78
|
+
this.stack.push({ node: v.children[j], offset: 0 })
|
|
79
|
+
}
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (k < v.keys.length) {
|
|
84
|
+
const j = this.reverse ? v.keys.length - 1 - k : k
|
|
85
|
+
|
|
86
|
+
const data = top.node.value.keys[j]
|
|
87
|
+
|
|
88
|
+
const c = this.reverse
|
|
89
|
+
? this.start
|
|
90
|
+
? b4a.compare(this.start, data.key)
|
|
91
|
+
: -1
|
|
92
|
+
: this.end
|
|
93
|
+
? b4a.compare(data.key, this.end)
|
|
94
|
+
: -1
|
|
95
|
+
|
|
96
|
+
if (c > this.compare) break
|
|
97
|
+
|
|
98
|
+
this.stack.push(top)
|
|
99
|
+
if (this.limit !== -1) this.limit--
|
|
100
|
+
return data
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return null
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
class RangeStream extends Readable {
|
|
109
|
+
constructor(tree, options = {}) {
|
|
110
|
+
const { highWaterMark } = options
|
|
111
|
+
super({ eagerOpen: true, highWaterMark })
|
|
112
|
+
|
|
113
|
+
this.tree = tree
|
|
114
|
+
this.iterator = new RangeIterator(tree, options)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async _open(cb) {
|
|
118
|
+
try {
|
|
119
|
+
await this.iterator.open()
|
|
120
|
+
} catch (err) {
|
|
121
|
+
cb(err)
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
cb(null)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async _read(cb) {
|
|
128
|
+
try {
|
|
129
|
+
this.push(await this.iterator.next())
|
|
130
|
+
} catch (err) {
|
|
131
|
+
cb(err)
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
cb(null)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
exports.RangeIterator = RangeIterator
|
|
139
|
+
exports.RangeStream = RangeStream
|
package/lib/write.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const b4a = require('b4a')
|
|
2
2
|
const c = require('compact-encoding')
|
|
3
|
-
const {
|
|
3
|
+
const { encodeBlock } = require('./encoding.js')
|
|
4
4
|
const {
|
|
5
|
+
DataPointer,
|
|
5
6
|
TreeNode,
|
|
6
7
|
TreeNodePointer,
|
|
7
8
|
MIN_KEYS,
|
|
@@ -11,23 +12,25 @@ const {
|
|
|
11
12
|
} = require('./tree.js')
|
|
12
13
|
|
|
13
14
|
module.exports = class WriteBatch {
|
|
14
|
-
constructor(tree, { length = -1, key = null, autoUpdate = true } = {}) {
|
|
15
|
+
constructor(tree, { length = -1, key = null, autoUpdate = true, blockFormat = 3 } = {}) {
|
|
15
16
|
this.tree = tree
|
|
16
17
|
this.snapshot = tree.snapshot()
|
|
17
18
|
this.autoUpdate = autoUpdate
|
|
18
19
|
this.length = length
|
|
19
20
|
this.key = key
|
|
21
|
+
this.blockFormat = blockFormat
|
|
20
22
|
this.closed = false
|
|
23
|
+
this.applied = 0
|
|
21
24
|
this.root = null
|
|
22
25
|
this.ops = []
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
tryPut(key, value) {
|
|
26
|
-
this.ops.push({ put: true, key, value })
|
|
29
|
+
this.ops.push({ put: true, applied: false, key, value })
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
tryDelete(key) {
|
|
30
|
-
this.ops.push({ put: false, key, value: null })
|
|
33
|
+
this.ops.push({ put: false, applied: false, key, value: null })
|
|
31
34
|
}
|
|
32
35
|
|
|
33
36
|
tryClear() {
|
|
@@ -51,7 +54,6 @@ module.exports = class WriteBatch {
|
|
|
51
54
|
|
|
52
55
|
try {
|
|
53
56
|
const ops = this.ops
|
|
54
|
-
this.ops = []
|
|
55
57
|
|
|
56
58
|
const root = await this.tree.bootstrap()
|
|
57
59
|
|
|
@@ -72,8 +74,9 @@ module.exports = class WriteBatch {
|
|
|
72
74
|
)
|
|
73
75
|
|
|
74
76
|
for (const op of ops) {
|
|
75
|
-
if (op.put) await this._put(op.key, op.value)
|
|
76
|
-
else await this._delete(op.key)
|
|
77
|
+
if (op.put) op.applied = await this._put(op.key, op.value)
|
|
78
|
+
else op.applied = await this._delete(op.key)
|
|
79
|
+
if (op.applied) this.applied++
|
|
77
80
|
}
|
|
78
81
|
|
|
79
82
|
await this._flush()
|
|
@@ -115,10 +118,10 @@ module.exports = class WriteBatch {
|
|
|
115
118
|
c = b4a.compare(target, m.key)
|
|
116
119
|
|
|
117
120
|
if (c === 0) {
|
|
118
|
-
if (b4a.equals(m.value, value)) return
|
|
121
|
+
if (b4a.equals(m.value, value)) return false
|
|
119
122
|
v.setValue(this.tree.context, mid, value)
|
|
120
123
|
for (let i = 0; i < stack.length; i++) stack[i].changed = true
|
|
121
|
-
return
|
|
124
|
+
return true
|
|
122
125
|
}
|
|
123
126
|
|
|
124
127
|
if (c < 0) e = mid
|
|
@@ -132,7 +135,7 @@ module.exports = class WriteBatch {
|
|
|
132
135
|
const v = ptr.value ? this.snapshot.bump(ptr) : await this.snapshot.inflate(ptr)
|
|
133
136
|
let status = v.put(this.tree.context, target, value, null)
|
|
134
137
|
|
|
135
|
-
if (status === UNCHANGED) return
|
|
138
|
+
if (status === UNCHANGED) return false
|
|
136
139
|
|
|
137
140
|
ptr.changed = true
|
|
138
141
|
|
|
@@ -155,6 +158,8 @@ module.exports = class WriteBatch {
|
|
|
155
158
|
status = UNCHANGED
|
|
156
159
|
}
|
|
157
160
|
}
|
|
161
|
+
|
|
162
|
+
return true
|
|
158
163
|
}
|
|
159
164
|
|
|
160
165
|
async _delete(key) {
|
|
@@ -181,18 +186,20 @@ module.exports = class WriteBatch {
|
|
|
181
186
|
// we mark these as changed late, so we don't rewrite them if it is a 404
|
|
182
187
|
for (let i = 0; i < stack.length; i++) stack[i].changed = true
|
|
183
188
|
this.root = await this._rebalance(stack)
|
|
184
|
-
return
|
|
189
|
+
return true
|
|
185
190
|
}
|
|
186
191
|
|
|
187
192
|
if (c < 0) e = mid
|
|
188
193
|
else s = mid + 1
|
|
189
194
|
}
|
|
190
195
|
|
|
191
|
-
if (!v.children.length) return
|
|
196
|
+
if (!v.children.length) return false
|
|
192
197
|
|
|
193
198
|
const i = c < 0 ? e : s
|
|
194
199
|
ptr = v.children[i]
|
|
195
200
|
}
|
|
201
|
+
|
|
202
|
+
return false
|
|
196
203
|
}
|
|
197
204
|
|
|
198
205
|
async _setKeyToNearestLeaf(v, index, stack) {
|
|
@@ -334,7 +341,7 @@ module.exports = class WriteBatch {
|
|
|
334
341
|
continue
|
|
335
342
|
}
|
|
336
343
|
|
|
337
|
-
if (first) {
|
|
344
|
+
if (first || this.blockFormat === 2) {
|
|
338
345
|
stack.push({ update, node: n })
|
|
339
346
|
first = false
|
|
340
347
|
} else {
|
|
@@ -345,15 +352,15 @@ module.exports = class WriteBatch {
|
|
|
345
352
|
}
|
|
346
353
|
}
|
|
347
354
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
if (!b && n.value.isEmpty()) return
|
|
355
|
+
const length = context.core.length
|
|
356
|
+
|
|
357
|
+
// if noop and not genesis, bail early
|
|
358
|
+
if (this.applied === 0 && length > 0) {
|
|
359
|
+
return
|
|
354
360
|
}
|
|
355
361
|
|
|
356
|
-
|
|
362
|
+
if (this.blockFormat === 2) toBlockFormat2(context, batch, this.ops)
|
|
363
|
+
|
|
357
364
|
const blocks = new Array(batch.length)
|
|
358
365
|
|
|
359
366
|
for (let i = 0; i < batch.length; i++) {
|
|
@@ -411,7 +418,7 @@ module.exports = class WriteBatch {
|
|
|
411
418
|
|
|
412
419
|
for (let i = 0; i < blocks.length; i++) {
|
|
413
420
|
blocks[i].checkpoint = context.checkpoint
|
|
414
|
-
buffers[i] =
|
|
421
|
+
buffers[i] = encodeBlock(blocks[i], this.blockFormat)
|
|
415
422
|
}
|
|
416
423
|
|
|
417
424
|
if (this.closed) {
|
|
@@ -429,3 +436,24 @@ module.exports = class WriteBatch {
|
|
|
429
436
|
}
|
|
430
437
|
}
|
|
431
438
|
}
|
|
439
|
+
|
|
440
|
+
function toBlockFormat2(context, batch, ops) {
|
|
441
|
+
const map = new Map()
|
|
442
|
+
let index = 0
|
|
443
|
+
|
|
444
|
+
for (const k of batch[0].keys) {
|
|
445
|
+
map.set(b4a.toString(k.key, 'hex'), k)
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
for (let i = ops.length - 1; i >= 0; i--) {
|
|
449
|
+
const op = ops[i]
|
|
450
|
+
if (!op.put && !op.applied) continue
|
|
451
|
+
|
|
452
|
+
const k = map.get(b4a.toString(op.key, 'hex'))
|
|
453
|
+
const j = index++
|
|
454
|
+
if (j === batch.length) batch.push({ node: [], keys: [] })
|
|
455
|
+
batch[j].keys = [k || new DataPointer(context, 0, 0, 0, false, op.key, op.value)]
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return batch
|
|
459
|
+
}
|
package/package.json
CHANGED
package/lib/key-value-stream.js
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
const { Readable } = require('streamx')
|
|
2
|
-
const b4a = require('b4a')
|
|
3
|
-
|
|
4
|
-
module.exports = class KeyValueStream extends Readable {
|
|
5
|
-
constructor(tree, options = {}) {
|
|
6
|
-
super({ eagerOpen: true })
|
|
7
|
-
|
|
8
|
-
this.tree = tree
|
|
9
|
-
this.root = null
|
|
10
|
-
|
|
11
|
-
this._stack = []
|
|
12
|
-
this._activeRequests = options.activeRequests || this.tree.activeRequests
|
|
13
|
-
this._limit = options.limit === undefined ? -1 : options.limit
|
|
14
|
-
this._reverse = !!options.reverse
|
|
15
|
-
this._start = options.gte || options.gt || null
|
|
16
|
-
this._end = options.lte || options.lt || null
|
|
17
|
-
this._inclusive = !!(this._reverse ? options.lte : options.gte)
|
|
18
|
-
this._compare = (this._reverse ? options.gte : options.lte) ? 0 : -1
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async _openp() {
|
|
22
|
-
if (!this.root) this.root = await this.tree.bootstrap(this._activeRequests)
|
|
23
|
-
if (!this.root) return
|
|
24
|
-
|
|
25
|
-
if (this._limit === 0) return
|
|
26
|
-
|
|
27
|
-
this._stack.push({ node: this.root, offset: 0 })
|
|
28
|
-
|
|
29
|
-
if (this._reverse ? !this._end : !this._start) return
|
|
30
|
-
|
|
31
|
-
const offset = this._inclusive ? 0 : 1
|
|
32
|
-
|
|
33
|
-
while (true) {
|
|
34
|
-
const top = this._stack[this._stack.length - 1]
|
|
35
|
-
const v = top.node.value
|
|
36
|
-
? this.tree.bump(top.node)
|
|
37
|
-
: await this.tree.inflate(top.node, this._activeRequests)
|
|
38
|
-
|
|
39
|
-
for (let i = 0; i < v.keys.length; i++) {
|
|
40
|
-
const j = this._reverse ? v.keys.length - 1 - i : i
|
|
41
|
-
|
|
42
|
-
const c = this._reverse
|
|
43
|
-
? b4a.compare(v.keys[j].key, this._end)
|
|
44
|
-
: b4a.compare(this._start, v.keys[j].key)
|
|
45
|
-
|
|
46
|
-
if (c < 0) break
|
|
47
|
-
top.offset = 2 * i + 1 + (c === 0 ? offset : 1)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const child = (top.offset & 1) === 0
|
|
51
|
-
const k = top.offset >> 1
|
|
52
|
-
|
|
53
|
-
if (!child || k >= v.children.length) break
|
|
54
|
-
|
|
55
|
-
const j = this._reverse ? v.children.length - 1 - k : k
|
|
56
|
-
|
|
57
|
-
this._stack.push({
|
|
58
|
-
node: v.children[j],
|
|
59
|
-
offset: 0
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
top.offset++
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
async _readp() {
|
|
67
|
-
while (this._stack.length && (this._limit === -1 || this._limit > 0)) {
|
|
68
|
-
const top = this._stack.pop()
|
|
69
|
-
const v = top.node.value
|
|
70
|
-
? this.tree.bump(top.node)
|
|
71
|
-
: await this.tree.inflate(top.node, this._activeRequests)
|
|
72
|
-
|
|
73
|
-
const offset = top.offset++
|
|
74
|
-
const child = (offset & 1) === 0
|
|
75
|
-
const k = offset >> 1
|
|
76
|
-
|
|
77
|
-
if (child) {
|
|
78
|
-
this._stack.push(top)
|
|
79
|
-
if (k < v.children.length) {
|
|
80
|
-
const j = this._reverse ? v.children.length - 1 - k : k
|
|
81
|
-
this._stack.push({ node: v.children[j], offset: 0 })
|
|
82
|
-
}
|
|
83
|
-
continue
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (k < v.keys.length) {
|
|
87
|
-
const j = this._reverse ? v.keys.length - 1 - k : k
|
|
88
|
-
|
|
89
|
-
const data = top.node.value.keys[j]
|
|
90
|
-
|
|
91
|
-
const c = this._reverse
|
|
92
|
-
? this._start
|
|
93
|
-
? b4a.compare(this._start, data.key)
|
|
94
|
-
: -1
|
|
95
|
-
: this._end
|
|
96
|
-
? b4a.compare(data.key, this._end)
|
|
97
|
-
: -1
|
|
98
|
-
|
|
99
|
-
if (c > this._compare) break
|
|
100
|
-
|
|
101
|
-
this._stack.push(top)
|
|
102
|
-
this.push(this._finalize(data))
|
|
103
|
-
if (this._limit !== -1) this._limit--
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
this.push(null)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
_finalize(kv) {
|
|
112
|
-
return kv
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async _open(cb) {
|
|
116
|
-
try {
|
|
117
|
-
await this._openp()
|
|
118
|
-
} catch (err) {
|
|
119
|
-
cb(err)
|
|
120
|
-
return
|
|
121
|
-
}
|
|
122
|
-
cb(null)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
async _read(cb) {
|
|
126
|
-
try {
|
|
127
|
-
await this._readp()
|
|
128
|
-
} catch (err) {
|
|
129
|
-
cb(err)
|
|
130
|
-
return
|
|
131
|
-
}
|
|
132
|
-
cb(null)
|
|
133
|
-
}
|
|
134
|
-
}
|