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 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 req
677
+ let block
666
678
 
667
679
  if (this.core.bitfield.get(index)) {
668
- req = this.core.blocks.get(index)
680
+ block = this.core.blocks.get(index)
669
681
 
670
- if (this.cache) this.cache.set(index, req)
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
- else if (this.onwait) this.onwait(index, this)
686
+ if (this.onwait) this.onwait(index, this)
675
687
 
676
688
  const activeRequests = (opts && opts.activeRequests) || this.activeRequests
677
689
 
678
- req = this._cacheOnResolve(
679
- index,
680
- this.replicator
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 req
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
- const j = index & 31
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
- const j = index & 31
22
- const i = (index - j) / 32
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
- this.bitfield[i] = u
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(buf.buffer, buf.byteOffset, Math.floor(buf.byteLength / 4))
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 & 32767
59
- const i = (index - j) / 32768
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 & 32767
67
- const i = (index - j) / 32768
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 (!p.set(j, val) || p.dirty) return
77
-
78
- p.dirty = true
79
- this.unflushed.push(p)
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
- for (let i = 0; i < length; i++) {
84
- this.set(start + i, val)
85
- }
86
- }
77
+ let j = start & (this.pageSize - 1)
78
+ let i = (start - j) / this.pageSize
87
79
 
88
- // Should prob be removed, when/if we re-add compression
89
- page (i) {
90
- const p = this.pages.get(i)
91
- return p ? p.bitfield : new Uint32Array(1024)
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(page.bitfield.buffer, page.bitfield.byteOffset, page.bitfield.byteLength)
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.length === size) return 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
@@ -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 r = index & 32767
10
- const i = (index - r) / 32768
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[r >>> 5] & (1 << (r & 31))) !== 0 : false
16
+ return p ? bits.get(p, j) : false
14
17
  }
15
18
 
16
19
  set (index, val) {
17
- const r = index & 32767
18
- const i = (index - r) / 32768
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
- if (val) p[r >>> 5] |= (1 << (r & 31))
22
- else p[r >>> 5] &= ~(1 << (r & 31))
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 p = pages(this.core)
363
+ const contig = this.core.header.contiguousLength
364
+ if (contig > 0) {
365
+ this.broadcastRange(0, contig, false)
360
366
 
361
- for (let index = 0; index < p.length; index++) {
362
- this.wireBitfield.send({
363
- start: index * this.core.bitfield.pageSize,
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
- // TODO
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
- for (const end = start + length; start < end; start++) {
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) return false
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.54",
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",