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 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 truncated = (status & 0b10) !== 0
500
- const appended = (status & 0b01) !== 0
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
- s.emit('truncate', bitfield.start, this.core.tree.fork)
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
- // For sparse sessions, immediately emit appends. Non-sparse sessions
517
- // are handled separately and only emit appends when their contiguous
518
- // length is updated.
519
- if (appended && s.sparse) s.emit('append')
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.replicator.onupgrade()
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 req
680
+ let block
669
681
 
670
682
  if (this.core.bitfield.get(index)) {
671
- req = this.core.blocks.get(index)
683
+ block = this.core.blocks.get(index)
672
684
 
673
- if (this.cache) this.cache.set(index, req)
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
- else if (this.onwait) this.onwait(index, this)
690
+ if (this.onwait) this.onwait(index, this)
678
691
 
679
692
  const activeRequests = (opts && opts.activeRequests) || this.activeRequests
680
693
 
681
- req = this._cacheOnResolve(
682
- index,
683
- this.replicator
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 req
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
- 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
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, oncontigupdate) {
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, opts.oncontigupdate || 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
- this.onupdate(0b01, entry.bitfield, null, null)
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._updateContiguousLength(bitfield.start, bitfield.length)
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
- this.onupdate(0b01, bitfield, value, from)
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._updateContiguousLength(bitfield.start, bitfield.length)
389
+ status = updateContig(this.header, bitfield, this.bitfield)
393
390
  }
394
391
 
395
392
  batch.commit()
396
393
 
397
- this.onupdate(0, bitfield, value, from)
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 appended = batch.length > batch.ancestors
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
- this.onupdate(appended ? 0b11 : 0b10, entry.bitfield, null, from)
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
 
@@ -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.53",
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",