hyperbee2 1.1.2 → 2.0.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,19 +1,29 @@
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')
8
- const { DataPointer, TreeNode, TreeNodePointer, EMPTY } = require('./lib/tree.js')
9
+ const {
10
+ Pointer,
11
+ KeyPointer,
12
+ ValuePointer,
13
+ TreeNode,
14
+ TreeNodePointer,
15
+ EMPTY
16
+ } = require('./lib/tree.js')
17
+ const { DeltaOp, DeltaCohort, OP_COHORT } = require('./lib/compression.js')
9
18
 
10
19
  class Hyperbee {
11
20
  constructor(store, options = {}) {
12
21
  const {
22
+ t = 128, // legacy number for now, should be 128 now
13
23
  key = null,
14
24
  encryption = null,
15
25
  core = key ? store.get(key) : store.get({ key, name: 'bee', encryption }),
16
- context = new CoreContext(store, core, core, encryption),
26
+ context = new CoreContext(store, core, core, encryption, t),
17
27
  maxCacheSize = 4096,
18
28
  cache = new NodeCache(maxCacheSize),
19
29
  root = null,
@@ -110,8 +120,12 @@ class Hyperbee {
110
120
  if (!this.view) await this.store.close()
111
121
  }
112
122
 
113
- createReadStream(range) {
114
- return new KeyValueStream(this, range)
123
+ createReadStream(options) {
124
+ return new RangeStream(this, options)
125
+ }
126
+
127
+ createDiffStream(right, options) {
128
+ return new DiffStream(this, right, options)
115
129
  }
116
130
 
117
131
  createChangesStream(options) {
@@ -119,12 +133,21 @@ class Hyperbee {
119
133
  }
120
134
 
121
135
  async peek(range = {}) {
122
- const rs = new KeyValueStream(this, { ...range, limit: 1 })
136
+ const rs = new RangeStream(this, { ...range, limit: 1 })
123
137
  let entry = null
124
138
  for await (const data of rs) entry = data
125
139
  return entry
126
140
  }
127
141
 
142
+ download(range = {}) {
143
+ const rs = new RangeStream(this, range)
144
+ rs.resume()
145
+ return new Promise((resolve, reject) => {
146
+ rs.once('error', reject)
147
+ rs.once('close', resolve)
148
+ })
149
+ }
150
+
128
151
  bump(ptr) {
129
152
  if (ptr.changed) return ptr.value
130
153
  this.cache.bump(ptr)
@@ -132,40 +155,75 @@ class Hyperbee {
132
155
  return ptr.value
133
156
  }
134
157
 
158
+ // TODO: unslab these and parallize
135
159
  async inflate(ptr, activeRequests = this.activeRequests) {
136
160
  if (ptr.value) {
137
161
  this.bump(ptr)
138
162
  return ptr.value
139
163
  }
140
164
 
141
- const block = await ptr.context.getBlock(ptr.seq, ptr.core, activeRequests)
142
- const context = await ptr.context.getContext(ptr.core, activeRequests)
165
+ const [block, context] = await Promise.all([
166
+ ptr.context.getBlock(ptr.seq, ptr.core, activeRequests),
167
+ ptr.context.getContext(ptr.core, activeRequests)
168
+ ])
169
+
143
170
  const tree = block.tree[ptr.offset]
144
171
 
145
172
  const keys = new Array(tree.keys.length)
146
173
  const children = new Array(tree.children.length)
147
174
 
148
175
  for (let i = 0; i < keys.length; i++) {
149
- const k = tree.keys[i]
150
- const blk =
151
- k.seq === ptr.seq && k.core === 0 && ptr.core === 0
152
- ? block
153
- : await context.getBlock(k.seq, k.core, activeRequests)
154
- const d = blk.data[k.offset]
155
- keys[i] = new DataPointer(context, k.core, k.seq, k.offset, false, d.key, d.value)
176
+ const d = tree.keys[i]
177
+ keys[i] = inflateKey(context, d, ptr, block, activeRequests)
156
178
  }
157
179
 
158
180
  for (let i = 0; i < children.length; i++) {
159
- const c = tree.children[i]
160
- children[i] = new TreeNodePointer(context, c.core, c.seq, c.offset, false, null)
181
+ const d = tree.children[i]
182
+ children[i] = inflateChild(context, d, ptr, block, activeRequests)
161
183
  }
162
184
 
163
- ptr.value = new TreeNode(keys, children)
185
+ ptr.value = new TreeNode(await Promise.all(keys), await Promise.all(children))
164
186
  this.bump(ptr)
165
187
 
166
188
  return ptr.value
167
189
  }
168
190
 
191
+ async finalizeKeyPointer(key, activeRequests = this.activeRequests) {
192
+ const value = key.value || (await this.inflateValue(key, activeRequests))
193
+
194
+ return {
195
+ core: key.context.getCore(key.core),
196
+ offset: key.offset,
197
+ seq: key.seq,
198
+ key: key.key,
199
+ value
200
+ }
201
+ }
202
+
203
+ async inflateValue(key, activeRequests = this.activeRequests) {
204
+ if (key.value) return key.value
205
+ if (!key.valuePointer) return null
206
+
207
+ const ptr = key.valuePointer
208
+
209
+ if (ptr.split === 0) {
210
+ const block = await ptr.context.getBlock(ptr.seq, ptr.core, activeRequests)
211
+ return block.values[ptr.offset]
212
+ }
213
+
214
+ const blockPromises = new Array(ptr.split + 1)
215
+ for (let i = 0; i < blockPromises.length; i++) {
216
+ blockPromises[i] = ptr.context.getBlock(ptr.seq - ptr.split + i, ptr.core, activeRequests)
217
+ }
218
+ const blocks = await Promise.all(blockPromises)
219
+ const splitValue = new Array(blockPromises.length)
220
+ for (let i = 0; i < splitValue.length - 1; i++) {
221
+ splitValue[i] = blocks[i].values[0]
222
+ }
223
+ splitValue[splitValue.length - 1] = blocks[blocks.length - 1].buffer[ptr.offset]
224
+ return b4a.concat(splitValue)
225
+ }
226
+
169
227
  async bootstrap(activeRequests = this.activeRequests) {
170
228
  if (!this.root) await this.ready()
171
229
  if (this.unbatch) await this._rollback(activeRequests)
@@ -218,11 +276,11 @@ class Hyperbee {
218
276
 
219
277
  while (s < e) {
220
278
  const mid = (s + e) >> 1
221
- const m = v.keys[mid]
279
+ const m = v.keys.get(mid)
222
280
 
223
281
  c = b4a.compare(key, m.key)
224
282
 
225
- if (c === 0) return m
283
+ if (c === 0) return this.finalizeKeyPointer(m, activeRequests)
226
284
 
227
285
  if (c < 0) e = mid
228
286
  else s = mid + 1
@@ -231,7 +289,7 @@ class Hyperbee {
231
289
  if (!v.children.length) return null
232
290
 
233
291
  const i = c < 0 ? e : s
234
- ptr = v.children[i]
292
+ ptr = v.children.get(i)
235
293
  }
236
294
  }
237
295
  }
@@ -239,3 +297,84 @@ class Hyperbee {
239
297
  module.exports = Hyperbee
240
298
 
241
299
  function noop() {}
300
+
301
+ function inflateKey(context, d, ptr, block, activeRequests) {
302
+ if (d.type === OP_COHORT) return inflateKeyCohort(context, d, ptr, block, activeRequests)
303
+ return inflateKeyDelta(context, d, ptr, block, activeRequests)
304
+ }
305
+
306
+ async function inflateKeyDelta(context, d, ptr, block, activeRequests) {
307
+ const k = d.pointer
308
+
309
+ if (!k) return new DeltaOp(false, d.type, d.index, null)
310
+
311
+ const blk =
312
+ k.seq === ptr.seq && k.core === 0 && ptr.core === 0
313
+ ? block
314
+ : await context.getBlock(k.seq, k.core, activeRequests)
315
+
316
+ const bk = blk.keys[k.offset]
317
+
318
+ let vp = null
319
+
320
+ if (bk.valuePointer) {
321
+ const p = bk.valuePointer
322
+ const ctx = await context.getContext(k.core, activeRequests)
323
+ vp = new ValuePointer(ctx, p.core, p.seq, p.offset, p.split)
324
+ }
325
+
326
+ const kp = new KeyPointer(context, k.core, k.seq, k.offset, false, bk.key, bk.value, vp)
327
+ return new DeltaOp(false, d.type, d.index, kp)
328
+ }
329
+
330
+ async function inflateKeyCohort(context, d, ptr, block, activeRequests) {
331
+ const co = d.pointer
332
+
333
+ const blk =
334
+ co.seq === ptr.seq && co.core === 0 && ptr.core === 0
335
+ ? block
336
+ : await context.getBlock(co.seq, co.core, activeRequests)
337
+
338
+ const cohort = blk.cohorts[co.offset]
339
+ const promises = new Array(cohort.length)
340
+
341
+ for (let i = 0; i < cohort.length; i++) {
342
+ const p = cohort[i]
343
+ const k = inflateKeyDelta(context, p, co, blk, activeRequests)
344
+ promises[i] = k
345
+ }
346
+
347
+ const p = new Pointer(context, co.core, co.seq, co.offset)
348
+ return new DeltaCohort(false, p, await Promise.all(promises))
349
+ }
350
+
351
+ function inflateChild(context, d, ptr, block, activeRequests) {
352
+ if (d.type === OP_COHORT) return inflateChildCohort(context, d, ptr, block, activeRequests)
353
+ return Promise.resolve(inflateChildDelta(context, d, ptr, block, activeRequests))
354
+ }
355
+
356
+ function inflateChildDelta(context, d, ptr, block, activeRequests) {
357
+ const p = d.pointer
358
+ const c = p && new TreeNodePointer(context, p.core, p.seq, p.offset, false, null)
359
+ return new DeltaOp(false, d.type, d.index, c)
360
+ }
361
+
362
+ async function inflateChildCohort(context, d, ptr, block, activeRequests) {
363
+ const co = d.pointer
364
+
365
+ const blk =
366
+ co.seq === ptr.seq && co.core === 0 && ptr.core === 0
367
+ ? block
368
+ : await context.getBlock(co.seq, co.core, activeRequests)
369
+
370
+ const cohort = blk.cohorts[co.offset]
371
+ const deltas = new Array(cohort.length)
372
+
373
+ for (let i = 0; i < cohort.length; i++) {
374
+ const c = cohort[i]
375
+ deltas[i] = inflateChildDelta(context, c, co, blk, activeRequests)
376
+ }
377
+
378
+ const p = new Pointer(context, co.core, co.seq, co.offset)
379
+ return new DeltaCohort(false, p, deltas)
380
+ }
package/lib/cache.js CHANGED
@@ -9,6 +9,13 @@ module.exports = class NodeCache {
9
9
  return this.latest ? this.latest.next : null
10
10
  }
11
11
 
12
+ empty() {
13
+ const maxSize = this.maxSize
14
+ this.maxSize = 0
15
+ this.gc()
16
+ this.maxSize = maxSize
17
+ }
18
+
12
19
  gc() {
13
20
  while (this.size > this.maxSize) {
14
21
  const old = this.oldest()
@@ -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,35 +1,55 @@
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')
5
+ const { OP_COHORT, OP_INSERT } = require('./compression')
4
6
  const varint = encodings.varint
5
7
  const skip = encodings.skip
6
8
 
7
- module.exports = decodeCompat
9
+ exports.decode = decodeCompat
10
+ exports.encode = encodeCompat
8
11
 
9
12
  function decodeCompat(buffer, seq) {
10
- const node = decodeNode(buffer)
11
- const index = decodeYoloIndex(node.index)
13
+ const node = buffer.length > 1 ? decodeNode(buffer) : null
14
+ const index = node ? decodeYoloIndex(node.index) : { levels: [] }
12
15
  const morphed = {
13
- type: 0,
16
+ type: 1,
14
17
  checkpoint: 0,
15
18
  batch: { start: 0, end: 0 },
16
19
  previous: seq > 1 ? { core: 0, seq: seq - 1 } : null,
20
+ metadata: null,
17
21
  tree: [],
18
- data: [{ key: node.key, value: node.value }],
19
- cores: null
22
+ keys: node ? [{ key: node.key, value: node.value, valuePointer: null }] : [],
23
+ values: [],
24
+ cohorts: []
20
25
  }
21
26
 
22
27
  for (const lvl of index.levels) {
23
28
  const t = { keys: [], children: [] }
24
29
 
25
- for (let i = 0; i < lvl.keys.length; i++) {
26
- const seq = lvl.keys[i]
27
- t.keys.push({ core: 0, seq, offset: 0 })
30
+ if (lvl.keys.length) {
31
+ const cohort = []
32
+ const offset = morphed.cohorts.push(cohort) - 1
33
+
34
+ for (let i = 0; i < lvl.keys.length; i++) {
35
+ const seq = lvl.keys[i]
36
+ cohort.push({ type: OP_INSERT, index: i, pointer: { core: 0, seq, offset: 0 } })
37
+ }
38
+
39
+ t.keys.push({ type: OP_COHORT, index: 0, pointer: { core: 0, seq, offset } })
28
40
  }
29
- for (let i = 0; i < lvl.children.length; i += 2) {
30
- const seq = lvl.children[i]
31
- const offset = lvl.children[i + 1]
32
- t.children.push({ core: 0, seq, offset })
41
+
42
+ if (lvl.children.length) {
43
+ const cohort = []
44
+ const offset = morphed.cohorts.push(cohort) - 1
45
+
46
+ for (let i = 0; i < lvl.children.length; i += 2) {
47
+ const seq = lvl.children[i]
48
+ const offset = lvl.children[i + 1]
49
+ cohort.push({ type: OP_INSERT, index: i / 2, pointer: { core: 0, seq, offset } })
50
+ }
51
+
52
+ t.children.push({ type: OP_COHORT, index: 0, pointer: { core: 0, seq, offset } })
33
53
  }
34
54
 
35
55
  morphed.tree.push(t)
@@ -38,6 +58,39 @@ function decodeCompat(buffer, seq) {
38
58
  return morphed
39
59
  }
40
60
 
61
+ function encodeCompat(node) {
62
+ const index = { levels: [] }
63
+
64
+ if (node.tree) {
65
+ for (const t of node.tree) {
66
+ const lvl = { keys: [], children: [] }
67
+ for (const k of t.keys) {
68
+ for (const kk of node.cohorts[k.pointer.offset]) {
69
+ lvl.keys.push(kk.pointer.seq)
70
+ }
71
+ }
72
+ for (const c of t.children) {
73
+ for (const cc of node.cohorts[c.pointer.offset]) {
74
+ lvl.children.push(cc.pointer.seq, cc.pointer.offset)
75
+ }
76
+ }
77
+ index.levels.push(lvl)
78
+ }
79
+ }
80
+
81
+ const bufIndex = b4a.allocUnsafe(encodingLengthYoloIndex(index))
82
+ encodeYoloIndex(index, bufIndex, 0)
83
+
84
+ const n = {
85
+ key: node.keys[0].key,
86
+ value: node.keys[0].value,
87
+ index: bufIndex
88
+ }
89
+
90
+ const buf = b4a.allocUnsafe(encodingLengthNode(n))
91
+ return encodeNode(n, buf, 0)
92
+ }
93
+
41
94
  function decodeLevel(buf, offset, end) {
42
95
  if (!offset) offset = 0
43
96
  if (!end) end = buf.length
@@ -86,6 +139,74 @@ function decodeLevel(buf, offset, end) {
86
139
  }
87
140
  }
88
141
 
142
+ function encodingLengthLevel(obj) {
143
+ let length = 0
144
+ if (obj.keys) {
145
+ let packedLen = 0
146
+ for (let i = 0; i < obj.keys.length; i++) {
147
+ if (!obj.keys[i]) continue
148
+ const len = encodings.varint.encodingLength(obj.keys[i])
149
+ packedLen += len
150
+ }
151
+ if (packedLen) {
152
+ length += 1 + packedLen + varint.encodingLength(packedLen)
153
+ }
154
+ }
155
+ if (obj.children) {
156
+ let packedLen = 0
157
+ for (let i = 0; i < obj.children.length; i++) {
158
+ if (!obj.children[i]) continue
159
+ const len = encodings.varint.encodingLength(obj.children[i])
160
+ packedLen += len
161
+ }
162
+ if (packedLen) {
163
+ length += 1 + packedLen + varint.encodingLength(packedLen)
164
+ }
165
+ }
166
+ return length
167
+ }
168
+
169
+ function encodeLevel(obj, buf, offset) {
170
+ if (!offset) offset = 0
171
+ const oldOffset = offset
172
+ if (obj.keys) {
173
+ let packedLen = 0
174
+ for (let i = 0; i < obj.keys.length; i++) {
175
+ if (!obj.keys[i]) continue
176
+ packedLen += encodings.varint.encodingLength(obj.keys[i])
177
+ }
178
+ if (packedLen) {
179
+ buf[offset++] = 10
180
+ varint.encode(packedLen, buf, offset)
181
+ offset += varint.encode.bytes
182
+ }
183
+ for (let i = 0; i < obj.keys.length; i++) {
184
+ if (!obj.keys[i]) continue
185
+ encodings.varint.encode(obj.keys[i], buf, offset)
186
+ offset += encodings.varint.encode.bytes
187
+ }
188
+ }
189
+ if (obj.children) {
190
+ let packedLen = 0
191
+ for (let i = 0; i < obj.children.length; i++) {
192
+ if (!obj.children[i]) continue
193
+ packedLen += encodings.varint.encodingLength(obj.children[i])
194
+ }
195
+ if (packedLen) {
196
+ buf[offset++] = 18
197
+ varint.encode(packedLen, buf, offset)
198
+ offset += varint.encode.bytes
199
+ }
200
+ for (let i = 0; i < obj.children.length; i++) {
201
+ if (!obj.children[i]) continue
202
+ encodings.varint.encode(obj.children[i], buf, offset)
203
+ offset += encodings.varint.encode.bytes
204
+ }
205
+ }
206
+ encodeLevel.bytes = offset - oldOffset
207
+ return buf
208
+ }
209
+
89
210
  function decodeYoloIndex(buf, offset, end) {
90
211
  if (!offset) offset = 0
91
212
  if (!end) end = buf.length
@@ -117,6 +238,37 @@ function decodeYoloIndex(buf, offset, end) {
117
238
  }
118
239
  }
119
240
 
241
+ function encodingLengthYoloIndex(obj) {
242
+ let length = 0
243
+ if (obj.levels) {
244
+ for (let i = 0; i < obj.levels.length; i++) {
245
+ if (!obj.levels[i]) continue
246
+ const len = encodingLengthLevel(obj.levels[i])
247
+ length += varint.encodingLength(len)
248
+ length += 1 + len
249
+ }
250
+ }
251
+ return length
252
+ }
253
+
254
+ function encodeYoloIndex(obj, buf, offset) {
255
+ if (!offset) offset = 0
256
+ const oldOffset = offset
257
+ if (obj.levels) {
258
+ for (let i = 0; i < obj.levels.length; i++) {
259
+ if (!obj.levels[i]) continue
260
+ buf[offset++] = 10
261
+ const len = encodingLengthLevel(obj.levels[i])
262
+ varint.encode(len, buf, offset)
263
+ offset += varint.encode.bytes
264
+ encodeLevel(obj.levels[i], buf, offset)
265
+ offset += encodeLevel.bytes
266
+ }
267
+ }
268
+ encodeYoloIndex.bytes = offset - oldOffset
269
+ return buf
270
+ }
271
+
120
272
  function decodeNode(buf, offset, end) {
121
273
  if (!offset) offset = 0
122
274
  if (!end) end = buf.length
@@ -162,3 +314,34 @@ function decodeNode(buf, offset, end) {
162
314
  }
163
315
  }
164
316
  }
317
+
318
+ function encodingLengthNode(obj) {
319
+ let length = 0
320
+ let len = encodings.bytes.encodingLength(obj.index)
321
+ length += 1 + len
322
+ len = encodings.bytes.encodingLength(obj.key)
323
+ length += 1 + len
324
+ if (obj.value) {
325
+ const len = encodings.bytes.encodingLength(obj.value)
326
+ length += 1 + len
327
+ }
328
+ return length
329
+ }
330
+
331
+ function encodeNode(obj, buf, offset) {
332
+ if (!offset) offset = 0
333
+ const oldOffset = offset
334
+ buf[offset++] = 10
335
+ encodings.bytes.encode(obj.index, buf, offset)
336
+ offset += encodings.bytes.encode.bytes
337
+ buf[offset++] = 18
338
+ encodings.bytes.encode(obj.key, buf, offset)
339
+ offset += encodings.bytes.encode.bytes
340
+ if (obj.value) {
341
+ buf[offset++] = 26
342
+ encodings.bytes.encode(obj.value, buf, offset)
343
+ offset += encodings.bytes.encode.bytes
344
+ }
345
+ encodeNode.bytes = offset - oldOffset
346
+ return buf
347
+ }