hyperbee2 2.8.0 → 2.9.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 +27 -163
- package/lib/context.js +3 -0
- package/lib/inflate.js +140 -0
- package/lib/session-config.js +17 -6
- package/lib/tree.js +16 -0
- package/lib/write.js +18 -8
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -8,8 +8,8 @@ const NodeCache = require('./lib/cache.js')
|
|
|
8
8
|
const WriteBatch = require('./lib/write.js')
|
|
9
9
|
const CoreContext = require('./lib/context.js')
|
|
10
10
|
const SessionConfig = require('./lib/session-config.js')
|
|
11
|
-
const {
|
|
12
|
-
const {
|
|
11
|
+
const { inflate, inflateValue } = require('./lib/inflate.js')
|
|
12
|
+
const { EMPTY } = require('./lib/tree.js')
|
|
13
13
|
|
|
14
14
|
class Hyperbee extends EventEmitter {
|
|
15
15
|
constructor(store, opts = {}) {
|
|
@@ -21,10 +21,11 @@ class Hyperbee extends EventEmitter {
|
|
|
21
21
|
encryption = null,
|
|
22
22
|
getEncryptionProvider = toEncryptionProvider(encryption),
|
|
23
23
|
maxCacheSize = 4096,
|
|
24
|
-
config = new SessionConfig([], 0, true),
|
|
24
|
+
config = new SessionConfig([], 0, true, null),
|
|
25
25
|
activeRequests = config.activeRequests,
|
|
26
26
|
timeout = config.timeout,
|
|
27
27
|
wait = config.wait,
|
|
28
|
+
trace = config.trace,
|
|
28
29
|
core = key
|
|
29
30
|
? store.get({ key, encryption: getEncryptionProvider(key) })
|
|
30
31
|
: store.get({ key, name: 'bee', encryption: getEncryptionProvider(key) }),
|
|
@@ -40,14 +41,14 @@ class Hyperbee extends EventEmitter {
|
|
|
40
41
|
view = false,
|
|
41
42
|
writable = true,
|
|
42
43
|
unbatch = 0,
|
|
43
|
-
autoUpdate =
|
|
44
|
+
autoUpdate = !writable && !view,
|
|
44
45
|
preload = null
|
|
45
46
|
} = opts
|
|
46
47
|
|
|
47
48
|
this.store = store
|
|
48
49
|
this.root = root
|
|
49
50
|
this.context = context
|
|
50
|
-
this.config = config.sub(activeRequests, timeout, wait)
|
|
51
|
+
this.config = config.sub(activeRequests, timeout, wait, trace)
|
|
51
52
|
this.view = view
|
|
52
53
|
this.writable = writable
|
|
53
54
|
this.unbatch = unbatch
|
|
@@ -117,12 +118,9 @@ class Hyperbee extends EventEmitter {
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
move({ length = this.core.length, key = null, writable = this.writable } = {}) {
|
|
120
|
-
|
|
121
|
-
const root = length === 0 ? EMPTY : context.createTreeNode(0, length - 1, 0, false, null)
|
|
122
|
-
this.context = context
|
|
121
|
+
this.context = key ? this.context.getContextByKey(key) : this.context
|
|
123
122
|
this.writable = writable
|
|
124
|
-
this.
|
|
125
|
-
this.emit('update')
|
|
123
|
+
this._setRoot(this._nodeAtSeq(length - 1), true)
|
|
126
124
|
}
|
|
127
125
|
|
|
128
126
|
snapshot() {
|
|
@@ -138,21 +136,23 @@ class Hyperbee extends EventEmitter {
|
|
|
138
136
|
return new WriteBatch(this, opts)
|
|
139
137
|
}
|
|
140
138
|
|
|
139
|
+
_lastNodeInCore() {
|
|
140
|
+
return this._nodeAtSeq(this.context.core.length - 1)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
_nodeAtSeq(seq) {
|
|
144
|
+
return seq < 0 ? EMPTY : this.context.createTreeNode(0, seq, 0, false, null)
|
|
145
|
+
}
|
|
146
|
+
|
|
141
147
|
async ready() {
|
|
142
148
|
if (!this.core.opened) await this.core.ready()
|
|
143
149
|
if (this.root) return
|
|
144
150
|
if (this.preload) await this.preload()
|
|
145
151
|
if (this.root) return
|
|
146
152
|
|
|
147
|
-
this.
|
|
148
|
-
this.context.core.length === 0
|
|
149
|
-
? EMPTY
|
|
150
|
-
: this.context.createTreeNode(0, this.core.length - 1, 0, false, null)
|
|
151
|
-
|
|
153
|
+
this._setRoot(this._lastNodeInCore(), false)
|
|
152
154
|
if (this.autoUpdate) {
|
|
153
|
-
this.core.on('append', () =>
|
|
154
|
-
this.update()
|
|
155
|
-
})
|
|
155
|
+
this.core.on('append', () => this._setRoot(this._lastNodeInCore(), true))
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
this.emit('ready')
|
|
@@ -202,45 +202,16 @@ class Hyperbee extends EventEmitter {
|
|
|
202
202
|
return ptr.value
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
// TODO: unslab these
|
|
206
205
|
async inflate(ptr, config) {
|
|
207
|
-
if (ptr.value) {
|
|
208
|
-
|
|
209
|
-
return ptr.value
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
const [block, context] = await Promise.all([
|
|
213
|
-
ptr.context.getBlock(ptr.seq, ptr.core, config),
|
|
214
|
-
ptr.context.getContext(ptr.core, config)
|
|
215
|
-
])
|
|
216
|
-
|
|
217
|
-
const tree = block.tree[ptr.offset]
|
|
218
|
-
|
|
219
|
-
const keys = new Array(tree.keys.length)
|
|
220
|
-
const children = new Array(tree.children.length)
|
|
221
|
-
|
|
222
|
-
for (let i = 0; i < keys.length; i++) {
|
|
223
|
-
const d = tree.keys[i]
|
|
224
|
-
keys[i] = inflateKey(context, d, ptr, block, config)
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
for (let i = 0; i < children.length; i++) {
|
|
228
|
-
const d = tree.children[i]
|
|
229
|
-
children[i] = inflateChild(context, d, ptr, block, config)
|
|
206
|
+
if (!ptr.value) {
|
|
207
|
+
await inflate(ptr, config)
|
|
230
208
|
}
|
|
231
|
-
|
|
232
|
-
const [k, c] = await Promise.all([Promise.all(keys), Promise.all(children)])
|
|
233
|
-
|
|
234
|
-
const value = new TreeNode(k, c)
|
|
235
|
-
if (!ptr.value) ptr.value = value
|
|
236
|
-
|
|
237
209
|
this.bump(ptr)
|
|
238
|
-
|
|
239
210
|
return ptr.value
|
|
240
211
|
}
|
|
241
212
|
|
|
242
213
|
async finalizeKeyPointer(key, config) {
|
|
243
|
-
const value = key.value || (await
|
|
214
|
+
const value = key.value || (await inflateValue(key, config))
|
|
244
215
|
|
|
245
216
|
return {
|
|
246
217
|
core: key.context.getCore(key.core),
|
|
@@ -251,30 +222,6 @@ class Hyperbee extends EventEmitter {
|
|
|
251
222
|
}
|
|
252
223
|
}
|
|
253
224
|
|
|
254
|
-
async inflateValue(key, config) {
|
|
255
|
-
if (key.value) return key.value
|
|
256
|
-
if (!key.valuePointer) return null
|
|
257
|
-
|
|
258
|
-
const ptr = key.valuePointer
|
|
259
|
-
|
|
260
|
-
if (ptr.split === 0) {
|
|
261
|
-
const block = await ptr.context.getBlock(ptr.seq, ptr.core, config)
|
|
262
|
-
return block.values[ptr.offset]
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const blockPromises = new Array(ptr.split + 1)
|
|
266
|
-
for (let i = 0; i < blockPromises.length; i++) {
|
|
267
|
-
blockPromises[i] = ptr.context.getBlock(ptr.seq - ptr.split + i, ptr.core, config)
|
|
268
|
-
}
|
|
269
|
-
const blocks = await Promise.all(blockPromises)
|
|
270
|
-
const splitValue = new Array(blockPromises.length)
|
|
271
|
-
for (let i = 0; i < splitValue.length - 1; i++) {
|
|
272
|
-
splitValue[i] = blocks[i].values[0]
|
|
273
|
-
}
|
|
274
|
-
splitValue[splitValue.length - 1] = blocks[blocks.length - 1].buffer[ptr.offset]
|
|
275
|
-
return b4a.concat(splitValue)
|
|
276
|
-
}
|
|
277
|
-
|
|
278
225
|
async bootstrap(config) {
|
|
279
226
|
if (!this.root) await this.ready()
|
|
280
227
|
if (this.unbatch) await this._rollback(config)
|
|
@@ -304,16 +251,16 @@ class Hyperbee extends EventEmitter {
|
|
|
304
251
|
|
|
305
252
|
if (expected === this.unbatch) {
|
|
306
253
|
this.context = context
|
|
307
|
-
this.
|
|
308
|
-
this.unbatch = 0
|
|
309
|
-
this.emit('update')
|
|
254
|
+
this._setRoot(this._nodeAtSeq(length - 1), true)
|
|
310
255
|
}
|
|
311
256
|
}
|
|
312
257
|
|
|
313
|
-
|
|
314
|
-
this.root
|
|
258
|
+
_setRoot(root, emit) {
|
|
259
|
+
if (!root.equivalentTo(this.root)) {
|
|
260
|
+
this.root = root
|
|
261
|
+
if (emit) this.emit('update')
|
|
262
|
+
}
|
|
315
263
|
this.unbatch = 0
|
|
316
|
-
this.emit('update')
|
|
317
264
|
}
|
|
318
265
|
|
|
319
266
|
async get(key, opts) {
|
|
@@ -353,89 +300,6 @@ module.exports = Hyperbee
|
|
|
353
300
|
|
|
354
301
|
function noop() {}
|
|
355
302
|
|
|
356
|
-
function inflateKey(context, d, ptr, block, config) {
|
|
357
|
-
if (d.type === OP_COHORT) return inflateKeyCohort(context, d, ptr, block, config)
|
|
358
|
-
return inflateKeyDelta(context, d, ptr, block, config)
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
async function inflateKeyDelta(context, d, ptr, block, config) {
|
|
362
|
-
const k = d.pointer
|
|
363
|
-
|
|
364
|
-
if (!k) return new DeltaOp(false, d.type, d.index, null)
|
|
365
|
-
|
|
366
|
-
const blk =
|
|
367
|
-
k.seq === ptr.seq && k.core === 0 && ptr.core === 0
|
|
368
|
-
? block
|
|
369
|
-
: await context.getBlock(k.seq, k.core, config)
|
|
370
|
-
|
|
371
|
-
const bk = blk.keys[k.offset]
|
|
372
|
-
|
|
373
|
-
let vp = null
|
|
374
|
-
|
|
375
|
-
if (bk.valuePointer) {
|
|
376
|
-
const p = bk.valuePointer
|
|
377
|
-
const ctx = await context.getContext(k.core, config)
|
|
378
|
-
vp = new ValuePointer(ctx, p.core, p.seq, p.offset, p.split)
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
const kp = new KeyPointer(context, k.core, k.seq, k.offset, false, bk.key, bk.value, vp)
|
|
382
|
-
return new DeltaOp(false, d.type, d.index, kp)
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
async function inflateKeyCohort(context, d, ptr, block, config) {
|
|
386
|
-
const co = d.pointer
|
|
387
|
-
|
|
388
|
-
const blk =
|
|
389
|
-
co.seq === ptr.seq && co.core === 0 && ptr.core === 0
|
|
390
|
-
? block
|
|
391
|
-
: await context.getBlock(co.seq, co.core, config)
|
|
392
|
-
|
|
393
|
-
const cohort = blk.cohorts[co.offset]
|
|
394
|
-
const promises = new Array(cohort.length)
|
|
395
|
-
|
|
396
|
-
for (let i = 0; i < cohort.length; i++) {
|
|
397
|
-
const p = cohort[i]
|
|
398
|
-
const k = inflateKeyDelta(context, p, co, blk, config)
|
|
399
|
-
promises[i] = k
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
const p = new Pointer(context, co.core, co.seq, co.offset)
|
|
403
|
-
return new DeltaCohort(false, p, await Promise.all(promises))
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
async function inflateChild(context, d, ptr, block, config) {
|
|
407
|
-
if (d.type === OP_COHORT) return inflateChildCohort(context, d, ptr, block, config)
|
|
408
|
-
if (d.pointer && !context.hasCore(d.pointer.core)) await context.update(config)
|
|
409
|
-
return inflateChildDelta(context, d, ptr, block, config)
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
function inflateChildDelta(context, d, ptr, block, config) {
|
|
413
|
-
const p = d.pointer
|
|
414
|
-
const c = p && context.createTreeNode(p.core, p.seq, p.offset, false, null)
|
|
415
|
-
return new DeltaOp(false, d.type, d.index, c)
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
async function inflateChildCohort(context, d, ptr, block, config) {
|
|
419
|
-
const co = d.pointer
|
|
420
|
-
|
|
421
|
-
const blk =
|
|
422
|
-
co.seq === ptr.seq && co.core === 0 && ptr.core === 0
|
|
423
|
-
? block
|
|
424
|
-
: await context.getBlock(co.seq, co.core, config)
|
|
425
|
-
|
|
426
|
-
const cohort = blk.cohorts[co.offset]
|
|
427
|
-
const deltas = new Array(cohort.length)
|
|
428
|
-
|
|
429
|
-
for (let i = 0; i < cohort.length; i++) {
|
|
430
|
-
const c = cohort[i]
|
|
431
|
-
if (c.pointer && !context.hasCore(c.pointer.core)) await context.update(config)
|
|
432
|
-
deltas[i] = inflateChildDelta(context, c, co, blk, config)
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
const p = new Pointer(context, co.core, co.seq, co.offset)
|
|
436
|
-
return new DeltaCohort(false, p, deltas)
|
|
437
|
-
}
|
|
438
|
-
|
|
439
303
|
function toEncryptionProvider(encryption) {
|
|
440
304
|
if (encryption) return (key) => encryption
|
|
441
305
|
return () => null
|
package/lib/context.js
CHANGED
|
@@ -161,6 +161,9 @@ class CoreContext {
|
|
|
161
161
|
const hc = this.getCore(core)
|
|
162
162
|
const buffer = await hc.get(seq, config)
|
|
163
163
|
if (buffer === null) throw BLOCK_NOT_AVAILABLE()
|
|
164
|
+
|
|
165
|
+
if (config.trace !== null) config.trace(core, seq)
|
|
166
|
+
|
|
164
167
|
const block = decodeBlock(buffer, seq)
|
|
165
168
|
return block
|
|
166
169
|
}
|
package/lib/inflate.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
const b4a = require('b4a')
|
|
2
|
+
const { Pointer, KeyPointer, ValuePointer, TreeNode } = require('./tree.js')
|
|
3
|
+
const { DeltaOp, DeltaCohort, OP_COHORT } = require('./compression.js')
|
|
4
|
+
|
|
5
|
+
exports.inflate = async function inflate(ptr, config) {
|
|
6
|
+
if (ptr.value) return ptr.value
|
|
7
|
+
|
|
8
|
+
const [block, context] = await Promise.all([
|
|
9
|
+
ptr.context.getBlock(ptr.seq, ptr.core, config),
|
|
10
|
+
ptr.context.getContext(ptr.core, config)
|
|
11
|
+
])
|
|
12
|
+
|
|
13
|
+
const tree = block.tree[ptr.offset]
|
|
14
|
+
|
|
15
|
+
const keys = new Array(tree.keys.length)
|
|
16
|
+
const children = new Array(tree.children.length)
|
|
17
|
+
|
|
18
|
+
for (let i = 0; i < keys.length; i++) {
|
|
19
|
+
const d = tree.keys[i]
|
|
20
|
+
keys[i] = inflateKey(context, d, ptr, block, config)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (let i = 0; i < children.length; i++) {
|
|
24
|
+
const d = tree.children[i]
|
|
25
|
+
children[i] = inflateChild(context, d, ptr, block, config)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const [k, c] = await Promise.all([Promise.all(keys), Promise.all(children)])
|
|
29
|
+
|
|
30
|
+
const value = new TreeNode(k, c)
|
|
31
|
+
if (!ptr.value) ptr.value = value
|
|
32
|
+
return ptr.value
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function inflateKey(context, d, ptr, block, config) {
|
|
36
|
+
if (d.type === OP_COHORT) return inflateKeyCohort(context, d, ptr, block, config)
|
|
37
|
+
return inflateKeyDelta(context, d, ptr, block, config)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function inflateKeyDelta(context, d, ptr, block, config) {
|
|
41
|
+
const k = d.pointer
|
|
42
|
+
|
|
43
|
+
if (!k) return new DeltaOp(false, d.type, d.index, null)
|
|
44
|
+
|
|
45
|
+
const blk =
|
|
46
|
+
k.seq === ptr.seq && k.core === 0 && ptr.core === 0
|
|
47
|
+
? block
|
|
48
|
+
: await context.getBlock(k.seq, k.core, config)
|
|
49
|
+
|
|
50
|
+
const bk = blk.keys[k.offset]
|
|
51
|
+
|
|
52
|
+
let vp = null
|
|
53
|
+
|
|
54
|
+
if (bk.valuePointer) {
|
|
55
|
+
const p = bk.valuePointer
|
|
56
|
+
const ctx = await context.getContext(k.core, config)
|
|
57
|
+
vp = new ValuePointer(ctx, p.core, p.seq, p.offset, p.split)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const kp = new KeyPointer(context, k.core, k.seq, k.offset, false, bk.key, bk.value, vp)
|
|
61
|
+
return new DeltaOp(false, d.type, d.index, kp)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
exports.inflateValue = async function inflateValue(key, config) {
|
|
65
|
+
if (key.value) return key.value
|
|
66
|
+
if (!key.valuePointer) return null
|
|
67
|
+
|
|
68
|
+
const ptr = key.valuePointer
|
|
69
|
+
|
|
70
|
+
if (ptr.split === 0) {
|
|
71
|
+
const block = await ptr.context.getBlock(ptr.seq, ptr.core, config)
|
|
72
|
+
return block.values[ptr.offset]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const blockPromises = new Array(ptr.split + 1)
|
|
76
|
+
for (let i = 0; i < blockPromises.length; i++) {
|
|
77
|
+
blockPromises[i] = ptr.context.getBlock(ptr.seq - ptr.split + i, ptr.core, config)
|
|
78
|
+
}
|
|
79
|
+
const blocks = await Promise.all(blockPromises)
|
|
80
|
+
const splitValue = new Array(blockPromises.length)
|
|
81
|
+
for (let i = 0; i < splitValue.length - 1; i++) {
|
|
82
|
+
splitValue[i] = blocks[i].values[0]
|
|
83
|
+
}
|
|
84
|
+
splitValue[splitValue.length - 1] = blocks[blocks.length - 1].buffer[ptr.offset]
|
|
85
|
+
return b4a.concat(splitValue)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function inflateKeyCohort(context, d, ptr, block, config) {
|
|
89
|
+
const co = d.pointer
|
|
90
|
+
|
|
91
|
+
const blk =
|
|
92
|
+
co.seq === ptr.seq && co.core === 0 && ptr.core === 0
|
|
93
|
+
? block
|
|
94
|
+
: await context.getBlock(co.seq, co.core, config)
|
|
95
|
+
|
|
96
|
+
const cohort = blk.cohorts[co.offset]
|
|
97
|
+
const promises = new Array(cohort.length)
|
|
98
|
+
|
|
99
|
+
for (let i = 0; i < cohort.length; i++) {
|
|
100
|
+
const p = cohort[i]
|
|
101
|
+
const k = inflateKeyDelta(context, p, co, blk, config)
|
|
102
|
+
promises[i] = k
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const p = new Pointer(context, co.core, co.seq, co.offset)
|
|
106
|
+
return new DeltaCohort(false, p, await Promise.all(promises))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function inflateChild(context, d, ptr, block, config) {
|
|
110
|
+
if (d.type === OP_COHORT) return inflateChildCohort(context, d, ptr, block, config)
|
|
111
|
+
if (d.pointer && !context.hasCore(d.pointer.core)) await context.update(config)
|
|
112
|
+
return inflateChildDelta(context, d, ptr, block, config)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function inflateChildDelta(context, d, ptr, block, config) {
|
|
116
|
+
const p = d.pointer
|
|
117
|
+
const c = p && context.createTreeNode(p.core, p.seq, p.offset, false, null)
|
|
118
|
+
return new DeltaOp(false, d.type, d.index, c)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function inflateChildCohort(context, d, ptr, block, config) {
|
|
122
|
+
const co = d.pointer
|
|
123
|
+
|
|
124
|
+
const blk =
|
|
125
|
+
co.seq === ptr.seq && co.core === 0 && ptr.core === 0
|
|
126
|
+
? block
|
|
127
|
+
: await context.getBlock(co.seq, co.core, config)
|
|
128
|
+
|
|
129
|
+
const cohort = blk.cohorts[co.offset]
|
|
130
|
+
const deltas = new Array(cohort.length)
|
|
131
|
+
|
|
132
|
+
for (let i = 0; i < cohort.length; i++) {
|
|
133
|
+
const c = cohort[i]
|
|
134
|
+
if (c.pointer && !context.hasCore(c.pointer.core)) await context.update(config)
|
|
135
|
+
deltas[i] = inflateChildDelta(context, c, co, blk, config)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const p = new Pointer(context, co.core, co.seq, co.offset)
|
|
139
|
+
return new DeltaCohort(false, p, deltas)
|
|
140
|
+
}
|
package/lib/session-config.js
CHANGED
|
@@ -1,22 +1,33 @@
|
|
|
1
1
|
class SessionConfig {
|
|
2
|
-
constructor(activeRequests, timeout, wait) {
|
|
2
|
+
constructor(activeRequests, timeout, wait, trace) {
|
|
3
3
|
this.activeRequests = activeRequests
|
|
4
4
|
this.timeout = timeout
|
|
5
5
|
this.wait = wait
|
|
6
|
+
this.trace = trace
|
|
6
7
|
}
|
|
7
8
|
|
|
8
|
-
sub(activeRequests, timeout, wait) {
|
|
9
|
-
if (
|
|
9
|
+
sub(activeRequests, timeout, wait, trace) {
|
|
10
|
+
if (
|
|
11
|
+
this.activeRequests === activeRequests &&
|
|
12
|
+
this.timeout === timeout &&
|
|
13
|
+
this.wait === wait &&
|
|
14
|
+
this.trace === trace
|
|
15
|
+
) {
|
|
10
16
|
return this
|
|
11
17
|
}
|
|
12
18
|
|
|
13
|
-
return new SessionConfig(activeRequests, timeout, wait)
|
|
19
|
+
return new SessionConfig(activeRequests, timeout, wait, trace)
|
|
14
20
|
}
|
|
15
21
|
|
|
16
22
|
options(opts) {
|
|
17
23
|
if (!opts) return this
|
|
18
|
-
const {
|
|
19
|
-
|
|
24
|
+
const {
|
|
25
|
+
activeRequests = this.activeRequests,
|
|
26
|
+
timeout = this.timeout,
|
|
27
|
+
wait = this.wait,
|
|
28
|
+
trace = this.trace
|
|
29
|
+
} = opts
|
|
30
|
+
return this.sub(activeRequests, timeout, wait, trace)
|
|
20
31
|
}
|
|
21
32
|
}
|
|
22
33
|
|
package/lib/tree.js
CHANGED
|
@@ -28,6 +28,22 @@ class Pointer {
|
|
|
28
28
|
this.changedBy = null
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// Compare two pointers to see if they point to equivalent positions
|
|
32
|
+
equivalentTo(other) {
|
|
33
|
+
// EMPTY is a special case that can look equivalent to a first entry
|
|
34
|
+
// in a hypercore but actually contains different data.
|
|
35
|
+
if (other === exports.EMPTY) return this === exports.EMPTY
|
|
36
|
+
if (!other) return false
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
this.seq === other.seq &&
|
|
40
|
+
this.offset === other.offset &&
|
|
41
|
+
this.changed === other.changed &&
|
|
42
|
+
this.context === other.context &&
|
|
43
|
+
this.core === other.core
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
31
47
|
retain() {
|
|
32
48
|
this.retained = this.context.cache.retained + 1
|
|
33
49
|
}
|
package/lib/write.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const b4a = require('b4a')
|
|
2
|
-
const c = require('compact-encoding')
|
|
3
2
|
const { OP_COHORT } = require('./compression.js')
|
|
4
3
|
const { encodeBlock, TYPE_COMPAT, TYPE_LATEST } = require('./encoding.js')
|
|
5
4
|
const { Pointer, KeyPointer, ValuePointer, TreeNode, INSERTED, NEEDS_SPLIT } = require('./tree.js')
|
|
5
|
+
const { inflateValue } = require('./inflate.js')
|
|
6
6
|
|
|
7
7
|
const PREFERRED_BLOCK_SIZE = 4096
|
|
8
8
|
const INLINE_VALUE_SIZE = 1024
|
|
@@ -41,14 +41,17 @@ module.exports = class WriteBatch {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
tryPut(key, value) {
|
|
44
|
+
this.checkIfClosed()
|
|
44
45
|
this.ops.push({ put: true, applied: false, key, value })
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
tryDelete(key) {
|
|
49
|
+
this.checkIfClosed()
|
|
48
50
|
this.ops.push({ put: false, applied: false, key, value: null })
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
tryClear() {
|
|
54
|
+
this.checkIfClosed()
|
|
52
55
|
this.ops = []
|
|
53
56
|
this.length = 0
|
|
54
57
|
}
|
|
@@ -76,9 +79,12 @@ module.exports = class WriteBatch {
|
|
|
76
79
|
}
|
|
77
80
|
|
|
78
81
|
async flush() {
|
|
82
|
+
this.checkIfClosed()
|
|
79
83
|
await this.lock()
|
|
80
84
|
|
|
81
85
|
try {
|
|
86
|
+
this.checkIfClosed()
|
|
87
|
+
|
|
82
88
|
const ops = this.ops
|
|
83
89
|
|
|
84
90
|
const root = await this.tree.bootstrap(this.config)
|
|
@@ -100,10 +106,10 @@ module.exports = class WriteBatch {
|
|
|
100
106
|
}
|
|
101
107
|
|
|
102
108
|
await this._flush()
|
|
103
|
-
await this.
|
|
109
|
+
await this.close()
|
|
104
110
|
|
|
105
111
|
if (this.autoUpdate) {
|
|
106
|
-
this.tree.
|
|
112
|
+
this.tree._setRoot(this.root, true)
|
|
107
113
|
}
|
|
108
114
|
} finally {
|
|
109
115
|
this._unlock()
|
|
@@ -140,7 +146,7 @@ module.exports = class WriteBatch {
|
|
|
140
146
|
c = b4a.compare(target, m.key)
|
|
141
147
|
|
|
142
148
|
if (c === 0) {
|
|
143
|
-
const existing = await
|
|
149
|
+
const existing = await inflateValue(m, conf)
|
|
144
150
|
if (b4a.equals(existing, value)) return false
|
|
145
151
|
v.setValue(this.tree.context, mid, value)
|
|
146
152
|
for (let i = 0; i < stack.length; i++) stack[i].changed = true
|
|
@@ -161,7 +167,7 @@ module.exports = class WriteBatch {
|
|
|
161
167
|
if (status >= 0) {
|
|
162
168
|
// already exists, upsert if changed
|
|
163
169
|
const m = v.keys.uget(status)
|
|
164
|
-
const existing = await
|
|
170
|
+
const existing = await inflateValue(m, conf)
|
|
165
171
|
if (b4a.equals(existing, value)) return false
|
|
166
172
|
v.setValue(this.tree.context, status, value)
|
|
167
173
|
}
|
|
@@ -517,9 +523,7 @@ module.exports = class WriteBatch {
|
|
|
517
523
|
buffers[i] = encodeBlock(blocks[i])
|
|
518
524
|
}
|
|
519
525
|
|
|
520
|
-
|
|
521
|
-
throw new Error('Write batch is closed')
|
|
522
|
-
}
|
|
526
|
+
this.checkIfClosed()
|
|
523
527
|
|
|
524
528
|
await context.core.append(buffers)
|
|
525
529
|
|
|
@@ -535,6 +539,12 @@ module.exports = class WriteBatch {
|
|
|
535
539
|
context.cache.retained++
|
|
536
540
|
context.cache.gc()
|
|
537
541
|
}
|
|
542
|
+
|
|
543
|
+
checkIfClosed() {
|
|
544
|
+
if (this.closed) {
|
|
545
|
+
throw new Error('Write batch is closed')
|
|
546
|
+
}
|
|
547
|
+
}
|
|
538
548
|
}
|
|
539
549
|
|
|
540
550
|
async function toCompatType(context, batch, ops) {
|