hypercore 10.0.0-alpha.5 → 10.0.0-alpha.6

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
@@ -113,6 +113,7 @@ A range can have the following properties:
113
113
  {
114
114
  start: startIndex,
115
115
  end: nonInclusiveEndIndex,
116
+ blocks: [index1, index2, ...],
116
117
  linear: false // download range linearly and not randomly
117
118
  }
118
119
  ```
@@ -125,6 +126,12 @@ To download the full core continously (often referred to as non sparse mode) do
125
126
  core.download({ start: 0, end: -1 })
126
127
  ```
127
128
 
129
+ To downloaded a discrete range of blocks pass a list of indices.
130
+
131
+ ```js
132
+ core.download({ blocks: [4, 9, 7] });
133
+ ```
134
+
128
135
  To cancel downloading a range simply destroy the range instance.
129
136
 
130
137
  ``` js
package/index.js CHANGED
@@ -28,14 +28,17 @@ module.exports = class Hypercore extends EventEmitter {
28
28
  opts = key
29
29
  key = null
30
30
  }
31
+
31
32
  if (key && typeof key === 'string') {
32
33
  key = Buffer.from(key, 'hex')
33
34
  }
34
- if (key && key.byteLength !== 32) {
35
+
36
+ if (!opts) opts = {}
37
+
38
+ if (!opts.crypto && key && key.byteLength !== 32) {
35
39
  throw new Error('Hypercore key should be 32 bytes')
36
40
  }
37
41
 
38
- if (!opts) opts = {}
39
42
  if (!storage) storage = opts.storage
40
43
 
41
44
  this[promises] = true
@@ -371,13 +374,27 @@ module.exports = class Hypercore extends EventEmitter {
371
374
  }
372
375
 
373
376
  download (range) {
374
- const start = (range && range.start) || 0
375
- const end = typeof (range && range.end) === 'number' ? range.end : -1 // download all
376
377
  const linear = !!(range && range.linear)
377
378
 
378
- // TODO: support range.blocks
379
+ let start
380
+ let end
381
+ let filter
382
+
383
+ if (range && range.blocks) {
384
+ const blocks = range.blocks instanceof Set
385
+ ? range.blocks
386
+ : new Set(range.blocks)
387
+
388
+ start = range.start || (blocks.size ? min(range.blocks) : 0)
389
+ end = range.end || (blocks.size ? max(range.blocks) + 1 : 0)
390
+
391
+ filter = (i) => blocks.has(i)
392
+ } else {
393
+ start = (range && range.start) || 0
394
+ end = typeof (range && range.end) === 'number' ? range.end : -1 // download all
395
+ }
379
396
 
380
- const r = Replicator.createRange(start, end, linear)
397
+ const r = Replicator.createRange(start, end, filter, linear)
381
398
 
382
399
  if (this.opened) this.replicator.addRange(r)
383
400
  else this.opening.then(() => this.replicator.addRange(r), noop)
@@ -459,3 +476,16 @@ function requireMaybe (name) {
459
476
  function toHex (buf) {
460
477
  return buf && buf.toString('hex')
461
478
  }
479
+
480
+ function reduce (iter, fn, acc) {
481
+ for (const item of iter) acc = fn(acc, item)
482
+ return acc
483
+ }
484
+
485
+ function min (arr) {
486
+ return reduce(arr, (a, b) => Math.min(a, b), Infinity)
487
+ }
488
+
489
+ function max (arr) {
490
+ return reduce(arr, (a, b) => Math.max(a, b), -Infinity)
491
+ }
package/lib/replicator.js CHANGED
@@ -120,9 +120,10 @@ class Seek {
120
120
  }
121
121
 
122
122
  class Range {
123
- constructor (start, end, linear) {
123
+ constructor (start, end, filter, linear) {
124
124
  this.start = start
125
125
  this.end = end
126
+ this.filter = filter
126
127
  this.linear = !!linear
127
128
  this.promise = null
128
129
  this.done = false
@@ -145,7 +146,7 @@ class Range {
145
146
  }
146
147
 
147
148
  for (; this._start < this.end; this._start++) {
148
- if (!bitfield.get(this._start)) return false
149
+ if (this.filter(this._start) && !bitfield.get(this._start)) return false
149
150
  }
150
151
 
151
152
  return true
@@ -299,10 +300,13 @@ class RequestPool {
299
300
  const end = range.end === -1 ? peer.state.length : range.end
300
301
  if (end <= range._start) return false
301
302
 
302
- if (range.linear) return !!this._requestRange(peer, range._start, end, 0, 0)
303
+ if (range.linear) return !!this._requestRange(peer, range._start, end, 0, 0, range.filter)
303
304
 
304
305
  const r = range._start + Math.floor(Math.random() * (end - range._start))
305
- return !!(this._requestRange(peer, r, end, 0, 0) || this._requestRange(peer, range._start, r, 0, 0))
306
+ return !!(
307
+ this._requestRange(peer, r, end, 0, 0, range.filter) ||
308
+ this._requestRange(peer, range._start, r, 0, 0, range.filter)
309
+ )
306
310
  }
307
311
 
308
312
  _updateUpgrade (peer) {
@@ -331,7 +335,7 @@ class RequestPool {
331
335
  this.pendingUpgrade = null
332
336
  }
333
337
 
334
- _requestRange (peer, start, end, seek, nodes) {
338
+ _requestRange (peer, start, end, seek, nodes, filter = tautology) {
335
339
  const remote = peer.state.bitfield
336
340
  const local = this.core.bitfield
337
341
 
@@ -339,7 +343,7 @@ class RequestPool {
339
343
  if (end === -1) end = peer.state.length
340
344
 
341
345
  for (let i = start; i < end; i++) {
342
- if (!remote.get(i) || local.get(i)) continue
346
+ if (!filter(i) || !remote.get(i) || local.get(i)) continue
343
347
  // TODO: if this was a NO_VALUE request, retry if no blocks can be found elsewhere
344
348
  if (this.requests.has(i)) continue
345
349
 
@@ -677,8 +681,18 @@ module.exports = class Replicator {
677
681
  return promise
678
682
  }
679
683
 
680
- static createRange (start, end, linear) {
681
- return new Range(start, end, linear)
684
+ static createRange (start, end, filter, linear) {
685
+ // createRange(start, end)
686
+ if (filter === undefined) {
687
+ filter = tautology
688
+
689
+ // createRange(start, end, linear)
690
+ } else if (typeof filter === 'boolean') {
691
+ linear = filter
692
+ filter = tautology
693
+ }
694
+
695
+ return new Range(start, end, filter, linear)
682
696
  }
683
697
 
684
698
  addRange (range) {
@@ -826,6 +840,10 @@ function pages (core) {
826
840
 
827
841
  function noop () {}
828
842
 
843
+ function tautology () {
844
+ return true
845
+ }
846
+
829
847
  function log2 (n) {
830
848
  let res = 1
831
849
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.0.0-alpha.5",
3
+ "version": "10.0.0-alpha.6",
4
4
  "description": "Hypercore 10",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/test/basic.js CHANGED
@@ -76,3 +76,15 @@ test('storage options', async function (t) {
76
76
  t.alike(await core.get(0), Buffer.from('hello'))
77
77
  t.end()
78
78
  })
79
+
80
+ test(
81
+ 'allow publicKeys with different byteLength that 32, if opts.crypto were passed',
82
+ function (t) {
83
+ const key = Buffer.alloc(33).fill('a')
84
+
85
+ const core = new Hypercore(ram, key, { crypto: {} })
86
+
87
+ t.is(core.key, key)
88
+ t.pass('creating a core with more than 32 byteLength key did not throw')
89
+ }
90
+ )
package/test/replicate.js CHANGED
@@ -331,3 +331,42 @@ test('destroying a stream and re-replicating works', async function (t) {
331
331
 
332
332
  t.is(blocks.length, 33, 'downloaded 33 blocks')
333
333
  })
334
+
335
+ test('replicate discrete range', async function (t) {
336
+ const a = await create()
337
+
338
+ await a.append(['a', 'b', 'c', 'd', 'e'])
339
+
340
+ const b = await create(a.key)
341
+
342
+ let d = 0
343
+ b.on('download', () => d++)
344
+
345
+ replicate(a, b, t)
346
+
347
+ const r = b.download({ blocks: [0, 2, 3] })
348
+ await r.downloaded()
349
+
350
+ t.is(d, 3)
351
+ t.alike(await b.get(0), Buffer.from('a'))
352
+ t.alike(await b.get(2), Buffer.from('c'))
353
+ t.alike(await b.get(3), Buffer.from('d'))
354
+ })
355
+
356
+ test('replicate discrete empty range', async function (t) {
357
+ const a = await create()
358
+
359
+ await a.append(['a', 'b', 'c', 'd', 'e'])
360
+
361
+ const b = await create(a.key)
362
+
363
+ let d = 0
364
+ b.on('download', () => d++)
365
+
366
+ replicate(a, b, t)
367
+
368
+ const r = b.download({ blocks: [] })
369
+ await r.downloaded()
370
+
371
+ t.is(d, 0)
372
+ })