hypercore 10.0.0-alpha.53 → 10.0.0-alpha.56
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/README.md +1 -1
- package/index.js +43 -32
- package/lib/bitfield.js +51 -39
- package/lib/core.js +54 -27
- package/lib/remote-bitfield.js +28 -7
- package/lib/replicator.js +57 -23
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -152,7 +152,7 @@ core.download({ start: 0, end: -1 })
|
|
|
152
152
|
To downloaded a discrete range of blocks pass a list of indices.
|
|
153
153
|
|
|
154
154
|
```js
|
|
155
|
-
core.download({ blocks: [4, 9, 7] })
|
|
155
|
+
core.download({ blocks: [4, 9, 7] })
|
|
156
156
|
```
|
|
157
157
|
|
|
158
158
|
To cancel downloading a range simply destroy the range instance.
|
package/index.js
CHANGED
|
@@ -69,6 +69,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
69
69
|
this.auth = opts.auth || null
|
|
70
70
|
this.autoClose = !!opts.autoClose
|
|
71
71
|
this.onwait = opts.onwait || null
|
|
72
|
+
this.wait = opts.wait !== false
|
|
72
73
|
|
|
73
74
|
this.closing = null
|
|
74
75
|
this.opening = this._openSession(key, storage, opts)
|
|
@@ -194,11 +195,13 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
194
195
|
}
|
|
195
196
|
|
|
196
197
|
const sparse = opts.sparse === false ? false : this.sparse
|
|
198
|
+
const wait = opts.wait === false ? false : this.wait
|
|
197
199
|
const onwait = opts.onwait === undefined ? this.onwait : opts.onwait
|
|
198
200
|
const Clz = opts.class || Hypercore
|
|
199
201
|
const s = new Clz(this.storage, this.key, {
|
|
200
202
|
...opts,
|
|
201
203
|
sparse,
|
|
204
|
+
wait,
|
|
202
205
|
onwait,
|
|
203
206
|
_opening: this.opening,
|
|
204
207
|
_sessions: this.sessions
|
|
@@ -312,8 +315,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
312
315
|
crypto: this.crypto,
|
|
313
316
|
legacy: opts.legacy,
|
|
314
317
|
auth: opts.auth,
|
|
315
|
-
onupdate: this._oncoreupdate.bind(this)
|
|
316
|
-
oncontigupdate: this._oncorecontigupdate.bind(this)
|
|
318
|
+
onupdate: this._oncoreupdate.bind(this)
|
|
317
319
|
})
|
|
318
320
|
|
|
319
321
|
if (opts.userData) {
|
|
@@ -496,30 +498,50 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
496
498
|
|
|
497
499
|
_oncoreupdate (status, bitfield, value, from) {
|
|
498
500
|
if (status !== 0) {
|
|
499
|
-
const
|
|
500
|
-
const
|
|
501
|
+
const truncatedNonSparse = (status & 0b1000) !== 0
|
|
502
|
+
const appendedNonSparse = (status & 0b0100) !== 0
|
|
503
|
+
const truncated = (status & 0b0010) !== 0
|
|
504
|
+
const appended = (status & 0b0001) !== 0
|
|
501
505
|
|
|
502
506
|
if (truncated) {
|
|
503
507
|
this.replicator.ontruncate(bitfield.start)
|
|
504
508
|
}
|
|
505
509
|
|
|
510
|
+
if ((status & 0b0011) !== 0) {
|
|
511
|
+
this.replicator.onupgrade()
|
|
512
|
+
}
|
|
513
|
+
|
|
506
514
|
for (let i = 0; i < this.sessions.length; i++) {
|
|
507
515
|
const s = this.sessions[i]
|
|
508
516
|
|
|
509
517
|
if (truncated) {
|
|
510
518
|
if (s.cache) s.cache.clear()
|
|
511
|
-
|
|
519
|
+
|
|
512
520
|
// If snapshotted, make sure to update our compat so we can fail gets
|
|
513
521
|
if (s._snapshot && bitfield.start < s._snapshot.compatLength) s._snapshot.compatLength = bitfield.start
|
|
514
522
|
}
|
|
515
523
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
524
|
+
if (s.sparse ? truncated : truncatedNonSparse) {
|
|
525
|
+
s.emit('truncate', bitfield.start, this.core.tree.fork)
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// For sparse sessions, immediately emit appends. If non-sparse, emit if contig length has updated
|
|
529
|
+
if (s.sparse ? appended : appendedNonSparse) {
|
|
530
|
+
s.emit('append')
|
|
531
|
+
}
|
|
520
532
|
}
|
|
521
533
|
|
|
522
|
-
this.
|
|
534
|
+
const contig = this.core.header.contiguousLength
|
|
535
|
+
|
|
536
|
+
// When the contig length catches up, broadcast the non-sparse length to peers
|
|
537
|
+
if (appendedNonSparse && contig === this.core.tree.length) {
|
|
538
|
+
for (const peer of this.peers) {
|
|
539
|
+
if (peer.broadcastedNonSparse) continue
|
|
540
|
+
|
|
541
|
+
peer.broadcastRange(0, contig)
|
|
542
|
+
peer.broadcastedNonSparse = true
|
|
543
|
+
}
|
|
544
|
+
}
|
|
523
545
|
}
|
|
524
546
|
|
|
525
547
|
if (bitfield) {
|
|
@@ -535,16 +557,6 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
535
557
|
}
|
|
536
558
|
}
|
|
537
559
|
|
|
538
|
-
_oncorecontigupdate () {
|
|
539
|
-
// For non-sparse sessions, emit appends only when the contiguous length is
|
|
540
|
-
// updated.
|
|
541
|
-
for (let i = 0; i < this.sessions.length; i++) {
|
|
542
|
-
const s = this.sessions[i]
|
|
543
|
-
|
|
544
|
-
if (!s.sparse) s.emit('append')
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
560
|
_onpeerupdate (added, peer) {
|
|
549
561
|
const name = added ? 'peer-add' : 'peer-remove'
|
|
550
562
|
|
|
@@ -665,29 +677,26 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
665
677
|
}
|
|
666
678
|
|
|
667
679
|
async _get (index, opts) {
|
|
668
|
-
let
|
|
680
|
+
let block
|
|
669
681
|
|
|
670
682
|
if (this.core.bitfield.get(index)) {
|
|
671
|
-
|
|
683
|
+
block = this.core.blocks.get(index)
|
|
672
684
|
|
|
673
|
-
if (this.cache) this.cache.set(index,
|
|
685
|
+
if (this.cache) this.cache.set(index, block)
|
|
674
686
|
} else {
|
|
675
687
|
if (opts && opts.wait === false) return null
|
|
688
|
+
if (this.wait === false && (!opts || !opts.wait)) return null
|
|
676
689
|
if (opts && opts.onwait) opts.onwait(index, this)
|
|
677
|
-
|
|
690
|
+
if (this.onwait) this.onwait(index, this)
|
|
678
691
|
|
|
679
692
|
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
680
693
|
|
|
681
|
-
req = this.
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
.addBlock(activeRequests, index)
|
|
685
|
-
.promise,
|
|
686
|
-
this.core.tree.fork
|
|
687
|
-
)
|
|
694
|
+
const req = this.replicator.addBlock(activeRequests, index)
|
|
695
|
+
|
|
696
|
+
block = this._cacheOnResolve(index, req.promise, this.core.tree.fork)
|
|
688
697
|
}
|
|
689
698
|
|
|
690
|
-
return
|
|
699
|
+
return block
|
|
691
700
|
}
|
|
692
701
|
|
|
693
702
|
async _cacheOnResolve (index, req, fork) {
|
|
@@ -728,7 +737,9 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
728
737
|
|
|
729
738
|
async _download (range) {
|
|
730
739
|
if (this.opened === false) await this.opening
|
|
740
|
+
|
|
731
741
|
const activeRequests = (range && range.activeRequests) || this.activeRequests
|
|
742
|
+
|
|
732
743
|
return this.replicator.addRange(activeRequests, range)
|
|
733
744
|
}
|
|
734
745
|
|
package/lib/bitfield.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
// TODO: needs massive improvements obvs
|
|
2
|
-
|
|
3
1
|
const BigSparseArray = require('big-sparse-array')
|
|
4
2
|
const b4a = require('b4a')
|
|
3
|
+
const bits = require('bits-to-bytes')
|
|
5
4
|
|
|
6
5
|
class FixedBitfield {
|
|
7
6
|
constructor (index, bitfield) {
|
|
@@ -11,26 +10,17 @@ class FixedBitfield {
|
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
get (index) {
|
|
14
|
-
|
|
15
|
-
const i = (index - j) / 32
|
|
16
|
-
|
|
17
|
-
return i < this.bitfield.length && (this.bitfield[i] & (1 << j)) !== 0
|
|
13
|
+
return bits.get(this.bitfield, index)
|
|
18
14
|
}
|
|
19
15
|
|
|
20
16
|
set (index, val) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const v = this.bitfield[i]
|
|
24
|
-
|
|
25
|
-
if (val === ((v & (1 << j)) !== 0)) return false
|
|
26
|
-
|
|
27
|
-
const u = val
|
|
28
|
-
? v | (1 << j)
|
|
29
|
-
: v ^ (1 << j)
|
|
30
|
-
|
|
31
|
-
if (u === v) return false
|
|
17
|
+
return bits.set(this.bitfield, index, val)
|
|
18
|
+
}
|
|
32
19
|
|
|
33
|
-
|
|
20
|
+
setRange (start, length, val) {
|
|
21
|
+
// Using fill instead of setRange is ~2 orders of magnitude faster, but does
|
|
22
|
+
// have the downside of not being able to tell if any bits actually changed.
|
|
23
|
+
bits.fill(this.bitfield, val, start, start + length)
|
|
34
24
|
return true
|
|
35
25
|
}
|
|
36
26
|
}
|
|
@@ -44,7 +34,11 @@ module.exports = class Bitfield {
|
|
|
44
34
|
this.resumed = !!(buf && buf.byteLength >= 4)
|
|
45
35
|
|
|
46
36
|
const all = this.resumed
|
|
47
|
-
? new Uint32Array(
|
|
37
|
+
? new Uint32Array(
|
|
38
|
+
buf.buffer,
|
|
39
|
+
buf.byteOffset,
|
|
40
|
+
Math.floor(buf.byteLength / 4)
|
|
41
|
+
)
|
|
48
42
|
: new Uint32Array(1024)
|
|
49
43
|
|
|
50
44
|
for (let i = 0; i < all.length; i += 1024) {
|
|
@@ -55,40 +49,53 @@ module.exports = class Bitfield {
|
|
|
55
49
|
}
|
|
56
50
|
|
|
57
51
|
get (index) {
|
|
58
|
-
const j = index &
|
|
59
|
-
const i = (index - j) /
|
|
52
|
+
const j = index & (this.pageSize - 1)
|
|
53
|
+
const i = (index - j) / this.pageSize
|
|
54
|
+
|
|
60
55
|
const p = this.pages.get(i)
|
|
61
56
|
|
|
62
57
|
return p ? p.get(j) : false
|
|
63
58
|
}
|
|
64
59
|
|
|
65
60
|
set (index, val) {
|
|
66
|
-
const j = index &
|
|
67
|
-
const i = (index - j) /
|
|
61
|
+
const j = index & (this.pageSize - 1)
|
|
62
|
+
const i = (index - j) / this.pageSize
|
|
68
63
|
|
|
69
64
|
let p = this.pages.get(i)
|
|
70
65
|
|
|
71
|
-
if (!p) {
|
|
72
|
-
if (!val) return
|
|
66
|
+
if (!p && val) {
|
|
73
67
|
p = this.pages.set(i, new FixedBitfield(i, new Uint32Array(1024)))
|
|
74
68
|
}
|
|
75
69
|
|
|
76
|
-
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
if (p && p.set(j, val) && !p.dirty) {
|
|
71
|
+
p.dirty = true
|
|
72
|
+
this.unflushed.push(p)
|
|
73
|
+
}
|
|
80
74
|
}
|
|
81
75
|
|
|
82
76
|
setRange (start, length, val) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
}
|
|
77
|
+
let j = start & (this.pageSize - 1)
|
|
78
|
+
let i = (start - j) / this.pageSize
|
|
87
79
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
80
|
+
while (length > 0) {
|
|
81
|
+
let p = this.pages.get(i)
|
|
82
|
+
|
|
83
|
+
if (!p && val) {
|
|
84
|
+
p = this.pages.set(i, new FixedBitfield(i, new Uint32Array(1024)))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const end = Math.min(j + length, this.pageSize)
|
|
88
|
+
const range = end - j
|
|
89
|
+
|
|
90
|
+
if (p && p.setRange(j, range, val) && !p.dirty) {
|
|
91
|
+
p.dirty = true
|
|
92
|
+
this.unflushed.push(p)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
j = 0
|
|
96
|
+
i++
|
|
97
|
+
length -= range
|
|
98
|
+
}
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
clear () {
|
|
@@ -120,7 +127,12 @@ module.exports = class Bitfield {
|
|
|
120
127
|
let error = null
|
|
121
128
|
|
|
122
129
|
for (const page of this.unflushed) {
|
|
123
|
-
const buf = b4a.from(
|
|
130
|
+
const buf = b4a.from(
|
|
131
|
+
page.bitfield.buffer,
|
|
132
|
+
page.bitfield.byteOffset,
|
|
133
|
+
page.bitfield.byteLength
|
|
134
|
+
)
|
|
135
|
+
|
|
124
136
|
page.dirty = false
|
|
125
137
|
this.storage.write(page.index * 4096, buf, done)
|
|
126
138
|
}
|
|
@@ -151,7 +163,7 @@ module.exports = class Bitfield {
|
|
|
151
163
|
}
|
|
152
164
|
|
|
153
165
|
function ensureSize (uint32, size) {
|
|
154
|
-
if (uint32.
|
|
166
|
+
if (uint32.byteLength === size) return uint32
|
|
155
167
|
const a = new Uint32Array(1024)
|
|
156
168
|
a.set(uint32, 0)
|
|
157
169
|
return a
|
package/lib/core.js
CHANGED
|
@@ -9,9 +9,8 @@ const { BAD_ARGUMENT, STORAGE_EMPTY, STORAGE_CONFLICT, INVALID_SIGNATURE } = req
|
|
|
9
9
|
const m = require('./messages')
|
|
10
10
|
|
|
11
11
|
module.exports = class Core {
|
|
12
|
-
constructor (header, crypto, oplog, tree, blocks, bitfield, auth, legacy, onupdate
|
|
12
|
+
constructor (header, crypto, oplog, tree, blocks, bitfield, auth, legacy, onupdate) {
|
|
13
13
|
this.onupdate = onupdate
|
|
14
|
-
this.oncontigupdate = oncontigupdate
|
|
15
14
|
this.header = header
|
|
16
15
|
this.crypto = crypto
|
|
17
16
|
this.oplog = oplog
|
|
@@ -27,8 +26,6 @@ module.exports = class Core {
|
|
|
27
26
|
this._verifiesFlushed = null
|
|
28
27
|
this._mutex = new Mutex()
|
|
29
28
|
this._legacy = legacy
|
|
30
|
-
|
|
31
|
-
this._updateContiguousLength(header.contiguousLength)
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
static async open (storage, opts = {}) {
|
|
@@ -134,6 +131,11 @@ module.exports = class Core {
|
|
|
134
131
|
await bitfield.clear()
|
|
135
132
|
}
|
|
136
133
|
|
|
134
|
+
// compat from earlier version that do not store contig length
|
|
135
|
+
if (header.contiguousLength === 0) {
|
|
136
|
+
while (bitfield.get(header.contiguousLength)) header.contiguousLength++
|
|
137
|
+
}
|
|
138
|
+
|
|
137
139
|
const auth = opts.auth || this.createAuth(crypto, header.signer)
|
|
138
140
|
|
|
139
141
|
for (const e of entries) {
|
|
@@ -149,6 +151,7 @@ module.exports = class Core {
|
|
|
149
151
|
|
|
150
152
|
if (e.bitfield) {
|
|
151
153
|
bitfield.setRange(e.bitfield.start, e.bitfield.length, !e.bitfield.drop)
|
|
154
|
+
updateContig(header, e.bitfield, bitfield)
|
|
152
155
|
}
|
|
153
156
|
|
|
154
157
|
if (e.treeUpgrade) {
|
|
@@ -165,7 +168,7 @@ module.exports = class Core {
|
|
|
165
168
|
}
|
|
166
169
|
}
|
|
167
170
|
|
|
168
|
-
return new this(header, crypto, oplog, tree, blocks, bitfield, auth, !!opts.legacy, opts.onupdate || noop
|
|
171
|
+
return new this(header, crypto, oplog, tree, blocks, bitfield, auth, !!opts.legacy, opts.onupdate || noop)
|
|
169
172
|
}
|
|
170
173
|
|
|
171
174
|
_shouldFlush () {
|
|
@@ -196,17 +199,6 @@ module.exports = class Core {
|
|
|
196
199
|
await this.blocks.put(index, value, byteOffset)
|
|
197
200
|
}
|
|
198
201
|
|
|
199
|
-
_updateContiguousLength (index, length = 0) {
|
|
200
|
-
if (index === this.header.contiguousLength) {
|
|
201
|
-
let i = this.header.contiguousLength + length
|
|
202
|
-
|
|
203
|
-
while (this.bitfield.get(i)) i++
|
|
204
|
-
|
|
205
|
-
this.header.contiguousLength = i
|
|
206
|
-
this.oncontigupdate()
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
202
|
async userData (key, value) {
|
|
211
203
|
// TODO: each oplog append can set user data, so we should have a way
|
|
212
204
|
// to just hitch a ride on one of the other ongoing appends?
|
|
@@ -286,12 +278,12 @@ module.exports = class Core {
|
|
|
286
278
|
this.bitfield.setRange(batch.ancestors, batch.length - batch.ancestors, true)
|
|
287
279
|
batch.commit()
|
|
288
280
|
|
|
289
|
-
this.header.contiguousLength = batch.length
|
|
290
|
-
this.oncontigupdate()
|
|
291
281
|
this.header.tree.length = batch.length
|
|
292
282
|
this.header.tree.rootHash = hash
|
|
293
283
|
this.header.tree.signature = batch.signature
|
|
294
|
-
|
|
284
|
+
|
|
285
|
+
const status = 0b0001 | updateContig(this.header, entry.bitfield, this.bitfield)
|
|
286
|
+
this.onupdate(status, entry.bitfield, null, null)
|
|
295
287
|
|
|
296
288
|
if (this._shouldFlush()) await this._flushOplog()
|
|
297
289
|
|
|
@@ -329,9 +321,11 @@ module.exports = class Core {
|
|
|
329
321
|
|
|
330
322
|
await this.oplog.append([entry], false)
|
|
331
323
|
|
|
324
|
+
let status = 0b0001
|
|
325
|
+
|
|
332
326
|
if (bitfield) {
|
|
333
327
|
this.bitfield.set(bitfield.start, true)
|
|
334
|
-
this.
|
|
328
|
+
status |= updateContig(this.header, bitfield, this.bitfield)
|
|
335
329
|
}
|
|
336
330
|
|
|
337
331
|
batch.commit()
|
|
@@ -340,7 +334,8 @@ module.exports = class Core {
|
|
|
340
334
|
this.header.tree.length = batch.length
|
|
341
335
|
this.header.tree.rootHash = batch.rootHash
|
|
342
336
|
this.header.tree.signature = batch.signature
|
|
343
|
-
|
|
337
|
+
|
|
338
|
+
this.onupdate(status, bitfield, value, from)
|
|
344
339
|
|
|
345
340
|
if (this._shouldFlush()) await this._flushOplog()
|
|
346
341
|
} finally {
|
|
@@ -387,14 +382,16 @@ module.exports = class Core {
|
|
|
387
382
|
continue
|
|
388
383
|
}
|
|
389
384
|
|
|
385
|
+
let status = 0
|
|
386
|
+
|
|
390
387
|
if (bitfield) {
|
|
391
388
|
this.bitfield.set(bitfield.start, true)
|
|
392
|
-
this.
|
|
389
|
+
status = updateContig(this.header, bitfield, this.bitfield)
|
|
393
390
|
}
|
|
394
391
|
|
|
395
392
|
batch.commit()
|
|
396
393
|
|
|
397
|
-
this.onupdate(
|
|
394
|
+
this.onupdate(status, bitfield, value, from)
|
|
398
395
|
}
|
|
399
396
|
|
|
400
397
|
if (this._shouldFlush()) await this._flushOplog()
|
|
@@ -472,15 +469,15 @@ module.exports = class Core {
|
|
|
472
469
|
addReorgHint(this.header.hints.reorgs, this.tree, batch)
|
|
473
470
|
batch.commit()
|
|
474
471
|
|
|
475
|
-
const
|
|
472
|
+
const contigStatus = updateContig(this.header, entry.bitfield, this.bitfield)
|
|
473
|
+
const status = ((batch.length > batch.ancestors) ? 0b0011 : 0b0010) | contigStatus
|
|
476
474
|
|
|
477
|
-
this.header.contiguousLength = Math.min(batch.ancestors, this.header.contiguousLength)
|
|
478
|
-
this.oncontigupdate()
|
|
479
475
|
this.header.tree.fork = batch.fork
|
|
480
476
|
this.header.tree.length = batch.length
|
|
481
477
|
this.header.tree.rootHash = batch.hash()
|
|
482
478
|
this.header.tree.signature = batch.signature
|
|
483
|
-
|
|
479
|
+
|
|
480
|
+
this.onupdate(status, entry.bitfield, null, from)
|
|
484
481
|
|
|
485
482
|
// TODO: there is a bug in the merkle tree atm where it cannot handle unflushed
|
|
486
483
|
// truncates if we append or download anything after the truncation point later on
|
|
@@ -501,6 +498,36 @@ module.exports = class Core {
|
|
|
501
498
|
}
|
|
502
499
|
}
|
|
503
500
|
|
|
501
|
+
function updateContig (header, upd, bitfield) {
|
|
502
|
+
const end = upd.start + upd.length
|
|
503
|
+
|
|
504
|
+
let c = header.contiguousLength
|
|
505
|
+
|
|
506
|
+
if (upd.drop) {
|
|
507
|
+
// If we dropped a block in the current contig range, "downgrade" it
|
|
508
|
+
if (c <= end && c > upd.start) {
|
|
509
|
+
c = upd.start
|
|
510
|
+
}
|
|
511
|
+
} else {
|
|
512
|
+
if (c <= end && c >= upd.start) {
|
|
513
|
+
c = end
|
|
514
|
+
while (bitfield.get(c)) c++
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (c === header.contiguousLength) {
|
|
519
|
+
return 0b0000
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if (c > header.contiguousLength) {
|
|
523
|
+
header.contiguousLength = c
|
|
524
|
+
return 0b0100
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
header.contiguousLength = c
|
|
528
|
+
return 0b1000
|
|
529
|
+
}
|
|
530
|
+
|
|
504
531
|
function addReorgHint (list, tree, batch) {
|
|
505
532
|
if (tree.length === 0 || tree.fork === batch.fork) return
|
|
506
533
|
|
package/lib/remote-bitfield.js
CHANGED
|
@@ -1,24 +1,45 @@
|
|
|
1
1
|
const BigSparseArray = require('big-sparse-array')
|
|
2
|
+
const bits = require('bits-to-bytes')
|
|
2
3
|
|
|
3
4
|
module.exports = class RemoteBitfield {
|
|
4
5
|
constructor () {
|
|
6
|
+
this.pageSize = 32768
|
|
5
7
|
this.pages = new BigSparseArray()
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
get (index) {
|
|
9
|
-
const
|
|
10
|
-
const i = (index -
|
|
11
|
+
const j = index & (this.pageSize - 1)
|
|
12
|
+
const i = (index - j) / this.pageSize
|
|
13
|
+
|
|
11
14
|
const p = this.pages.get(i)
|
|
12
15
|
|
|
13
|
-
return p ? (p
|
|
16
|
+
return p ? bits.get(p, j) : false
|
|
14
17
|
}
|
|
15
18
|
|
|
16
19
|
set (index, val) {
|
|
17
|
-
const
|
|
18
|
-
const i = (index -
|
|
20
|
+
const j = index & (this.pageSize - 1)
|
|
21
|
+
const i = (index - j) / this.pageSize
|
|
22
|
+
|
|
19
23
|
const p = this.pages.get(i) || this.pages.set(i, new Uint32Array(1024))
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
bits.set(p, j, val)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
setRange (start, length, val) {
|
|
29
|
+
let j = start & (this.pageSize - 1)
|
|
30
|
+
let i = (start - j) / this.pageSize
|
|
31
|
+
|
|
32
|
+
while (length > 0) {
|
|
33
|
+
const p = this.pages.get(i) || this.pages.set(i, new Uint32Array(1024))
|
|
34
|
+
|
|
35
|
+
const end = Math.min(j + length, this.pageSize)
|
|
36
|
+
const range = end - j
|
|
37
|
+
|
|
38
|
+
bits.fill(p, val, j, end)
|
|
39
|
+
|
|
40
|
+
j = 0
|
|
41
|
+
i++
|
|
42
|
+
length -= range
|
|
43
|
+
}
|
|
23
44
|
}
|
|
24
45
|
}
|
package/lib/replicator.js
CHANGED
|
@@ -7,6 +7,7 @@ const m = require('./messages')
|
|
|
7
7
|
const caps = require('./caps')
|
|
8
8
|
|
|
9
9
|
const DEFAULT_MAX_INFLIGHT = 32
|
|
10
|
+
const DEFAULT_SEGMENT_SIZE = 128 * 1024 // 128 KiB
|
|
10
11
|
|
|
11
12
|
class Attachable {
|
|
12
13
|
constructor () {
|
|
@@ -286,6 +287,9 @@ class Peer {
|
|
|
286
287
|
this.remoteDownloading = true
|
|
287
288
|
this.remoteSynced = false
|
|
288
289
|
|
|
290
|
+
this.segmentsWanted = new Set()
|
|
291
|
+
this.broadcastedNonSparse = false
|
|
292
|
+
|
|
289
293
|
this.lengthAcked = 0
|
|
290
294
|
|
|
291
295
|
this.extensions = new Map()
|
|
@@ -356,13 +360,13 @@ class Peer {
|
|
|
356
360
|
|
|
357
361
|
this.sendSync()
|
|
358
362
|
|
|
359
|
-
const
|
|
363
|
+
const contig = this.core.header.contiguousLength
|
|
364
|
+
if (contig > 0) {
|
|
365
|
+
this.broadcastRange(0, contig, false)
|
|
360
366
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
bitfield: p[index]
|
|
365
|
-
})
|
|
367
|
+
if (contig === this.core.tree.length) {
|
|
368
|
+
this.broadcastedNonSparse = true
|
|
369
|
+
}
|
|
366
370
|
}
|
|
367
371
|
|
|
368
372
|
this.replicator._ifAvailable--
|
|
@@ -552,8 +556,8 @@ class Peer {
|
|
|
552
556
|
this.replicator._onnodata(this, req)
|
|
553
557
|
}
|
|
554
558
|
|
|
555
|
-
onwant () {
|
|
556
|
-
|
|
559
|
+
onwant ({ start, length }) {
|
|
560
|
+
this.replicator._onwant(this, start, length)
|
|
557
561
|
}
|
|
558
562
|
|
|
559
563
|
onunwant () {
|
|
@@ -577,9 +581,7 @@ class Peer {
|
|
|
577
581
|
onrange ({ drop, start, length }) {
|
|
578
582
|
const has = drop === false
|
|
579
583
|
|
|
580
|
-
|
|
581
|
-
this.remoteBitfield.set(start, has)
|
|
582
|
-
}
|
|
584
|
+
this.remoteBitfield.setRange(start, length, has)
|
|
583
585
|
|
|
584
586
|
if (drop === false) this._update()
|
|
585
587
|
}
|
|
@@ -674,6 +676,7 @@ class Peer {
|
|
|
674
676
|
return true
|
|
675
677
|
}
|
|
676
678
|
|
|
679
|
+
this._maybeWant(s.seeker.start, len)
|
|
677
680
|
return false
|
|
678
681
|
}
|
|
679
682
|
|
|
@@ -685,7 +688,10 @@ class Peer {
|
|
|
685
688
|
_requestBlock (b) {
|
|
686
689
|
const { length, fork } = this.core.tree
|
|
687
690
|
|
|
688
|
-
if (this.remoteBitfield.get(b.index) === false || fork !== this.remoteFork)
|
|
691
|
+
if (this.remoteBitfield.get(b.index) === false || fork !== this.remoteFork) {
|
|
692
|
+
this._maybeWant(b.index)
|
|
693
|
+
return false
|
|
694
|
+
}
|
|
689
695
|
|
|
690
696
|
const req = this._makeRequest(b.index >= length)
|
|
691
697
|
if (req === null) return false
|
|
@@ -743,6 +749,7 @@ class Peer {
|
|
|
743
749
|
return true
|
|
744
750
|
}
|
|
745
751
|
|
|
752
|
+
this._maybeWant(r.start, len)
|
|
746
753
|
return false
|
|
747
754
|
}
|
|
748
755
|
|
|
@@ -780,9 +787,25 @@ class Peer {
|
|
|
780
787
|
return true
|
|
781
788
|
}
|
|
782
789
|
|
|
790
|
+
this._maybeWant(f.batch.want.start, len)
|
|
783
791
|
return false
|
|
784
792
|
}
|
|
785
793
|
|
|
794
|
+
_maybeWant (start, length = 1) {
|
|
795
|
+
let i = Math.floor(start / DEFAULT_SEGMENT_SIZE)
|
|
796
|
+
const n = Math.ceil((start + length) / DEFAULT_SEGMENT_SIZE)
|
|
797
|
+
|
|
798
|
+
for (; i < n; i++) {
|
|
799
|
+
if (this.segmentsWanted.has(i)) continue
|
|
800
|
+
this.segmentsWanted.add(i)
|
|
801
|
+
|
|
802
|
+
this.wireWant.send({
|
|
803
|
+
start: i * DEFAULT_SEGMENT_SIZE,
|
|
804
|
+
length: DEFAULT_SEGMENT_SIZE
|
|
805
|
+
})
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
786
809
|
async _send (req) {
|
|
787
810
|
const fork = this.core.tree.fork
|
|
788
811
|
|
|
@@ -1299,6 +1322,28 @@ module.exports = class Replicator {
|
|
|
1299
1322
|
else this.updatePeer(peer)
|
|
1300
1323
|
}
|
|
1301
1324
|
|
|
1325
|
+
_onwant (peer, start, length) {
|
|
1326
|
+
length = Math.min(length, this.core.tree.length - start)
|
|
1327
|
+
|
|
1328
|
+
peer.protomux.cork()
|
|
1329
|
+
|
|
1330
|
+
let i = Math.floor(start / this.core.bitfield.pageSize)
|
|
1331
|
+
const n = Math.ceil((start + length) / this.core.bitfield.pageSize)
|
|
1332
|
+
|
|
1333
|
+
for (; i < n; i++) {
|
|
1334
|
+
const p = this.core.bitfield.pages.get(i)
|
|
1335
|
+
|
|
1336
|
+
if (p) {
|
|
1337
|
+
peer.wireBitfield.send({
|
|
1338
|
+
start: i * this.core.bitfield.pageSize,
|
|
1339
|
+
bitfield: p.bitfield
|
|
1340
|
+
})
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
peer.protomux.uncork()
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1302
1347
|
async _onreorgdata (peer, req, data) {
|
|
1303
1348
|
const f = this._addReorg(data.fork, peer)
|
|
1304
1349
|
|
|
@@ -1525,17 +1570,6 @@ module.exports = class Replicator {
|
|
|
1525
1570
|
}
|
|
1526
1571
|
}
|
|
1527
1572
|
|
|
1528
|
-
function pages (core) {
|
|
1529
|
-
const res = []
|
|
1530
|
-
|
|
1531
|
-
for (let i = 0; i < core.tree.length; i += core.bitfield.pageSize) {
|
|
1532
|
-
const p = core.bitfield.page(i / core.bitfield.pageSize)
|
|
1533
|
-
res.push(p)
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
return res
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
1573
|
function matchingRequest (req, data) {
|
|
1540
1574
|
if (data.block !== null && (req.block === null || req.block.index !== data.block.index)) return false
|
|
1541
1575
|
if (data.hash !== null && (req.hash === null || req.hash.index !== data.hash.index)) return false
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore",
|
|
3
|
-
"version": "10.0.0-alpha.
|
|
3
|
+
"version": "10.0.0-alpha.56",
|
|
4
4
|
"description": "Hypercore 10",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"@hyperswarm/secret-stream": "^6.0.0",
|
|
36
36
|
"b4a": "^1.1.0",
|
|
37
37
|
"big-sparse-array": "^1.0.2",
|
|
38
|
+
"bits-to-bytes": "^1.2.0",
|
|
38
39
|
"codecs": "^3.0.0",
|
|
39
40
|
"compact-encoding": "^2.5.0",
|
|
40
41
|
"crc32-universal": "^1.0.1",
|