hyperbee2 2.2.0 → 2.4.1

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
@@ -6,14 +6,8 @@ const { ChangesStream } = require('./lib/changes.js')
6
6
  const NodeCache = require('./lib/cache.js')
7
7
  const WriteBatch = require('./lib/write.js')
8
8
  const CoreContext = require('./lib/context.js')
9
- const {
10
- Pointer,
11
- KeyPointer,
12
- ValuePointer,
13
- TreeNode,
14
- TreeNodePointer,
15
- EMPTY
16
- } = require('./lib/tree.js')
9
+ const SessionConfig = require('./lib/session-config.js')
10
+ const { Pointer, KeyPointer, ValuePointer, TreeNode, EMPTY } = require('./lib/tree.js')
17
11
  const { DeltaOp, DeltaCohort, OP_COHORT } = require('./lib/compression.js')
18
12
 
19
13
  class Hyperbee {
@@ -22,12 +16,14 @@ class Hyperbee {
22
16
  t = 128, // legacy number for now, should be 128 now
23
17
  key = null,
24
18
  encryption = null,
25
- core = key ? store.get(key) : store.get({ key, name: 'bee', encryption }),
26
- context = new CoreContext(store, core, core, encryption, t),
27
19
  maxCacheSize = 4096,
28
- cache = new NodeCache(maxCacheSize),
20
+ config = new SessionConfig([], 0, true),
21
+ activeRequests = config.activeRequests,
22
+ timeout = config.timeout,
23
+ wait = config.wait,
24
+ core = key ? store.get(key) : store.get({ key, name: 'bee', encryption }),
25
+ context = new CoreContext(store, core, new NodeCache(maxCacheSize), core, encryption, t),
29
26
  root = null,
30
- activeRequests = [],
31
27
  view = false,
32
28
  writable = true,
33
29
  unbatch = 0,
@@ -36,9 +32,8 @@ class Hyperbee {
36
32
 
37
33
  this.store = store
38
34
  this.root = root
39
- this.cache = cache
40
35
  this.context = context
41
- this.activeRequests = activeRequests
36
+ this.config = config.sub(activeRequests, timeout, wait)
42
37
  this.view = view
43
38
  this.writable = writable
44
39
  this.unbatch = unbatch
@@ -58,6 +53,10 @@ class Hyperbee {
58
53
  return { length: this.root.seq + 1, key: this.root.context.core.key }
59
54
  }
60
55
 
56
+ get cache() {
57
+ return this.context.cache
58
+ }
59
+
61
60
  get core() {
62
61
  return this.context.core
63
62
  }
@@ -84,8 +83,9 @@ class Hyperbee {
84
83
 
85
84
  _makeView(context, root, writable, unbatch) {
86
85
  return new Hyperbee(this.store, {
86
+ config: this.config,
87
+ core: context.core,
87
88
  context,
88
- cache: this.cache,
89
89
  root,
90
90
  view: true,
91
91
  writable,
@@ -95,7 +95,7 @@ class Hyperbee {
95
95
 
96
96
  checkout({ length = this.core.length, key = null, writable = false } = {}) {
97
97
  const context = key ? this.context.getContextByKey(key) : this.context
98
- const root = length === 0 ? EMPTY : new TreeNodePointer(context, 0, length - 1, 0, false, null)
98
+ const root = length === 0 ? EMPTY : context.createTreeNode(0, length - 1, 0, false, null)
99
99
  return this._makeView(context, root, writable, 0)
100
100
  }
101
101
 
@@ -119,7 +119,7 @@ class Hyperbee {
119
119
  this.root =
120
120
  this.context.core.length === 0
121
121
  ? EMPTY
122
- : new TreeNodePointer(this.context, 0, this.core.length - 1, 0, false, null)
122
+ : this.context.createTreeNode(0, this.core.length - 1, 0, false, null)
123
123
 
124
124
  if (this._autoUpdate) {
125
125
  this.core.on('append', () => {
@@ -129,7 +129,7 @@ class Hyperbee {
129
129
  }
130
130
 
131
131
  async close() {
132
- if (this.activeRequests.length) Hypercore.clearRequests(this.activeRequests)
132
+ if (this.config.activeRequests.length) Hypercore.clearRequests(this.config.activeRequests)
133
133
  if (!this.view) await this.store.close()
134
134
  }
135
135
 
@@ -163,21 +163,21 @@ class Hyperbee {
163
163
 
164
164
  bump(ptr) {
165
165
  if (ptr.changed) return ptr.value
166
- this.cache.bump(ptr)
167
- this.cache.gc()
166
+ this.context.cache.bump(ptr)
167
+ this.context.cache.gc()
168
168
  return ptr.value
169
169
  }
170
170
 
171
- // TODO: unslab these and parallize
172
- async inflate(ptr, activeRequests = this.activeRequests) {
171
+ // TODO: unslab these
172
+ async inflate(ptr, config) {
173
173
  if (ptr.value) {
174
174
  this.bump(ptr)
175
175
  return ptr.value
176
176
  }
177
177
 
178
178
  const [block, context] = await Promise.all([
179
- ptr.context.getBlock(ptr.seq, ptr.core, activeRequests),
180
- ptr.context.getContext(ptr.core, activeRequests)
179
+ ptr.context.getBlock(ptr.seq, ptr.core, config),
180
+ ptr.context.getContext(ptr.core, config)
181
181
  ])
182
182
 
183
183
  const tree = block.tree[ptr.offset]
@@ -187,15 +187,17 @@ class Hyperbee {
187
187
 
188
188
  for (let i = 0; i < keys.length; i++) {
189
189
  const d = tree.keys[i]
190
- keys[i] = inflateKey(context, d, ptr, block, activeRequests)
190
+ keys[i] = inflateKey(context, d, ptr, block, config)
191
191
  }
192
192
 
193
193
  for (let i = 0; i < children.length; i++) {
194
194
  const d = tree.children[i]
195
- children[i] = inflateChild(context, d, ptr, block, activeRequests)
195
+ children[i] = inflateChild(context, d, ptr, block, config)
196
196
  }
197
197
 
198
- const value = new TreeNode(await Promise.all(keys), await Promise.all(children))
198
+ const [k, c] = await Promise.all([Promise.all(keys), Promise.all(children)])
199
+
200
+ const value = new TreeNode(k, c)
199
201
  if (!ptr.value) ptr.value = value
200
202
 
201
203
  this.bump(ptr)
@@ -203,8 +205,8 @@ class Hyperbee {
203
205
  return ptr.value
204
206
  }
205
207
 
206
- async finalizeKeyPointer(key, activeRequests = this.activeRequests) {
207
- const value = key.value || (await this.inflateValue(key, activeRequests))
208
+ async finalizeKeyPointer(key, config) {
209
+ const value = key.value || (await this.inflateValue(key, config))
208
210
 
209
211
  return {
210
212
  core: key.context.getCore(key.core),
@@ -215,20 +217,20 @@ class Hyperbee {
215
217
  }
216
218
  }
217
219
 
218
- async inflateValue(key, activeRequests = this.activeRequests) {
220
+ async inflateValue(key, config) {
219
221
  if (key.value) return key.value
220
222
  if (!key.valuePointer) return null
221
223
 
222
224
  const ptr = key.valuePointer
223
225
 
224
226
  if (ptr.split === 0) {
225
- const block = await ptr.context.getBlock(ptr.seq, ptr.core, activeRequests)
227
+ const block = await ptr.context.getBlock(ptr.seq, ptr.core, config)
226
228
  return block.values[ptr.offset]
227
229
  }
228
230
 
229
231
  const blockPromises = new Array(ptr.split + 1)
230
232
  for (let i = 0; i < blockPromises.length; i++) {
231
- blockPromises[i] = ptr.context.getBlock(ptr.seq - ptr.split + i, ptr.core, activeRequests)
233
+ blockPromises[i] = ptr.context.getBlock(ptr.seq - ptr.split + i, ptr.core, config)
232
234
  }
233
235
  const blocks = await Promise.all(blockPromises)
234
236
  const splitValue = new Array(blockPromises.length)
@@ -239,13 +241,13 @@ class Hyperbee {
239
241
  return b4a.concat(splitValue)
240
242
  }
241
243
 
242
- async bootstrap(activeRequests = this.activeRequests) {
244
+ async bootstrap(config) {
243
245
  if (!this.root) await this.ready()
244
- if (this.unbatch) await this._rollback(activeRequests)
246
+ if (this.unbatch) await this._rollback(config)
245
247
  return this.root === EMPTY ? null : this.root
246
248
  }
247
249
 
248
- async _rollback(activeRequests) {
250
+ async _rollback(config) {
249
251
  const expected = this.unbatch
250
252
 
251
253
  let n = expected
@@ -254,21 +256,21 @@ class Hyperbee {
254
256
 
255
257
  while (n > 0 && length > 0 && expected === this.unbatch) {
256
258
  const seq = length - 1
257
- const blk = await context.getBlock(seq, 0, activeRequests)
259
+ const blk = await context.getBlock(seq, 0, config)
258
260
 
259
261
  if (!blk.previous) {
260
262
  length = 0
261
263
  break
262
264
  }
263
265
 
264
- context = await context.getContext(blk.previous.core, activeRequests)
266
+ context = await context.getContext(blk.previous.core, config)
265
267
  length = blk.previous.seq + 1
266
268
  n--
267
269
  }
268
270
 
269
271
  if (expected === this.unbatch) {
270
272
  this.context = context
271
- this.root = length === 0 ? EMPTY : new TreeNodePointer(context, 0, length - 1, 0, false, null)
273
+ this.root = length === 0 ? EMPTY : context.createTreeNode(0, length - 1, 0, false, null)
272
274
  this.unbatch = 0
273
275
  }
274
276
  }
@@ -278,12 +280,14 @@ class Hyperbee {
278
280
  this.unbatch = 0
279
281
  }
280
282
 
281
- async get(key, { activeRequests = this.activeRequests } = {}) {
282
- let ptr = await this.bootstrap(activeRequests)
283
+ async get(key, opts) {
284
+ const config = this.config.options(opts)
285
+
286
+ let ptr = await this.bootstrap(config)
283
287
  if (!ptr) return null
284
288
 
285
289
  while (true) {
286
- const v = ptr.value ? this.bump(ptr) : await this.inflate(ptr, activeRequests)
290
+ const v = ptr.value ? this.bump(ptr) : await this.inflate(ptr, config)
287
291
 
288
292
  let s = 0
289
293
  let e = v.keys.length
@@ -295,7 +299,7 @@ class Hyperbee {
295
299
 
296
300
  c = b4a.compare(key, m.key)
297
301
 
298
- if (c === 0) return this.finalizeKeyPointer(m, activeRequests)
302
+ if (c === 0) return this.finalizeKeyPointer(m, config)
299
303
 
300
304
  if (c < 0) e = mid
301
305
  else s = mid + 1
@@ -313,12 +317,12 @@ module.exports = Hyperbee
313
317
 
314
318
  function noop() {}
315
319
 
316
- function inflateKey(context, d, ptr, block, activeRequests) {
317
- if (d.type === OP_COHORT) return inflateKeyCohort(context, d, ptr, block, activeRequests)
318
- return inflateKeyDelta(context, d, ptr, block, activeRequests)
320
+ function inflateKey(context, d, ptr, block, config) {
321
+ if (d.type === OP_COHORT) return inflateKeyCohort(context, d, ptr, block, config)
322
+ return inflateKeyDelta(context, d, ptr, block, config)
319
323
  }
320
324
 
321
- async function inflateKeyDelta(context, d, ptr, block, activeRequests) {
325
+ async function inflateKeyDelta(context, d, ptr, block, config) {
322
326
  const k = d.pointer
323
327
 
324
328
  if (!k) return new DeltaOp(false, d.type, d.index, null)
@@ -326,7 +330,7 @@ async function inflateKeyDelta(context, d, ptr, block, activeRequests) {
326
330
  const blk =
327
331
  k.seq === ptr.seq && k.core === 0 && ptr.core === 0
328
332
  ? block
329
- : await context.getBlock(k.seq, k.core, activeRequests)
333
+ : await context.getBlock(k.seq, k.core, config)
330
334
 
331
335
  const bk = blk.keys[k.offset]
332
336
 
@@ -334,7 +338,7 @@ async function inflateKeyDelta(context, d, ptr, block, activeRequests) {
334
338
 
335
339
  if (bk.valuePointer) {
336
340
  const p = bk.valuePointer
337
- const ctx = await context.getContext(k.core, activeRequests)
341
+ const ctx = await context.getContext(k.core, config)
338
342
  vp = new ValuePointer(ctx, p.core, p.seq, p.offset, p.split)
339
343
  }
340
344
 
@@ -342,20 +346,20 @@ async function inflateKeyDelta(context, d, ptr, block, activeRequests) {
342
346
  return new DeltaOp(false, d.type, d.index, kp)
343
347
  }
344
348
 
345
- async function inflateKeyCohort(context, d, ptr, block, activeRequests) {
349
+ async function inflateKeyCohort(context, d, ptr, block, config) {
346
350
  const co = d.pointer
347
351
 
348
352
  const blk =
349
353
  co.seq === ptr.seq && co.core === 0 && ptr.core === 0
350
354
  ? block
351
- : await context.getBlock(co.seq, co.core, activeRequests)
355
+ : await context.getBlock(co.seq, co.core, config)
352
356
 
353
357
  const cohort = blk.cohorts[co.offset]
354
358
  const promises = new Array(cohort.length)
355
359
 
356
360
  for (let i = 0; i < cohort.length; i++) {
357
361
  const p = cohort[i]
358
- const k = inflateKeyDelta(context, p, co, blk, activeRequests)
362
+ const k = inflateKeyDelta(context, p, co, blk, config)
359
363
  promises[i] = k
360
364
  }
361
365
 
@@ -363,31 +367,31 @@ async function inflateKeyCohort(context, d, ptr, block, activeRequests) {
363
367
  return new DeltaCohort(false, p, await Promise.all(promises))
364
368
  }
365
369
 
366
- function inflateChild(context, d, ptr, block, activeRequests) {
367
- if (d.type === OP_COHORT) return inflateChildCohort(context, d, ptr, block, activeRequests)
368
- return Promise.resolve(inflateChildDelta(context, d, ptr, block, activeRequests))
370
+ function inflateChild(context, d, ptr, block, config) {
371
+ if (d.type === OP_COHORT) return inflateChildCohort(context, d, ptr, block, config)
372
+ return Promise.resolve(inflateChildDelta(context, d, ptr, block, config))
369
373
  }
370
374
 
371
- function inflateChildDelta(context, d, ptr, block, activeRequests) {
375
+ function inflateChildDelta(context, d, ptr, block, config) {
372
376
  const p = d.pointer
373
- const c = p && new TreeNodePointer(context, p.core, p.seq, p.offset, false, null)
377
+ const c = p && context.createTreeNode(p.core, p.seq, p.offset, false, null)
374
378
  return new DeltaOp(false, d.type, d.index, c)
375
379
  }
376
380
 
377
- async function inflateChildCohort(context, d, ptr, block, activeRequests) {
381
+ async function inflateChildCohort(context, d, ptr, block, config) {
378
382
  const co = d.pointer
379
383
 
380
384
  const blk =
381
385
  co.seq === ptr.seq && co.core === 0 && ptr.core === 0
382
386
  ? block
383
- : await context.getBlock(co.seq, co.core, activeRequests)
387
+ : await context.getBlock(co.seq, co.core, config)
384
388
 
385
389
  const cohort = blk.cohorts[co.offset]
386
390
  const deltas = new Array(cohort.length)
387
391
 
388
392
  for (let i = 0; i < cohort.length; i++) {
389
393
  const c = cohort[i]
390
- deltas[i] = inflateChildDelta(context, c, co, blk, activeRequests)
394
+ deltas[i] = inflateChildDelta(context, c, co, blk, config)
391
395
  }
392
396
 
393
397
  const p = new Pointer(context, co.core, co.seq, co.offset)
package/lib/cache.js CHANGED
@@ -3,6 +3,8 @@ module.exports = class NodeCache {
3
3
  this.size = 0
4
4
  this.maxSize = maxSize
5
5
  this.latest = null
6
+ this.retained = 0
7
+ this.byId = new Map()
6
8
  }
7
9
 
8
10
  oldest() {
@@ -20,7 +22,7 @@ module.exports = class NodeCache {
20
22
  gc() {
21
23
  while (this.size > this.maxSize) {
22
24
  const old = this.oldest()
23
- if (old.changed) break
25
+ if (old.retained > this.retained) break
24
26
  this.remove(old)
25
27
  old.value = null
26
28
  }
@@ -29,6 +31,10 @@ module.exports = class NodeCache {
29
31
  bump(node) {
30
32
  if (node === this.latest) return
31
33
 
34
+ if (node.next === null && node.prev === null) {
35
+ this.byId.set(cacheId(node), node)
36
+ }
37
+
32
38
  if (node.prev) this.remove(node)
33
39
  this.size++
34
40
 
@@ -44,6 +50,10 @@ module.exports = class NodeCache {
44
50
  }
45
51
  }
46
52
 
53
+ get(node) {
54
+ return this.byId.get(cacheId(node)) || null
55
+ }
56
+
47
57
  remove(node) {
48
58
  if (node.prev) {
49
59
  this.size--
@@ -57,6 +67,11 @@ module.exports = class NodeCache {
57
67
  }
58
68
 
59
69
  node.prev = node.next = null
70
+
71
+ const id = cacheId(node)
72
+ if (this.byId.get(id) === node) {
73
+ this.byId.delete(id)
74
+ }
60
75
  }
61
76
 
62
77
  *[Symbol.iterator]() {
@@ -71,3 +86,8 @@ module.exports = class NodeCache {
71
86
  } while (node !== next)
72
87
  }
73
88
  }
89
+
90
+ function cacheId(node) {
91
+ const id = node.core === 0 ? node.context.core.id : node.context.opened[node.core - 1].id
92
+ return id + '@' + node.seq + '.' + node.offset
93
+ }
package/lib/changes.js CHANGED
@@ -1,43 +1,41 @@
1
1
  const { Readable } = require('streamx')
2
2
 
3
3
  class ChangesStream extends Readable {
4
- constructor(tree, options = {}) {
5
- const { highWaterMark } = options
4
+ constructor(tree, opts = {}) {
5
+ const { highWaterMark, head = null } = opts
6
6
  super({ eagerOpen: true, highWaterMark })
7
7
 
8
- const { head = null, activeRequests = tree.activeRequests } = options
9
-
10
8
  this.tree = tree
11
9
 
12
- this._head = head
13
- this._context = null
14
- this._activeRequests = activeRequests
10
+ this.head = head
11
+ this.context = null
12
+ this.config = tree.config.options(opts)
15
13
  }
16
14
 
17
15
  async _openp() {
18
- await this.tree.bootstrap(this._activeRequests)
19
- if (this._head === null) this._head = this.tree.head()
20
- if (this._head !== null) this._context = this.tree.context.getContextByKey(this._head.key)
16
+ await this.tree.bootstrap(this.config)
17
+ if (this.head === null) this.head = this.tree.head()
18
+ if (this.head !== null) this.context = this.tree.context.getContextByKey(this.head.key)
21
19
  }
22
20
 
23
21
  async _readp() {
24
- if (!this._context || this._head.length === 0) {
22
+ if (!this.context || this.head.length === 0) {
25
23
  this.push(null)
26
24
  return
27
25
  }
28
26
 
29
27
  const data = {
30
- head: this._head,
28
+ head: this.head,
31
29
  batch: []
32
30
  }
33
31
 
34
- const seq = this._head.length - 1
35
- const blk = await this._context.getBlock(seq, 0, this._activeRequests)
32
+ const seq = this.head.length - 1
33
+ const blk = await this.context.getBlock(seq, 0, this.config)
36
34
  const batchStart = seq - blk.batch.start
37
35
  const remaining = new Array(blk.batch.start)
38
36
 
39
37
  for (let i = 0; i < remaining.length; i++) {
40
- remaining[i] = this._context.getBlock(batchStart + i, 0, this._activeRequests)
38
+ remaining[i] = this.context.getBlock(batchStart + i, 0, this.config)
41
39
  }
42
40
 
43
41
  for (const blk of await Promise.all(remaining)) data.batch.push(blk)
@@ -46,13 +44,13 @@ class ChangesStream extends Readable {
46
44
  this.push(data)
47
45
 
48
46
  if (!blk.previous) {
49
- this._head = null
50
- this._context = null
47
+ this.head = null
48
+ this.context = null
51
49
  return
52
50
  }
53
51
 
54
- this._context = await this._context.getContext(blk.previous.core, this._activeRequests)
55
- this._head = { key: this._context.core.key, length: blk.previous.seq + 1 }
52
+ this.context = await this.context.getContext(blk.previous.core, this.config)
53
+ this.head = { key: this.context.core.key, length: blk.previous.seq + 1 }
56
54
  }
57
55
 
58
56
  async _open(cb) {
@@ -23,6 +23,8 @@ class CompressedArray {
23
23
  constructor(delta) {
24
24
  this.entries = []
25
25
  this.delta = delta
26
+ this.uentries = null
27
+ this.updates = 0
26
28
 
27
29
  for (const d of delta) {
28
30
  if (d.type === OP_COHORT) {
@@ -39,8 +41,32 @@ class CompressedArray {
39
41
  return this.entries.length
40
42
  }
41
43
 
42
- touch(index) {
43
- const pointer = this.entries[index]
44
+ get ulength() {
45
+ return this.uentries ? this.uentries.length : this.entries.length
46
+ }
47
+
48
+ commit() {
49
+ const c = new CompressedArray([])
50
+
51
+ c.delta = this.delta
52
+ c.entries = this.uentries || this.entries.slice(0)
53
+
54
+ this.delta = this.delta.slice(0, this.updates)
55
+ this.uentries = null
56
+ this.updates = 0
57
+
58
+ return c
59
+ }
60
+
61
+ _update() {
62
+ if (this.uentries) return
63
+ this.uentries = this.entries.slice(0)
64
+ }
65
+
66
+ touch(index, pointer) {
67
+ if (pointer) this.entries[index] = pointer
68
+ else pointer = this.entries[index]
69
+
44
70
  if (pointer.changedBy === this) return
45
71
  this.set(index, pointer)
46
72
  }
@@ -49,8 +75,13 @@ class CompressedArray {
49
75
  return this.entries[index]
50
76
  }
51
77
 
78
+ uget(index) {
79
+ return this.uentries ? this.uentries[index] : this.entries[index]
80
+ }
81
+
52
82
  push(pointer) {
53
- this.insert(this.entries.length, pointer)
83
+ if (!this.uentries) this._update()
84
+ this.insert(this.uentries.length, pointer)
54
85
  }
55
86
 
56
87
  unshift(pointer) {
@@ -58,37 +89,43 @@ class CompressedArray {
58
89
  }
59
90
 
60
91
  pop() {
61
- if (this.entries.length === 0) return
62
- const head = this.entries[this.entries.length - 1]
63
- this.delete(this.entries.length - 1)
92
+ if (!this.uentries) this._update()
93
+ if (this.uentries.length === 0) return
94
+ const head = this.uentries[this.uentries.length - 1]
95
+ this.delete(this.uentries.length - 1)
64
96
  return head
65
97
  }
66
98
 
67
99
  shift() {
68
- if (this.entries.length === 0) return
69
- const tail = this.entries[0]
100
+ if (!this.uentries) this._update()
101
+ if (this.uentries.length === 0) return
102
+ const tail = this.uentries[0]
70
103
  this.delete(0)
71
104
  return tail
72
105
  }
73
106
 
74
107
  _touch(pointer) {
108
+ this.updates++
75
109
  if (pointer) pointer.changedBy = this
76
110
  }
77
111
 
78
112
  insert(index, pointer) {
79
- if (!insert(this.entries, index, pointer)) return
113
+ if (!this.uentries) this._update()
114
+ if (!insert(this.uentries, index, pointer)) return
80
115
  this._touch(pointer)
81
116
  this.delta.push(new DeltaOp(true, OP_INSERT, index, pointer))
82
117
  }
83
118
 
84
119
  delete(index) {
85
- if (!del(this.entries, index)) return
120
+ if (!this.uentries) this._update()
121
+ if (!del(this.uentries, index)) return
86
122
  this._touch(null)
87
123
  this.delta.push(new DeltaOp(true, OP_DEL, index, null))
88
124
  }
89
125
 
90
126
  set(index, pointer) {
91
- if (!set(this.entries, index, pointer)) return
127
+ if (!this.uentries) this._update()
128
+ if (!set(this.uentries, index, pointer)) return
92
129
  this._touch(pointer)
93
130
  this.delta.push(new DeltaOp(true, OP_SET, index, pointer))
94
131
  }