hypercore 10.37.0 → 10.37.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/lib/batch.js CHANGED
@@ -25,6 +25,7 @@ module.exports = class HypercoreBatch extends EventEmitter {
25
25
  this._sessionLength = 0
26
26
  this._sessionByteLength = 0
27
27
  this._sessionBatch = null
28
+ this._cachedBatch = null
28
29
  this._flushing = null
29
30
  this._clear = clear
30
31
 
@@ -207,20 +208,32 @@ module.exports = class HypercoreBatch extends EventEmitter {
207
208
  return this.session.core.tree.restoreBatch(length)
208
209
  }
209
210
 
210
- createTreeBatch (length, blocks = []) {
211
+ _catchupBatch (clone) {
212
+ if (this._cachedBatch === null) this._cachedBatch = this._sessionBatch.clone()
213
+
214
+ if (this.length > this._cachedBatch.length) {
215
+ const offset = this._cachedBatch.length - this._sessionBatch.length
216
+
217
+ for (let i = offset; i < this._appendsActual.length; i++) {
218
+ this._cachedBatch.append(this._appendsActual[i])
219
+ }
220
+ }
221
+
222
+ return clone ? this._cachedBatch.clone() : this._cachedBatch
223
+ }
224
+
225
+ createTreeBatch (length, opts = {}) {
226
+ if (Array.isArray(opts)) opts = { blocks: opts }
227
+
228
+ const { blocks = [], clone = true } = opts
211
229
  if (!length && length !== 0) length = this.length + blocks.length
212
230
 
213
231
  const maxLength = this.length + blocks.length
214
- const b = this._sessionBatch.clone()
232
+ const b = this._catchupBatch(clone || (blocks.length > 0 || length !== this.length))
215
233
  const len = Math.min(length, this.length)
216
234
 
217
235
  if (len < this._sessionLength || length > maxLength) return null
218
-
219
- for (let i = 0; i < len - this._sessionLength; i++) {
220
- b.append(this._appendsActual[i])
221
- }
222
-
223
- if (len < this.length) return b
236
+ if (len < b.length) b.checkout(len, this._sessionBatch.roots)
224
237
 
225
238
  for (let i = 0; i < length - len; i++) {
226
239
  b.append(this._appendsActual === this._appends ? blocks[i] : this._encrypt(b.length, blocks[i]))
@@ -239,6 +252,8 @@ module.exports = class HypercoreBatch extends EventEmitter {
239
252
  if (typeof opts === 'number') opts = { fork: opts }
240
253
  const { fork = this.fork + 1, force = false } = opts
241
254
 
255
+ this._cachedBatch = null
256
+
242
257
  const length = this._sessionLength
243
258
  if (newLength < length) {
244
259
  if (!force) throw new Error('Cannot truncate committed blocks')
@@ -380,6 +395,8 @@ module.exports = class HypercoreBatch extends EventEmitter {
380
395
  this._sessionByteLength = info.byteLength
381
396
  this._sessionBatch = newBatch
382
397
 
398
+ if (this._cachedBatch !== null) this._cachedBatch.prune(info.length)
399
+
383
400
  const same = this._appends === this._appendsActual
384
401
 
385
402
  this._appends = this._appends.slice(flushingLength)
@@ -57,6 +57,68 @@ class MerkleTreeBatch {
57
57
  this.upgraded = false
58
58
  }
59
59
 
60
+ checkout (length, additionalRoots) {
61
+ const roots = []
62
+ let r = 0
63
+
64
+ const head = 2 * length - 2
65
+ const gaps = new Set()
66
+ const all = new Map()
67
+
68
+ // additional roots is so the original roots can be passed (we mutate the array in appendRoot)
69
+ if (additionalRoots) {
70
+ for (const node of additionalRoots) all.set(node.index, node)
71
+ }
72
+
73
+ for (const node of this.nodes) all.set(node.index, node)
74
+
75
+ for (const index of flat.fullRoots(head + 2)) {
76
+ const left = flat.leftSpan(index)
77
+ if (left !== 0) gaps.add(left - 1)
78
+
79
+ if (r < this.roots.length && this.roots[r].index === index) {
80
+ roots.push(this.roots[r++])
81
+ continue
82
+ }
83
+ const node = all.get(index)
84
+ if (!node) throw new BAD_ARGUMENT('root missing for given length')
85
+ roots.push(node)
86
+ }
87
+
88
+ this.roots = roots
89
+ this.length = length
90
+ this.byteLength = totalSize(roots)
91
+ this.hashCached = null
92
+ this.signature = null
93
+
94
+ for (let i = 0; i < this.nodes.length; i++) {
95
+ const index = this.nodes[i].index
96
+ if (index <= head && !gaps.has(index)) continue
97
+ const last = this.nodes.pop()
98
+ if (i < this.nodes.length) this.nodes[i--] = last
99
+ }
100
+ }
101
+
102
+ prune (length) {
103
+ if (length === 0) return
104
+
105
+ const head = 2 * length - 2
106
+ const gaps = new Set()
107
+
108
+ // TODO: make a function for this in flat-tree
109
+ for (const index of flat.fullRoots(head + 2)) {
110
+ const left = flat.leftSpan(index)
111
+ if (left !== 0) gaps.add(left - 1)
112
+ }
113
+
114
+ for (let i = 0; i < this.nodes.length; i++) {
115
+ const index = this.nodes[i].index
116
+ if (index > head || gaps.has(index)) continue
117
+ const last = this.nodes.pop()
118
+ if (i < this.nodes.length) this.nodes[i--] = last
119
+ }
120
+ }
121
+
60
122
  clone () {
61
123
  const b = new MerkleTreeBatch(this.tree)
62
124
 
@@ -230,7 +230,11 @@ module.exports = class RemoteBitfield {
230
230
  i++
231
231
  }
232
232
 
233
- return val ? -1 : this._maxSegments * BITS_PER_SEGMENT
233
+ // For the val === false case, we always return at least
234
+ // the 'position', also if nothing was found
235
+ return val
236
+ ? -1
237
+ : Math.max(position, this._maxSegments * BITS_PER_SEGMENT)
234
238
  }
235
239
 
236
240
  firstSet (position) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.37.0",
3
+ "version": "10.37.1",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {