hypercore 10.0.0-alpha.54 → 10.0.0-alpha.55
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 +22 -12
- package/lib/bitfield.js +51 -39
- 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
|
@@ -527,6 +527,18 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
527
527
|
s.emit('append')
|
|
528
528
|
}
|
|
529
529
|
}
|
|
530
|
+
|
|
531
|
+
const contig = this.core.header.contiguousLength
|
|
532
|
+
|
|
533
|
+
// When the contig length catches up, broadcast the non-sparse length to peers
|
|
534
|
+
if (appendedNonSparse && contig === this.core.tree.length) {
|
|
535
|
+
for (const peer of this.peers) {
|
|
536
|
+
if (peer.broadcastedNonSparse) continue
|
|
537
|
+
|
|
538
|
+
peer.broadcastRange(0, contig)
|
|
539
|
+
peer.broadcastedNonSparse = true
|
|
540
|
+
}
|
|
541
|
+
}
|
|
530
542
|
}
|
|
531
543
|
|
|
532
544
|
if (bitfield) {
|
|
@@ -662,29 +674,25 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
662
674
|
}
|
|
663
675
|
|
|
664
676
|
async _get (index, opts) {
|
|
665
|
-
let
|
|
677
|
+
let block
|
|
666
678
|
|
|
667
679
|
if (this.core.bitfield.get(index)) {
|
|
668
|
-
|
|
680
|
+
block = this.core.blocks.get(index)
|
|
669
681
|
|
|
670
|
-
if (this.cache) this.cache.set(index,
|
|
682
|
+
if (this.cache) this.cache.set(index, block)
|
|
671
683
|
} else {
|
|
672
684
|
if (opts && opts.wait === false) return null
|
|
673
685
|
if (opts && opts.onwait) opts.onwait(index, this)
|
|
674
|
-
|
|
686
|
+
if (this.onwait) this.onwait(index, this)
|
|
675
687
|
|
|
676
688
|
const activeRequests = (opts && opts.activeRequests) || this.activeRequests
|
|
677
689
|
|
|
678
|
-
req = this.
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
.addBlock(activeRequests, index)
|
|
682
|
-
.promise,
|
|
683
|
-
this.core.tree.fork
|
|
684
|
-
)
|
|
690
|
+
const req = this.replicator.addBlock(activeRequests, index)
|
|
691
|
+
|
|
692
|
+
block = this._cacheOnResolve(index, req.promise, this.core.tree.fork)
|
|
685
693
|
}
|
|
686
694
|
|
|
687
|
-
return
|
|
695
|
+
return block
|
|
688
696
|
}
|
|
689
697
|
|
|
690
698
|
async _cacheOnResolve (index, req, fork) {
|
|
@@ -725,7 +733,9 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
725
733
|
|
|
726
734
|
async _download (range) {
|
|
727
735
|
if (this.opened === false) await this.opening
|
|
736
|
+
|
|
728
737
|
const activeRequests = (range && range.activeRequests) || this.activeRequests
|
|
738
|
+
|
|
729
739
|
return this.replicator.addRange(activeRequests, range)
|
|
730
740
|
}
|
|
731
741
|
|
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/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.55",
|
|
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",
|