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 +7 -0
- package/index.js +36 -6
- package/lib/replicator.js +26 -8
- package/package.json +1 -1
- package/test/basic.js +12 -0
- package/test/replicate.js +39 -0
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
|
-
|
|
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
|
-
|
|
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 !!(
|
|
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
|
-
|
|
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
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
|
+
})
|