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 CHANGED
@@ -1,7 +1,8 @@
1
1
  const b4a = require('b4a')
2
2
  const Hypercore = require('hypercore')
3
- const KeyValueStream = require('./lib/key-value-stream.js')
4
- const ChangesStream = require('./lib/changes-stream.js')
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 KeyValueStream(this, range)
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
- module.exports = class ChangesStream extends Readable {
3
+ class ChangesStream extends Readable {
4
4
  constructor(tree, options = {}) {
5
- super({ eagerOpen: true })
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
- module.exports = decodeCompat
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 decodeCompatBlock = require('./compat.js')
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 ? decodeCompatBlock(buffer, seq) : c.decode(Block, buffer)
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 { Block } = require('./encoding.js')
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
- // if only the root was marked dirty and is === current bootstrap the batch is a noop - skip
349
- if (update.node.length === 1 && update.keys.length === 0) {
350
- const b = await this.tree.bootstrap()
351
- const n = update.node[0]
352
- if (b && b.context === n.context && b.core === n.core && b.seq === n.seq) return
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
- const length = context.core.length
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] = c.encode(Block, blocks[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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperbee2",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "btree",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -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
- }