hypercore 10.3.1 → 10.4.0
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/index.js +22 -16
- package/lib/bitfield.js +124 -58
- package/lib/compat.js +11 -0
- package/lib/info.js +11 -12
- package/lib/oplog.js +1 -1
- package/lib/remote-bitfield.js +246 -16
- package/lib/replicator.js +44 -36
- package/package.json +3 -3
package/index.js
CHANGED
|
@@ -242,14 +242,19 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
242
242
|
async _openFromExisting (from, opts) {
|
|
243
243
|
await from.opening
|
|
244
244
|
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
// includes ourself as well, so the loop below also updates us
|
|
246
|
+
const sessions = this.sessions
|
|
247
|
+
|
|
248
|
+
for (const s of sessions) {
|
|
249
|
+
s.sessions = from.sessions
|
|
250
|
+
s.sessions.push(s)
|
|
251
|
+
s._passCapabilities(from)
|
|
252
|
+
}
|
|
253
|
+
|
|
247
254
|
this.storage = from.storage
|
|
248
255
|
this.replicator.findingPeers += this._findingPeers
|
|
249
256
|
|
|
250
257
|
ensureEncryption(this, opts)
|
|
251
|
-
|
|
252
|
-
this.sessions.push(this)
|
|
253
258
|
}
|
|
254
259
|
|
|
255
260
|
async _openSession (key, storage, opts) {
|
|
@@ -424,29 +429,30 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
424
429
|
const protocolStream = Hypercore.createProtocolStream(isInitiator, opts)
|
|
425
430
|
const noiseStream = protocolStream.noiseStream
|
|
426
431
|
const protocol = noiseStream.userData
|
|
432
|
+
const useSession = !!opts.session
|
|
427
433
|
|
|
428
|
-
this._attachToMuxer(protocol,
|
|
434
|
+
this._attachToMuxer(protocol, useSession)
|
|
429
435
|
|
|
430
436
|
return protocolStream
|
|
431
437
|
}
|
|
432
438
|
|
|
433
|
-
_attachToMuxer (mux,
|
|
434
|
-
// If the user wants to, we can make this replication run in a session
|
|
435
|
-
// that way the core wont close "under them" during replication
|
|
436
|
-
if (opts.session) {
|
|
437
|
-
const s = this.session()
|
|
438
|
-
mux.stream.on('close', () => s.close().catch(noop))
|
|
439
|
-
}
|
|
440
|
-
|
|
439
|
+
_attachToMuxer (mux, useSession) {
|
|
441
440
|
if (this.opened) {
|
|
442
|
-
this.
|
|
441
|
+
this._attachToMuxerOpened(mux, useSession)
|
|
443
442
|
} else {
|
|
444
|
-
this.opening.then(
|
|
443
|
+
this.opening.then(this._attachToMuxerOpened.bind(this, mux, useSession), mux.destroy.bind(mux))
|
|
445
444
|
}
|
|
446
445
|
|
|
447
446
|
return mux
|
|
448
447
|
}
|
|
449
448
|
|
|
449
|
+
_attachToMuxerOpened (mux, useSession) {
|
|
450
|
+
// If the user wants to, we can make this replication run in a session
|
|
451
|
+
// that way the core wont close "under them" during replication
|
|
452
|
+
const session = useSession ? this.session() : null
|
|
453
|
+
this.replicator.attachTo(mux, session)
|
|
454
|
+
}
|
|
455
|
+
|
|
450
456
|
get discoveryKey () {
|
|
451
457
|
return this.replicator === null ? null : this.replicator.discoveryKey
|
|
452
458
|
}
|
|
@@ -611,7 +617,7 @@ module.exports = class Hypercore extends EventEmitter {
|
|
|
611
617
|
async info () {
|
|
612
618
|
if (this.opened === false) await this.opening
|
|
613
619
|
|
|
614
|
-
return Info.from(this
|
|
620
|
+
return Info.from(this)
|
|
615
621
|
}
|
|
616
622
|
|
|
617
623
|
async update (opts) {
|
package/lib/bitfield.js
CHANGED
|
@@ -1,99 +1,116 @@
|
|
|
1
1
|
const BigSparseArray = require('big-sparse-array')
|
|
2
2
|
const b4a = require('b4a')
|
|
3
|
-
const
|
|
3
|
+
const quickbit = require('./compat').quickbit
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const BITS_PER_PAGE = 2097152
|
|
6
|
+
const BYTES_PER_PAGE = BITS_PER_PAGE / 8
|
|
7
|
+
const WORDS_PER_PAGE = BYTES_PER_PAGE / 4
|
|
8
|
+
|
|
9
|
+
class BitfieldPage {
|
|
6
10
|
constructor (index, bitfield) {
|
|
7
11
|
this.dirty = false
|
|
8
12
|
this.index = index
|
|
9
13
|
this.bitfield = bitfield
|
|
14
|
+
this.tree = quickbit.Index.from(this.bitfield)
|
|
10
15
|
}
|
|
11
16
|
|
|
12
17
|
get (index) {
|
|
13
|
-
return
|
|
18
|
+
return quickbit.get(this.bitfield, index)
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
set (index, val) {
|
|
17
|
-
|
|
22
|
+
if (quickbit.set(this.bitfield, index, val)) {
|
|
23
|
+
this.tree.update(index)
|
|
24
|
+
}
|
|
18
25
|
}
|
|
19
26
|
|
|
20
27
|
setRange (start, length, val) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
quickbit.fill(this.bitfield, val, start, start + length)
|
|
29
|
+
|
|
30
|
+
let i = Math.floor(start / 32)
|
|
31
|
+
const n = i + Math.ceil(length / 32)
|
|
32
|
+
|
|
33
|
+
while (i < n) this.tree.update(i++ * 32)
|
|
25
34
|
}
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
return
|
|
36
|
+
findFirst (val, position) {
|
|
37
|
+
return quickbit.findFirst(this.bitfield, val, this.tree.skipFirst(!val, position))
|
|
29
38
|
}
|
|
30
39
|
|
|
31
|
-
|
|
32
|
-
return
|
|
40
|
+
findLast (val, position) {
|
|
41
|
+
return quickbit.findLast(this.bitfield, val, this.tree.skipLast(!val, position))
|
|
33
42
|
}
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
module.exports = class Bitfield {
|
|
37
46
|
constructor (storage, buf) {
|
|
38
|
-
this.pageSize = 32768
|
|
39
|
-
this.pages = new BigSparseArray()
|
|
40
47
|
this.unflushed = []
|
|
41
48
|
this.storage = storage
|
|
42
49
|
this.resumed = !!(buf && buf.byteLength >= 4)
|
|
43
50
|
|
|
51
|
+
this._pages = new BigSparseArray()
|
|
52
|
+
|
|
44
53
|
const all = this.resumed
|
|
45
54
|
? new Uint32Array(buf.buffer, buf.byteOffset, Math.floor(buf.byteLength / 4))
|
|
46
|
-
: new Uint32Array(
|
|
55
|
+
: new Uint32Array(WORDS_PER_PAGE)
|
|
47
56
|
|
|
48
|
-
for (let i = 0; i < all.length; i +=
|
|
49
|
-
const bitfield = ensureSize(all.subarray(i, i +
|
|
50
|
-
const page = new
|
|
51
|
-
this.
|
|
57
|
+
for (let i = 0; i < all.length; i += WORDS_PER_PAGE) {
|
|
58
|
+
const bitfield = ensureSize(all.subarray(i, i + (WORDS_PER_PAGE)), WORDS_PER_PAGE)
|
|
59
|
+
const page = new BitfieldPage(i / (WORDS_PER_PAGE), bitfield)
|
|
60
|
+
this._pages.set(page.index, page)
|
|
52
61
|
}
|
|
53
62
|
}
|
|
54
63
|
|
|
55
64
|
get (index) {
|
|
56
|
-
const j = index & (
|
|
57
|
-
const i = (index - j) /
|
|
65
|
+
const j = index & (BITS_PER_PAGE - 1)
|
|
66
|
+
const i = (index - j) / BITS_PER_PAGE
|
|
58
67
|
|
|
59
|
-
const p = this.
|
|
68
|
+
const p = this._pages.get(i)
|
|
60
69
|
|
|
61
70
|
return p ? p.get(j) : false
|
|
62
71
|
}
|
|
63
72
|
|
|
64
73
|
set (index, val) {
|
|
65
|
-
const j = index & (
|
|
66
|
-
const i = (index - j) /
|
|
74
|
+
const j = index & (BITS_PER_PAGE - 1)
|
|
75
|
+
const i = (index - j) / BITS_PER_PAGE
|
|
67
76
|
|
|
68
|
-
let p = this.
|
|
77
|
+
let p = this._pages.get(i)
|
|
69
78
|
|
|
70
79
|
if (!p && val) {
|
|
71
|
-
p = this.
|
|
80
|
+
p = this._pages.set(i, new BitfieldPage(i, new Uint32Array(WORDS_PER_PAGE)))
|
|
72
81
|
}
|
|
73
82
|
|
|
74
|
-
if (p
|
|
75
|
-
p.
|
|
76
|
-
|
|
83
|
+
if (p) {
|
|
84
|
+
p.set(j, val)
|
|
85
|
+
|
|
86
|
+
if (!p.dirty) {
|
|
87
|
+
p.dirty = true
|
|
88
|
+
this.unflushed.push(p)
|
|
89
|
+
}
|
|
77
90
|
}
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
setRange (start, length, val) {
|
|
81
|
-
let j = start & (
|
|
82
|
-
let i = (start - j) /
|
|
94
|
+
let j = start & (BITS_PER_PAGE - 1)
|
|
95
|
+
let i = (start - j) / BITS_PER_PAGE
|
|
83
96
|
|
|
84
97
|
while (length > 0) {
|
|
85
|
-
let p = this.
|
|
98
|
+
let p = this._pages.get(i)
|
|
86
99
|
|
|
87
100
|
if (!p && val) {
|
|
88
|
-
p = this.
|
|
101
|
+
p = this._pages.set(i, new BitfieldPage(i, new Uint32Array(WORDS_PER_PAGE)))
|
|
89
102
|
}
|
|
90
103
|
|
|
91
|
-
const end = Math.min(j + length,
|
|
104
|
+
const end = Math.min(j + length, BITS_PER_PAGE)
|
|
92
105
|
const range = end - j
|
|
93
106
|
|
|
94
|
-
if (p
|
|
95
|
-
p.
|
|
96
|
-
|
|
107
|
+
if (p) {
|
|
108
|
+
p.setRange(j, range, val)
|
|
109
|
+
|
|
110
|
+
if (!p.dirty) {
|
|
111
|
+
p.dirty = true
|
|
112
|
+
this.unflushed.push(p)
|
|
113
|
+
}
|
|
97
114
|
}
|
|
98
115
|
|
|
99
116
|
j = 0
|
|
@@ -102,18 +119,18 @@ module.exports = class Bitfield {
|
|
|
102
119
|
}
|
|
103
120
|
}
|
|
104
121
|
|
|
105
|
-
|
|
106
|
-
let j = position & (
|
|
107
|
-
let i = (position - j) /
|
|
122
|
+
findFirst (val, position) {
|
|
123
|
+
let j = position & (BITS_PER_PAGE - 1)
|
|
124
|
+
let i = (position - j) / BITS_PER_PAGE
|
|
108
125
|
|
|
109
|
-
while (i < this.
|
|
110
|
-
const p = this.
|
|
126
|
+
while (i < this._pages.maxLength) {
|
|
127
|
+
const p = this._pages.get(i)
|
|
111
128
|
|
|
112
129
|
if (p) {
|
|
113
|
-
const index = p.
|
|
130
|
+
const index = p.findFirst(val, j)
|
|
114
131
|
|
|
115
132
|
if (index !== -1) {
|
|
116
|
-
return i *
|
|
133
|
+
return i * BITS_PER_PAGE + index
|
|
117
134
|
}
|
|
118
135
|
}
|
|
119
136
|
|
|
@@ -124,33 +141,72 @@ module.exports = class Bitfield {
|
|
|
124
141
|
return -1
|
|
125
142
|
}
|
|
126
143
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
144
|
+
firstSet (position) {
|
|
145
|
+
return this.findFirst(true, position)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
firstUnset (position) {
|
|
149
|
+
return this.findFirst(false, position)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
findLast (val, position) {
|
|
153
|
+
let j = position & (BITS_PER_PAGE - 1)
|
|
154
|
+
let i = (position - j) / BITS_PER_PAGE
|
|
130
155
|
|
|
131
156
|
while (i >= 0) {
|
|
132
|
-
const p = this.
|
|
157
|
+
const p = this._pages.get(i)
|
|
133
158
|
|
|
134
159
|
if (p) {
|
|
135
|
-
const index = p.
|
|
160
|
+
const index = p.findLast(val, j)
|
|
136
161
|
|
|
137
162
|
if (index !== -1) {
|
|
138
|
-
return i *
|
|
163
|
+
return i * BITS_PER_PAGE + index
|
|
139
164
|
}
|
|
140
165
|
}
|
|
141
166
|
|
|
142
|
-
j =
|
|
167
|
+
j = BITS_PER_PAGE - 1
|
|
143
168
|
i--
|
|
144
169
|
}
|
|
145
170
|
|
|
146
171
|
return -1
|
|
147
172
|
}
|
|
148
173
|
|
|
174
|
+
lastSet (position) {
|
|
175
|
+
return this.findLast(true, position)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
lastUnset (position) {
|
|
179
|
+
return this.findLast(false, position)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
* want (start, length) {
|
|
183
|
+
const j = start & (BITS_PER_PAGE - 1)
|
|
184
|
+
let i = (start - j) / BITS_PER_PAGE
|
|
185
|
+
|
|
186
|
+
while (length > 0) {
|
|
187
|
+
const p = this._pages.get(i)
|
|
188
|
+
|
|
189
|
+
if (p) {
|
|
190
|
+
// We always send at least 4 KiB worth of bitfield in a want, rounding
|
|
191
|
+
// to the nearest 4 KiB.
|
|
192
|
+
const end = ceilTo(clamp(length / 8, 4096, BYTES_PER_PAGE), 4096)
|
|
193
|
+
|
|
194
|
+
yield {
|
|
195
|
+
start: i * BITS_PER_PAGE,
|
|
196
|
+
bitfield: p.bitfield.subarray(0, end / 4)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
i++
|
|
201
|
+
length -= BITS_PER_PAGE
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
149
205
|
clear () {
|
|
150
206
|
return new Promise((resolve, reject) => {
|
|
151
207
|
this.storage.truncate(0, (err) => {
|
|
152
208
|
if (err) return reject(err)
|
|
153
|
-
this.
|
|
209
|
+
this._pages = new BigSparseArray()
|
|
154
210
|
this.unflushed = []
|
|
155
211
|
resolve()
|
|
156
212
|
})
|
|
@@ -182,7 +238,7 @@ module.exports = class Bitfield {
|
|
|
182
238
|
)
|
|
183
239
|
|
|
184
240
|
page.dirty = false
|
|
185
|
-
this.storage.write(page.index *
|
|
241
|
+
this.storage.write(page.index * BYTES_PER_PAGE, buf, done)
|
|
186
242
|
}
|
|
187
243
|
|
|
188
244
|
function done (err) {
|
|
@@ -210,9 +266,19 @@ module.exports = class Bitfield {
|
|
|
210
266
|
}
|
|
211
267
|
}
|
|
212
268
|
|
|
213
|
-
function ensureSize (
|
|
214
|
-
if (
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
return
|
|
269
|
+
function ensureSize (buffer, size) {
|
|
270
|
+
if (buffer.byteLength === size) return buffer
|
|
271
|
+
const copy = new Uint32Array(size)
|
|
272
|
+
copy.set(buffer, 0)
|
|
273
|
+
return copy
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function clamp (n, min, max) {
|
|
277
|
+
return Math.min(Math.max(n, min), max)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function ceilTo (n, multiple = 1) {
|
|
281
|
+
const remainder = n % multiple
|
|
282
|
+
if (remainder === 0) return n
|
|
283
|
+
return n + multiple - remainder
|
|
218
284
|
}
|
package/lib/compat.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Export the appropriate version of `quickbit-universal` as the plain import
|
|
2
|
+
// may resolve to an older version in some environments
|
|
3
|
+
let quickbit = require('quickbit-universal')
|
|
4
|
+
if (
|
|
5
|
+
typeof quickbit.findFirst !== 'function' ||
|
|
6
|
+
typeof quickbit.findLast !== 'function'
|
|
7
|
+
) {
|
|
8
|
+
// This should always load the fallback from the locally installed version
|
|
9
|
+
quickbit = require('quickbit-universal/fallback')
|
|
10
|
+
}
|
|
11
|
+
exports.quickbit = quickbit
|
package/lib/info.js
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
module.exports = class Info {
|
|
2
2
|
constructor (opts = {}) {
|
|
3
|
+
this.key = opts.key
|
|
4
|
+
this.discoveryKey = opts.discoveryKey
|
|
3
5
|
this.length = opts.length || 0
|
|
4
6
|
this.contiguousLength = opts.contiguousLength || 0
|
|
5
7
|
this.byteLength = opts.byteLength || 0
|
|
8
|
+
this.fork = opts.fork || 0
|
|
6
9
|
this.padding = opts.padding || 0
|
|
7
10
|
}
|
|
8
11
|
|
|
9
|
-
static async from (
|
|
12
|
+
static async from (session) {
|
|
10
13
|
return new Info({
|
|
11
|
-
key:
|
|
12
|
-
discoveryKey:
|
|
13
|
-
length:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
? snapshot.byteLength
|
|
19
|
-
: (core.tree.byteLength - (core.tree.length * padding)),
|
|
20
|
-
fork: core.tree.fork,
|
|
21
|
-
padding
|
|
14
|
+
key: session.key,
|
|
15
|
+
discoveryKey: session.discoveryKey,
|
|
16
|
+
length: session.length,
|
|
17
|
+
contiguousLength: session.contiguousLength,
|
|
18
|
+
byteLength: session.byteLength,
|
|
19
|
+
fork: session.fork,
|
|
20
|
+
padding: session.padding
|
|
22
21
|
})
|
|
23
22
|
}
|
|
24
23
|
}
|
package/lib/oplog.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const cenc = require('compact-encoding')
|
|
2
2
|
const b4a = require('b4a')
|
|
3
|
-
const crc32 = require('
|
|
3
|
+
const { crc32 } = require('crc-universal')
|
|
4
4
|
|
|
5
5
|
module.exports = class Oplog {
|
|
6
6
|
constructor (storage, { pageSize = 4096, headerEncoding = cenc.raw, entryEncoding = cenc.raw } = {}) {
|
package/lib/remote-bitfield.js
CHANGED
|
@@ -1,45 +1,275 @@
|
|
|
1
1
|
const BigSparseArray = require('big-sparse-array')
|
|
2
|
-
const
|
|
2
|
+
const quickbit = require('./compat').quickbit
|
|
3
|
+
|
|
4
|
+
const BITS_PER_PAGE = 32768
|
|
5
|
+
const BYTES_PER_PAGE = BITS_PER_PAGE / 8
|
|
6
|
+
const WORDS_PER_PAGE = BYTES_PER_PAGE / 4
|
|
7
|
+
const BITS_PER_SEGMENT = 2097152
|
|
8
|
+
const BYTES_PER_SEGMENT = BITS_PER_SEGMENT / 8
|
|
9
|
+
const PAGES_PER_SEGMENT = BITS_PER_SEGMENT / BITS_PER_PAGE
|
|
10
|
+
|
|
11
|
+
class RemoteBitfieldPage {
|
|
12
|
+
constructor (index, bitfield, segment) {
|
|
13
|
+
this.index = index
|
|
14
|
+
this.offset = index * BYTES_PER_PAGE - segment.offset
|
|
15
|
+
this.bitfield = bitfield
|
|
16
|
+
this.segment = segment
|
|
17
|
+
|
|
18
|
+
segment.add(this)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get tree () {
|
|
22
|
+
return this.segment.tree
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get (index) {
|
|
26
|
+
return quickbit.get(this.bitfield, index)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
set (index, val) {
|
|
30
|
+
if (quickbit.set(this.bitfield, index, val)) {
|
|
31
|
+
this.tree.update(this.offset * 8 + index)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
setRange (start, length, val) {
|
|
36
|
+
quickbit.fill(this.bitfield, val, start, start + length)
|
|
37
|
+
|
|
38
|
+
let i = Math.floor(start / 32)
|
|
39
|
+
const n = i + Math.ceil(length / 32)
|
|
40
|
+
|
|
41
|
+
while (i < n) this.tree.update(this.offset * 8 + i++ * 32)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
findFirst (val, position) {
|
|
45
|
+
return quickbit.findFirst(this.bitfield, val, position)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
findLast (val, position) {
|
|
49
|
+
return quickbit.findLast(this.bitfield, val, position)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
insert (start, bitfield) {
|
|
53
|
+
this.bitfield.set(bitfield, start / 32)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
class RemoteBitfieldSegment {
|
|
58
|
+
constructor (index) {
|
|
59
|
+
this.index = index
|
|
60
|
+
this.offset = index * BYTES_PER_SEGMENT
|
|
61
|
+
this.tree = quickbit.Index.from([])
|
|
62
|
+
this.pages = new Array(PAGES_PER_SEGMENT)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get chunks () {
|
|
66
|
+
return this.tree.chunks
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
add (page) {
|
|
70
|
+
this.pages[page.index - this.index * PAGES_PER_SEGMENT] = page
|
|
71
|
+
|
|
72
|
+
const chunk = { field: page.bitfield, offset: page.offset }
|
|
73
|
+
|
|
74
|
+
this.chunks.push(chunk)
|
|
75
|
+
|
|
76
|
+
for (let i = this.chunks.length - 2; i >= 0; i--) {
|
|
77
|
+
const prev = this.chunks[i]
|
|
78
|
+
if (prev.offset <= chunk.offset) break
|
|
79
|
+
this.chunks[i] = chunk
|
|
80
|
+
this.chunks[i + 1] = prev
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
findFirst (val, position) {
|
|
85
|
+
position = this.tree.skipFirst(!val, position)
|
|
86
|
+
|
|
87
|
+
const j = position & (BITS_PER_PAGE - 1)
|
|
88
|
+
const i = (position - j) / BITS_PER_PAGE
|
|
89
|
+
|
|
90
|
+
if (i >= PAGES_PER_SEGMENT) return -1
|
|
91
|
+
|
|
92
|
+
const p = this.pages[i]
|
|
93
|
+
|
|
94
|
+
if (p) {
|
|
95
|
+
const index = p.findFirst(val, j)
|
|
96
|
+
|
|
97
|
+
if (index !== -1) {
|
|
98
|
+
return i * BITS_PER_PAGE + index
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return -1
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
findLast (val, position) {
|
|
106
|
+
position = this.tree.skipLast(!val, position)
|
|
107
|
+
|
|
108
|
+
const j = position & (BITS_PER_PAGE - 1)
|
|
109
|
+
const i = (position - j) / BITS_PER_PAGE
|
|
110
|
+
|
|
111
|
+
if (i >= PAGES_PER_SEGMENT) return -1
|
|
112
|
+
|
|
113
|
+
const p = this.pages[i]
|
|
114
|
+
|
|
115
|
+
if (p) {
|
|
116
|
+
const index = p.findLast(val, j)
|
|
117
|
+
|
|
118
|
+
if (index !== -1) {
|
|
119
|
+
return i * BITS_PER_PAGE + index
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return -1
|
|
124
|
+
}
|
|
125
|
+
}
|
|
3
126
|
|
|
4
127
|
module.exports = class RemoteBitfield {
|
|
5
128
|
constructor () {
|
|
6
|
-
this.
|
|
7
|
-
this.
|
|
129
|
+
this._pages = new BigSparseArray()
|
|
130
|
+
this._segments = new BigSparseArray()
|
|
8
131
|
}
|
|
9
132
|
|
|
10
133
|
get (index) {
|
|
11
|
-
const j = index & (
|
|
12
|
-
const i = (index - j) /
|
|
134
|
+
const j = index & (BITS_PER_PAGE - 1)
|
|
135
|
+
const i = (index - j) / BITS_PER_PAGE
|
|
13
136
|
|
|
14
|
-
const p = this.
|
|
137
|
+
const p = this._pages.get(i)
|
|
15
138
|
|
|
16
|
-
return p ?
|
|
139
|
+
return p ? p.get(j) : false
|
|
17
140
|
}
|
|
18
141
|
|
|
19
142
|
set (index, val) {
|
|
20
|
-
const j = index & (
|
|
21
|
-
const i = (index - j) /
|
|
143
|
+
const j = index & (BITS_PER_PAGE - 1)
|
|
144
|
+
const i = (index - j) / BITS_PER_PAGE
|
|
22
145
|
|
|
23
|
-
|
|
146
|
+
let p = this._pages.get(i)
|
|
24
147
|
|
|
25
|
-
|
|
148
|
+
if (!p && val) {
|
|
149
|
+
const k = Math.floor(i / PAGES_PER_SEGMENT)
|
|
150
|
+
const s = this._segments.get(k) || this._segments.set(k, new RemoteBitfieldSegment(k))
|
|
151
|
+
|
|
152
|
+
p = this._pages.set(i, new RemoteBitfieldPage(i, new Uint32Array(WORDS_PER_PAGE), s))
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (p) p.set(j, val)
|
|
26
156
|
}
|
|
27
157
|
|
|
28
158
|
setRange (start, length, val) {
|
|
29
|
-
let j = start & (
|
|
30
|
-
let i = (start - j) /
|
|
159
|
+
let j = start & (BITS_PER_PAGE - 1)
|
|
160
|
+
let i = (start - j) / BITS_PER_PAGE
|
|
31
161
|
|
|
32
162
|
while (length > 0) {
|
|
33
|
-
|
|
163
|
+
let p = this._pages.get(i)
|
|
164
|
+
|
|
165
|
+
if (!p && val) {
|
|
166
|
+
const k = Math.floor(i / PAGES_PER_SEGMENT)
|
|
167
|
+
const s = this._segments.get(k) || this._segments.set(k, new RemoteBitfieldSegment(k))
|
|
168
|
+
|
|
169
|
+
p = this._pages.set(i, new RemoteBitfieldPage(i, new Uint32Array(WORDS_PER_PAGE), s))
|
|
170
|
+
}
|
|
34
171
|
|
|
35
|
-
const end = Math.min(j + length,
|
|
172
|
+
const end = Math.min(j + length, BITS_PER_PAGE)
|
|
36
173
|
const range = end - j
|
|
37
174
|
|
|
38
|
-
|
|
175
|
+
if (p) p.setRange(j, range, val)
|
|
39
176
|
|
|
40
177
|
j = 0
|
|
41
178
|
i++
|
|
42
179
|
length -= range
|
|
43
180
|
}
|
|
44
181
|
}
|
|
182
|
+
|
|
183
|
+
findFirst (val, position) {
|
|
184
|
+
let j = position & (BITS_PER_SEGMENT - 1)
|
|
185
|
+
let i = (position - j) / BITS_PER_SEGMENT
|
|
186
|
+
|
|
187
|
+
while (i < this._segments.maxLength) {
|
|
188
|
+
const s = this._segments.get(i)
|
|
189
|
+
|
|
190
|
+
if (s) {
|
|
191
|
+
const index = s.findFirst(val, j)
|
|
192
|
+
|
|
193
|
+
if (index !== -1) {
|
|
194
|
+
return i * BITS_PER_SEGMENT + index
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
j = 0
|
|
199
|
+
i++
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return -1
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
firstSet (position) {
|
|
206
|
+
return this.findFirst(true, position)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
firstUnset (position) {
|
|
210
|
+
return this.findFirst(false, position)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
findLast (val, position) {
|
|
214
|
+
let j = position & (BITS_PER_SEGMENT - 1)
|
|
215
|
+
let i = (position - j) / BITS_PER_SEGMENT
|
|
216
|
+
|
|
217
|
+
while (i >= 0) {
|
|
218
|
+
const s = this._segments.get(i)
|
|
219
|
+
|
|
220
|
+
if (s) {
|
|
221
|
+
const index = s.findLast(val, j)
|
|
222
|
+
|
|
223
|
+
if (index !== -1) {
|
|
224
|
+
return i * BITS_PER_SEGMENT + index
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
j = BITS_PER_SEGMENT - 1
|
|
229
|
+
i--
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return -1
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
lastSet (position) {
|
|
236
|
+
return this.findLast(true, position)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
lastUnset (position) {
|
|
240
|
+
return this.findLast(false, position)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
insert (start, bitfield) {
|
|
244
|
+
if (start % 32 !== 0) return false
|
|
245
|
+
|
|
246
|
+
let length = bitfield.byteLength * 8
|
|
247
|
+
|
|
248
|
+
let j = start & (BITS_PER_PAGE - 1)
|
|
249
|
+
let i = (start - j) / BITS_PER_PAGE
|
|
250
|
+
|
|
251
|
+
while (length > 0) {
|
|
252
|
+
let p = this._pages.get(i)
|
|
253
|
+
|
|
254
|
+
if (!p) {
|
|
255
|
+
const k = Math.floor(i / PAGES_PER_SEGMENT)
|
|
256
|
+
const s = this._segments.get(k) || this._segments.set(k, new RemoteBitfieldSegment(k))
|
|
257
|
+
|
|
258
|
+
p = this._pages.set(i, new RemoteBitfieldPage(i, new Uint32Array(WORDS_PER_PAGE), s))
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const end = Math.min(j + length, BITS_PER_PAGE)
|
|
262
|
+
const range = end - j
|
|
263
|
+
|
|
264
|
+
p.insert(j, bitfield.subarray(0, range / 32))
|
|
265
|
+
|
|
266
|
+
bitfield = bitfield.subarray(range / 32)
|
|
267
|
+
|
|
268
|
+
j = 0
|
|
269
|
+
i++
|
|
270
|
+
length -= range
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return true
|
|
274
|
+
}
|
|
45
275
|
}
|
package/lib/replicator.js
CHANGED
|
@@ -243,13 +243,15 @@ class BlockTracker {
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
class Peer {
|
|
246
|
-
constructor (replicator, protomux, channel) {
|
|
246
|
+
constructor (replicator, protomux, channel, session) {
|
|
247
247
|
this.core = replicator.core
|
|
248
248
|
this.replicator = replicator
|
|
249
249
|
this.stream = protomux.stream
|
|
250
250
|
this.protomux = protomux
|
|
251
251
|
this.remotePublicKey = this.stream.remotePublicKey
|
|
252
252
|
|
|
253
|
+
this.session = session
|
|
254
|
+
|
|
253
255
|
this.channel = channel
|
|
254
256
|
this.channel.userData = this
|
|
255
257
|
|
|
@@ -376,6 +378,8 @@ class Peer {
|
|
|
376
378
|
}
|
|
377
379
|
|
|
378
380
|
onclose (isRemote) {
|
|
381
|
+
if (this.session) this.session.close().catch(noop)
|
|
382
|
+
|
|
379
383
|
if (this.remoteOpened === false) {
|
|
380
384
|
this.replicator._ifAvailable--
|
|
381
385
|
this.replicator.updateAll()
|
|
@@ -570,17 +574,9 @@ class Peer {
|
|
|
570
574
|
}
|
|
571
575
|
|
|
572
576
|
onbitfield ({ start, bitfield }) {
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
if (bitfield.length < 1024) {
|
|
576
|
-
const buf = b4a.from(bitfield.buffer, bitfield.byteOffset, bitfield.byteLength)
|
|
577
|
-
const bigger = b4a.concat([buf, b4a.alloc(4096 - buf.length)])
|
|
578
|
-
bitfield = new Uint32Array(bigger.buffer, bigger.byteOffset, 1024)
|
|
577
|
+
if (this.remoteBitfield.insert(start, bitfield)) {
|
|
578
|
+
this._update()
|
|
579
579
|
}
|
|
580
|
-
|
|
581
|
-
this.remoteBitfield.pages.set(start / this.core.bitfield.pageSize, bitfield)
|
|
582
|
-
|
|
583
|
-
this._update()
|
|
584
580
|
}
|
|
585
581
|
|
|
586
582
|
onrange ({ drop, start, length }) {
|
|
@@ -1219,8 +1215,7 @@ module.exports = class Replicator {
|
|
|
1219
1215
|
for (let i = 0; i < this._ranges.length; i++) {
|
|
1220
1216
|
const r = this._ranges[i]
|
|
1221
1217
|
|
|
1222
|
-
|
|
1223
|
-
while (r.start < r.end && this.core.bitfield.get(mapIndex(r.blocks, r.end - 1)) === true) r.end--
|
|
1218
|
+
clampRange(this.core, r)
|
|
1224
1219
|
|
|
1225
1220
|
if (r.end !== -1 && r.start >= r.end) {
|
|
1226
1221
|
this._resolveRangeRequest(r, i--)
|
|
@@ -1332,18 +1327,8 @@ module.exports = class Replicator {
|
|
|
1332
1327
|
|
|
1333
1328
|
peer.protomux.cork()
|
|
1334
1329
|
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
for (; i < n; i++) {
|
|
1339
|
-
const p = this.core.bitfield.pages.get(i)
|
|
1340
|
-
|
|
1341
|
-
if (p) {
|
|
1342
|
-
peer.wireBitfield.send({
|
|
1343
|
-
start: i * this.core.bitfield.pageSize,
|
|
1344
|
-
bitfield: p.bitfield
|
|
1345
|
-
})
|
|
1346
|
-
}
|
|
1330
|
+
for (const msg of this.core.bitfield.want(start, length)) {
|
|
1331
|
+
peer.wireBitfield.send(msg)
|
|
1347
1332
|
}
|
|
1348
1333
|
|
|
1349
1334
|
peer.protomux.uncork()
|
|
@@ -1525,21 +1510,21 @@ module.exports = class Replicator {
|
|
|
1525
1510
|
this._maybeResolveIfAvailableRanges()
|
|
1526
1511
|
}
|
|
1527
1512
|
|
|
1528
|
-
attachTo (protomux) {
|
|
1529
|
-
const makePeer = this._makePeer.bind(this, protomux)
|
|
1513
|
+
attachTo (protomux, session) {
|
|
1514
|
+
const makePeer = this._makePeer.bind(this, protomux, session)
|
|
1530
1515
|
|
|
1531
1516
|
protomux.pair({ protocol: 'hypercore/alpha', id: this.discoveryKey }, makePeer)
|
|
1532
|
-
|
|
1533
1517
|
this._ifAvailable++
|
|
1534
1518
|
protomux.stream.opened.then((opened) => {
|
|
1535
1519
|
this._ifAvailable--
|
|
1536
1520
|
if (opened) makePeer()
|
|
1521
|
+
else if (session) session.close().catch(noop)
|
|
1537
1522
|
this._checkUpgradeIfAvailable()
|
|
1538
1523
|
})
|
|
1539
1524
|
}
|
|
1540
1525
|
|
|
1541
|
-
_makePeer (protomux) {
|
|
1542
|
-
if (protomux.opened({ protocol: 'hypercore/alpha', id: this.discoveryKey })) return
|
|
1526
|
+
_makePeer (protomux, session) {
|
|
1527
|
+
if (protomux.opened({ protocol: 'hypercore/alpha', id: this.discoveryKey })) return onnochannel()
|
|
1543
1528
|
|
|
1544
1529
|
const channel = protomux.createChannel({
|
|
1545
1530
|
userData: null,
|
|
@@ -1563,9 +1548,9 @@ module.exports = class Replicator {
|
|
|
1563
1548
|
onclose: onwireclose
|
|
1564
1549
|
})
|
|
1565
1550
|
|
|
1566
|
-
if (channel === null) return
|
|
1551
|
+
if (channel === null) return onnochannel()
|
|
1567
1552
|
|
|
1568
|
-
const peer = new Peer(this, protomux, channel)
|
|
1553
|
+
const peer = new Peer(this, protomux, channel, session)
|
|
1569
1554
|
const stream = protomux.stream
|
|
1570
1555
|
|
|
1571
1556
|
peer.channel.open({
|
|
@@ -1573,6 +1558,11 @@ module.exports = class Replicator {
|
|
|
1573
1558
|
})
|
|
1574
1559
|
|
|
1575
1560
|
return true
|
|
1561
|
+
|
|
1562
|
+
function onnochannel () {
|
|
1563
|
+
if (session) session.close().catch(noop)
|
|
1564
|
+
return false
|
|
1565
|
+
}
|
|
1576
1566
|
}
|
|
1577
1567
|
}
|
|
1578
1568
|
|
|
@@ -1592,16 +1582,34 @@ function removeInflight (inf, req) {
|
|
|
1592
1582
|
return true
|
|
1593
1583
|
}
|
|
1594
1584
|
|
|
1595
|
-
function mapIndex (blocks, index) {
|
|
1596
|
-
return blocks === null ? index : blocks[index]
|
|
1597
|
-
}
|
|
1598
|
-
|
|
1599
1585
|
function noop () {}
|
|
1600
1586
|
|
|
1601
1587
|
function toLength (start, end) {
|
|
1602
1588
|
return end === -1 ? -1 : (end < start ? 0 : end - start)
|
|
1603
1589
|
}
|
|
1604
1590
|
|
|
1591
|
+
function clampRange (core, r) {
|
|
1592
|
+
if (r.blocks === null) {
|
|
1593
|
+
const start = core.bitfield.firstUnset(r.start)
|
|
1594
|
+
|
|
1595
|
+
if (r.end === -1) {
|
|
1596
|
+
r.start = start === -1 ? core.tree.length : start
|
|
1597
|
+
} else {
|
|
1598
|
+
const end = core.bitfield.lastUnset(r.end - 1) + 1
|
|
1599
|
+
|
|
1600
|
+
if (start === -1) {
|
|
1601
|
+
r.start = r.end
|
|
1602
|
+
} else {
|
|
1603
|
+
r.start = start
|
|
1604
|
+
r.end = end
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
} else {
|
|
1608
|
+
while (r.start < r.end && core.bitfield.get(r.blocks[r.start])) r.start++
|
|
1609
|
+
while (r.start < r.end && core.bitfield.get(r.blocks[r.end - 1])) r.end--
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1605
1613
|
function onwireopen (m, c) {
|
|
1606
1614
|
return c.userData.onopen(m)
|
|
1607
1615
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hypercore",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.4.0",
|
|
4
4
|
"description": "Hypercore is a secure, distributed append-only log",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -36,14 +36,14 @@
|
|
|
36
36
|
"@hyperswarm/secret-stream": "^6.0.0",
|
|
37
37
|
"b4a": "^1.1.0",
|
|
38
38
|
"big-sparse-array": "^1.0.2",
|
|
39
|
-
"bits-to-bytes": "^1.3.0",
|
|
40
39
|
"compact-encoding": "^2.11.0",
|
|
41
|
-
"
|
|
40
|
+
"crc-universal": "^1.0.2",
|
|
42
41
|
"events": "^3.3.0",
|
|
43
42
|
"flat-tree": "^1.9.0",
|
|
44
43
|
"hypercore-crypto": "^3.2.1",
|
|
45
44
|
"is-options": "^1.0.1",
|
|
46
45
|
"protomux": "^3.4.0",
|
|
46
|
+
"quickbit-universal": "^2.0.3",
|
|
47
47
|
"random-access-file": "^4.0.0",
|
|
48
48
|
"random-array-iterator": "^1.0.0",
|
|
49
49
|
"safety-catch": "^1.0.1",
|