hypercore 10.38.2 → 11.0.1
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 +13 -30
- package/index.js +388 -444
- package/lib/audit.js +33 -41
- package/lib/bit-interlude.js +174 -0
- package/lib/bitfield.js +79 -87
- package/lib/block-store.js +12 -50
- package/lib/copy-prologue.js +236 -0
- package/lib/core.js +414 -746
- package/lib/download.js +42 -4
- package/lib/merkle-tree.js +263 -406
- package/lib/multisig.js +9 -6
- package/lib/mutex.js +4 -0
- package/lib/remote-bitfield.js +9 -9
- package/lib/replicator.js +247 -177
- package/lib/session-state.js +949 -0
- package/lib/verifier.js +20 -13
- package/package.json +2 -2
- package/lib/batch.js +0 -431
- package/lib/big-header.js +0 -55
- package/lib/oplog.js +0 -228
package/lib/audit.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
const hypercoreCrypto = require('hypercore-crypto')
|
|
2
2
|
const flat = require('flat-tree')
|
|
3
|
-
const c = require('compact-encoding')
|
|
4
3
|
const b4a = require('b4a')
|
|
5
4
|
|
|
6
|
-
const
|
|
5
|
+
const BitInterlude = require('./bit-interlude')
|
|
7
6
|
|
|
8
7
|
// this is optimised for speed over mem atm
|
|
9
8
|
// can be tweaked in the future
|
|
10
9
|
|
|
11
|
-
module.exports = async function auditCore (core) {
|
|
10
|
+
module.exports = async function auditCore (core, storage) {
|
|
12
11
|
const corrections = {
|
|
13
12
|
tree: 0,
|
|
14
13
|
blocks: 0
|
|
@@ -16,8 +15,10 @@ module.exports = async function auditCore (core) {
|
|
|
16
15
|
|
|
17
16
|
const length = core.header.tree.length
|
|
18
17
|
|
|
19
|
-
const
|
|
20
|
-
|
|
18
|
+
const bitfield = new BitInterlude()
|
|
19
|
+
|
|
20
|
+
const data = await readAllBlocks(core.storage)
|
|
21
|
+
const tree = await readAllTreeNodes(core.tree.storage)
|
|
21
22
|
|
|
22
23
|
const valid = new Uint8Array(Math.ceil(tree.byteLength / 40))
|
|
23
24
|
const stack = []
|
|
@@ -32,8 +33,8 @@ module.exports = async function auditCore (core) {
|
|
|
32
33
|
if ((node.index & 1) === 0) continue
|
|
33
34
|
|
|
34
35
|
const [left, right] = flat.children(node.index)
|
|
35
|
-
const leftNode =
|
|
36
|
-
const rightNode =
|
|
36
|
+
const leftNode = tree.get(left)
|
|
37
|
+
const rightNode = tree.get(right)
|
|
37
38
|
|
|
38
39
|
if (!rightNode && !leftNode) continue
|
|
39
40
|
|
|
@@ -48,12 +49,8 @@ module.exports = async function auditCore (core) {
|
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
if (leftNode.size) clearNode(
|
|
52
|
-
if (rightNode.size) clearNode(
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (corrections.tree) {
|
|
56
|
-
core.tree.cache.clear()
|
|
52
|
+
if (leftNode.size) clearNode(left)
|
|
53
|
+
if (rightNode.size) clearNode(right)
|
|
57
54
|
}
|
|
58
55
|
|
|
59
56
|
let i = 0
|
|
@@ -73,57 +70,52 @@ module.exports = async function auditCore (core) {
|
|
|
73
70
|
try {
|
|
74
71
|
nextOffset = await core.tree.byteOffset(i * 2)
|
|
75
72
|
} catch {
|
|
76
|
-
|
|
73
|
+
storage.deleteBlock(i)
|
|
74
|
+
bitfield.set(i, false)
|
|
77
75
|
corrections.blocks++
|
|
78
76
|
i++
|
|
79
77
|
continue
|
|
80
78
|
}
|
|
81
79
|
}
|
|
82
80
|
|
|
83
|
-
const node =
|
|
84
|
-
const blk = data.
|
|
81
|
+
const node = tree.get(i * 2)
|
|
82
|
+
const blk = data.get(i)
|
|
85
83
|
const hash = hypercoreCrypto.data(blk)
|
|
86
84
|
|
|
87
85
|
nextOffset += blk.byteLength
|
|
88
86
|
|
|
89
87
|
if (!b4a.equals(hash, node.hash)) {
|
|
90
|
-
|
|
88
|
+
storage.deleteBlock(i)
|
|
89
|
+
bitfield.set(i, false)
|
|
91
90
|
corrections.blocks++
|
|
92
91
|
}
|
|
93
92
|
|
|
94
93
|
i++
|
|
95
94
|
}
|
|
96
95
|
|
|
97
|
-
|
|
96
|
+
bitfield.flush(storage, core.bitfield)
|
|
98
97
|
|
|
99
|
-
|
|
100
|
-
if (index * 40 + 40 > tree.byteLength) return null
|
|
101
|
-
const state = { start: index * 40, end: index * 40 + 40, buffer: tree }
|
|
102
|
-
const size = c.uint64.decode(state)
|
|
103
|
-
const hash = c.fixed32.decode(state)
|
|
104
|
-
if (size === 0 && hash.equals(empty)) return null
|
|
105
|
-
return { index, size, hash }
|
|
106
|
-
}
|
|
98
|
+
return corrections
|
|
107
99
|
|
|
108
100
|
function clearNode (node) {
|
|
109
101
|
valid[node.index] = 0
|
|
102
|
+
storage.deleteTreeNode(node.index)
|
|
103
|
+
corrections.tree++
|
|
104
|
+
}
|
|
105
|
+
}
|
|
110
106
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
107
|
+
async function readAllBlocks (storage) {
|
|
108
|
+
const data = new Map()
|
|
109
|
+
for await (const block of storage.createBlockStream()) {
|
|
110
|
+
data.set(block.index, block.value)
|
|
116
111
|
}
|
|
112
|
+
return data
|
|
117
113
|
}
|
|
118
114
|
|
|
119
|
-
function
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
else resolve(data)
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
})
|
|
115
|
+
async function readAllTreeNodes (storage) {
|
|
116
|
+
const nodes = new Map()
|
|
117
|
+
for await (const node of storage.createTreeNodeStream()) {
|
|
118
|
+
nodes.set(node.index, node)
|
|
119
|
+
}
|
|
120
|
+
return nodes
|
|
129
121
|
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
const b4a = require('b4a')
|
|
2
|
+
const quickbit = require('./compat').quickbit
|
|
3
|
+
|
|
4
|
+
module.exports = class BitInterlude {
|
|
5
|
+
constructor () {
|
|
6
|
+
this.ranges = []
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
contiguousLength (from) {
|
|
10
|
+
for (const r of this.ranges) {
|
|
11
|
+
if (r.start > from) break
|
|
12
|
+
if (!r.value && r.start <= from) return r.start
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// TODO: be smarter
|
|
16
|
+
while (this.get(from) === true) from++
|
|
17
|
+
return from
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get (index) {
|
|
21
|
+
let start = 0
|
|
22
|
+
let end = this.ranges.length
|
|
23
|
+
|
|
24
|
+
while (start < end) {
|
|
25
|
+
const mid = (start + end) >> 1
|
|
26
|
+
const r = this.ranges[mid]
|
|
27
|
+
|
|
28
|
+
if (index < r.start) {
|
|
29
|
+
end = mid
|
|
30
|
+
continue
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (index >= r.end) {
|
|
34
|
+
if (mid === start) break
|
|
35
|
+
start = mid
|
|
36
|
+
continue
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return r.value
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return false
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
setRange (start, end, value) {
|
|
46
|
+
if (start === end) return
|
|
47
|
+
|
|
48
|
+
let r = null
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < this.ranges.length; i++) {
|
|
51
|
+
r = this.ranges[i]
|
|
52
|
+
|
|
53
|
+
// if already inside, stop
|
|
54
|
+
if (r.start <= start && end <= r.end) {
|
|
55
|
+
if (value === r.value) return
|
|
56
|
+
|
|
57
|
+
const ranges = mergeRanges(r, { start, end, value })
|
|
58
|
+
this.ranges.splice(i, 1, ...ranges)
|
|
59
|
+
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// we wanna overun the interval
|
|
64
|
+
if (start > r.end) {
|
|
65
|
+
continue
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// we overran but this interval is ending after us, move it back
|
|
69
|
+
if (end >= r.start && end <= r.end) {
|
|
70
|
+
r.start = r.value === value ? start : end
|
|
71
|
+
if (r.value !== value) this.ranges.splice(i, 0, { start, end, value })
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// we overran but our start is contained in this interval, move start back
|
|
76
|
+
if (start >= r.start && start <= r.end) {
|
|
77
|
+
if (r.value !== value) {
|
|
78
|
+
this.ranges.splice(++i, 0, { start, end, value })
|
|
79
|
+
r.end = start
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
start = r.start
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let remove = 0
|
|
87
|
+
|
|
88
|
+
for (let j = i; j < this.ranges.length; j++) {
|
|
89
|
+
const n = this.ranges[j]
|
|
90
|
+
if (n.start > end || n.value !== value) break
|
|
91
|
+
if (n.start <= end && n.end > end) end = n.end
|
|
92
|
+
remove++
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.ranges.splice(i, remove, { start, end, value })
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (r !== null) {
|
|
100
|
+
if (start <= r.end && end > r.end) {
|
|
101
|
+
r.end = end
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// we never
|
|
106
|
+
if (r.end > start) return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this.ranges.push({ start, end, value })
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
flush (tx, bitfield) {
|
|
113
|
+
if (!this.ranges.length) return []
|
|
114
|
+
|
|
115
|
+
let index = this.ranges[0].start
|
|
116
|
+
const final = this.ranges[this.ranges.length - 1].end
|
|
117
|
+
|
|
118
|
+
let i = 0
|
|
119
|
+
|
|
120
|
+
while (index < final) {
|
|
121
|
+
const page = bitfield.getBitfield(index) // read only
|
|
122
|
+
const pageIndex = page ? page.index : bitfield.getPageIndex(index)
|
|
123
|
+
|
|
124
|
+
const buf = b4a.allocUnsafe(bitfield.getPageByteLength())
|
|
125
|
+
|
|
126
|
+
const view = new DataView(
|
|
127
|
+
buf.buffer,
|
|
128
|
+
buf.byteOffset,
|
|
129
|
+
buf.byteLength
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
if (page) {
|
|
133
|
+
for (let i = 0; i < page.bitfield.length; i++) {
|
|
134
|
+
view.setUint32(i * 4, page.bitfield[i], true)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const last = (pageIndex + 1) * (buf.byteLength << 3)
|
|
139
|
+
const offset = pageIndex * (buf.byteLength << 3)
|
|
140
|
+
|
|
141
|
+
let hasValue = false
|
|
142
|
+
|
|
143
|
+
while (i < this.ranges.length) {
|
|
144
|
+
const { start, end, value } = this.ranges[i]
|
|
145
|
+
|
|
146
|
+
if (!hasValue && value) hasValue = true
|
|
147
|
+
|
|
148
|
+
const from = start < index ? index : start
|
|
149
|
+
const to = end < last ? end : last
|
|
150
|
+
|
|
151
|
+
quickbit.fill(buf, value, from - offset, to - offset)
|
|
152
|
+
|
|
153
|
+
index = to
|
|
154
|
+
|
|
155
|
+
if (to === last) break
|
|
156
|
+
|
|
157
|
+
i++
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (page || hasValue) tx.putBitfieldPage(pageIndex, buf)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return this.ranges
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function mergeRanges (a, b) {
|
|
168
|
+
const ranges = []
|
|
169
|
+
if (a.start < b.start) ranges.push({ start: a.start, end: b.start, value: a.value })
|
|
170
|
+
ranges.push({ start: b.start, end: b.end, value: b.value })
|
|
171
|
+
if (b.end < a.end) ranges.push({ start: b.end, end: a.end, value: a.value })
|
|
172
|
+
|
|
173
|
+
return ranges
|
|
174
|
+
}
|
package/lib/bitfield.js
CHANGED
|
@@ -14,7 +14,6 @@ const SEGMENT_GROWTH_FACTOR = 4
|
|
|
14
14
|
|
|
15
15
|
class BitfieldPage {
|
|
16
16
|
constructor (index, segment) {
|
|
17
|
-
this.dirty = false
|
|
18
17
|
this.index = index
|
|
19
18
|
this.offset = index * BYTES_PER_PAGE - segment.offset
|
|
20
19
|
this.bitfield = null
|
|
@@ -27,7 +26,7 @@ class BitfieldPage {
|
|
|
27
26
|
return this.segment.tree
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
get (index) {
|
|
29
|
+
get (index, dirty) {
|
|
31
30
|
return quickbit.get(this.bitfield, index)
|
|
32
31
|
}
|
|
33
32
|
|
|
@@ -37,11 +36,11 @@ class BitfieldPage {
|
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
setRange (start,
|
|
41
|
-
quickbit.fill(this.bitfield, val, start,
|
|
39
|
+
setRange (start, end, val) {
|
|
40
|
+
quickbit.fill(this.bitfield, val, start, end)
|
|
42
41
|
|
|
43
42
|
let i = Math.floor(start / 128)
|
|
44
|
-
const n = i + Math.ceil(
|
|
43
|
+
const n = i + Math.ceil((end - start) / 128)
|
|
45
44
|
|
|
46
45
|
while (i <= n) this.tree.update(this.offset * 8 + i++ * 128)
|
|
47
46
|
}
|
|
@@ -172,10 +171,11 @@ class BitfieldSegment {
|
|
|
172
171
|
}
|
|
173
172
|
|
|
174
173
|
module.exports = class Bitfield {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
174
|
+
static BITS_PER_PAGE = BITS_PER_PAGE
|
|
175
|
+
static BYTES_PER_PAGE = BYTES_PER_PAGE
|
|
176
|
+
|
|
177
|
+
constructor (buffer) {
|
|
178
|
+
this.resumed = !!(buffer && buffer.byteLength >= 0)
|
|
179
179
|
|
|
180
180
|
this._pages = new BigSparseArray()
|
|
181
181
|
this._segments = new BigSparseArray()
|
|
@@ -213,6 +213,10 @@ module.exports = class Bitfield {
|
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
static from (bitfield) {
|
|
217
|
+
return new Bitfield(bitfield.toBuffer(bitfield._pages.maxLength * BITS_PER_PAGE))
|
|
218
|
+
}
|
|
219
|
+
|
|
216
220
|
toBuffer (length) {
|
|
217
221
|
const pages = Math.ceil(length / BITS_PER_PAGE)
|
|
218
222
|
const buffer = b4a.allocUnsafe(pages * BYTES_PER_PAGE)
|
|
@@ -238,13 +242,29 @@ module.exports = class Bitfield {
|
|
|
238
242
|
}
|
|
239
243
|
|
|
240
244
|
getBitfield (index) {
|
|
241
|
-
const
|
|
242
|
-
const i = (index - j) / BITS_PER_PAGE
|
|
245
|
+
const i = this.getPageIndex(index)
|
|
243
246
|
|
|
244
247
|
const p = this._pages.get(i)
|
|
245
248
|
return p || null
|
|
246
249
|
}
|
|
247
250
|
|
|
251
|
+
merge (bitfield, length) {
|
|
252
|
+
let i = 0
|
|
253
|
+
|
|
254
|
+
while (i < length) {
|
|
255
|
+
const start = bitfield.firstSet(i)
|
|
256
|
+
if (start === -1) break
|
|
257
|
+
|
|
258
|
+
i = bitfield.firstUnset(start)
|
|
259
|
+
|
|
260
|
+
if (i === -1 || i > length) i = length
|
|
261
|
+
|
|
262
|
+
this.setRange(start, i, true)
|
|
263
|
+
|
|
264
|
+
if (i >= length) break
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
248
268
|
get (index) {
|
|
249
269
|
const j = index & (BITS_PER_PAGE - 1)
|
|
250
270
|
const i = (index - j) / BITS_PER_PAGE
|
|
@@ -254,6 +274,32 @@ module.exports = class Bitfield {
|
|
|
254
274
|
return p ? p.get(j) : false
|
|
255
275
|
}
|
|
256
276
|
|
|
277
|
+
getPageByteLength () {
|
|
278
|
+
return BYTES_PER_PAGE
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
getPageIndex (index) {
|
|
282
|
+
const j = index & (BITS_PER_PAGE - 1)
|
|
283
|
+
return (index - j) / BITS_PER_PAGE
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
getPage (index, create) {
|
|
287
|
+
const i = this.getPageIndex(index)
|
|
288
|
+
|
|
289
|
+
let p = this._pages.get(i)
|
|
290
|
+
|
|
291
|
+
if (p) return p
|
|
292
|
+
|
|
293
|
+
if (!create) return null
|
|
294
|
+
|
|
295
|
+
const k = Math.floor(i / PAGES_PER_SEGMENT)
|
|
296
|
+
const s = this._segments.get(k) || this._segments.set(k, new BitfieldSegment(k, new Uint32Array(k === 0 ? INITIAL_WORDS_PER_SEGMENT : WORDS_PER_SEGMENT)))
|
|
297
|
+
|
|
298
|
+
p = this._pages.set(i, new BitfieldPage(i, s))
|
|
299
|
+
|
|
300
|
+
return p
|
|
301
|
+
}
|
|
302
|
+
|
|
257
303
|
set (index, val) {
|
|
258
304
|
const j = index & (BITS_PER_PAGE - 1)
|
|
259
305
|
const i = (index - j) / BITS_PER_PAGE
|
|
@@ -267,21 +313,14 @@ module.exports = class Bitfield {
|
|
|
267
313
|
p = this._pages.set(i, new BitfieldPage(i, s))
|
|
268
314
|
}
|
|
269
315
|
|
|
270
|
-
if (p)
|
|
271
|
-
p.set(j, val)
|
|
272
|
-
|
|
273
|
-
if (!p.dirty) {
|
|
274
|
-
p.dirty = true
|
|
275
|
-
this.unflushed.push(p)
|
|
276
|
-
}
|
|
277
|
-
}
|
|
316
|
+
if (p) p.set(j, val)
|
|
278
317
|
}
|
|
279
318
|
|
|
280
|
-
setRange (start,
|
|
319
|
+
setRange (start, end, val) {
|
|
281
320
|
let j = start & (BITS_PER_PAGE - 1)
|
|
282
321
|
let i = (start - j) / BITS_PER_PAGE
|
|
283
322
|
|
|
284
|
-
while (
|
|
323
|
+
while (start < end) {
|
|
285
324
|
let p = this._pages.get(i)
|
|
286
325
|
|
|
287
326
|
if (!p && val) {
|
|
@@ -291,21 +330,14 @@ module.exports = class Bitfield {
|
|
|
291
330
|
p = this._pages.set(i, new BitfieldPage(i, s))
|
|
292
331
|
}
|
|
293
332
|
|
|
294
|
-
const
|
|
295
|
-
const range =
|
|
296
|
-
|
|
297
|
-
if (p) {
|
|
298
|
-
p.setRange(j, range, val)
|
|
333
|
+
const last = Math.min(end, BITS_PER_PAGE)
|
|
334
|
+
const range = last - j
|
|
299
335
|
|
|
300
|
-
|
|
301
|
-
p.dirty = true
|
|
302
|
-
this.unflushed.push(p)
|
|
303
|
-
}
|
|
304
|
-
}
|
|
336
|
+
if (p) p.setRange(j, last, val)
|
|
305
337
|
|
|
306
338
|
j = 0
|
|
307
339
|
i++
|
|
308
|
-
|
|
340
|
+
end -= range
|
|
309
341
|
}
|
|
310
342
|
}
|
|
311
343
|
|
|
@@ -420,68 +452,28 @@ module.exports = class Bitfield {
|
|
|
420
452
|
}
|
|
421
453
|
}
|
|
422
454
|
|
|
423
|
-
clear () {
|
|
424
|
-
return
|
|
425
|
-
this.storage.truncate(0, (err) => {
|
|
426
|
-
if (err) return reject(err)
|
|
427
|
-
this._pages = new BigSparseArray()
|
|
428
|
-
this.unflushed = []
|
|
429
|
-
resolve()
|
|
430
|
-
})
|
|
431
|
-
})
|
|
455
|
+
clear (tx) {
|
|
456
|
+
return tx.deleteBitfieldPageRange(0, -1)
|
|
432
457
|
}
|
|
433
458
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
this.
|
|
437
|
-
|
|
438
|
-
else resolve()
|
|
439
|
-
})
|
|
440
|
-
})
|
|
459
|
+
onupdate (ranges) {
|
|
460
|
+
for (const { start, end, value } of ranges) {
|
|
461
|
+
this.setRange(start, end, value)
|
|
462
|
+
}
|
|
441
463
|
}
|
|
442
464
|
|
|
443
|
-
|
|
444
|
-
return new
|
|
445
|
-
if (!this.unflushed.length) return resolve()
|
|
446
|
-
|
|
447
|
-
const self = this
|
|
448
|
-
let missing = this.unflushed.length
|
|
449
|
-
let error = null
|
|
465
|
+
static async open (storage, length) {
|
|
466
|
+
if (length === 0) return new Bitfield(storage, null)
|
|
450
467
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
page.bitfield.byteOffset,
|
|
455
|
-
page.bitfield.byteLength
|
|
456
|
-
)
|
|
468
|
+
const pages = Math.ceil(length / BITS_PER_PAGE)
|
|
469
|
+
const buffer = b4a.alloc(pages * BYTES_PER_PAGE)
|
|
470
|
+
const stream = storage.createBitfieldStream()
|
|
457
471
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
472
|
+
for await (const { index, page } of stream) {
|
|
473
|
+
buffer.set(page, index * BYTES_PER_PAGE)
|
|
474
|
+
}
|
|
461
475
|
|
|
462
|
-
|
|
463
|
-
if (err) error = err
|
|
464
|
-
if (--missing) return
|
|
465
|
-
if (error) return reject(error)
|
|
466
|
-
self.unflushed = []
|
|
467
|
-
resolve()
|
|
468
|
-
}
|
|
469
|
-
})
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
static open (storage, tree = null) {
|
|
473
|
-
return new Promise((resolve, reject) => {
|
|
474
|
-
storage.stat((err, st) => {
|
|
475
|
-
if (err) return resolve(new Bitfield(storage, null))
|
|
476
|
-
let size = st.size - (st.size & 3)
|
|
477
|
-
if (!size) return resolve(new Bitfield(storage, null))
|
|
478
|
-
if (tree) size = Math.min(size, ceilTo(tree.length / 8, 4096))
|
|
479
|
-
storage.read(0, size, (err, data) => {
|
|
480
|
-
if (err) return reject(err)
|
|
481
|
-
resolve(new Bitfield(storage, data))
|
|
482
|
-
})
|
|
483
|
-
})
|
|
484
|
-
})
|
|
476
|
+
return new Bitfield(buffer)
|
|
485
477
|
}
|
|
486
478
|
}
|
|
487
479
|
|
package/lib/block-store.js
CHANGED
|
@@ -1,63 +1,25 @@
|
|
|
1
|
-
const b4a = require('b4a')
|
|
2
|
-
const { WRITE_FAILED } = require('hypercore-errors')
|
|
3
|
-
|
|
4
1
|
module.exports = class BlockStore {
|
|
5
|
-
constructor (storage
|
|
2
|
+
constructor (storage) {
|
|
6
3
|
this.storage = storage
|
|
7
|
-
this.tree = tree
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
async get (i, tree) {
|
|
11
|
-
if (!tree) tree = this.tree
|
|
12
|
-
const [offset, size] = await tree.byteRange(2 * i)
|
|
13
|
-
return this._read(offset, size)
|
|
14
4
|
}
|
|
15
5
|
|
|
16
|
-
async
|
|
17
|
-
return
|
|
6
|
+
async get (rx, i) {
|
|
7
|
+
return rx.getBlock(i)
|
|
18
8
|
}
|
|
19
9
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return this.put(i, batch.length === 1 ? batch[0] : b4a.concat(batch), offset)
|
|
10
|
+
put (tx, i, data) {
|
|
11
|
+
tx.putBlock(i, data)
|
|
23
12
|
}
|
|
24
13
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (length === -1) this.storage.truncate(offset, done)
|
|
28
|
-
else this.storage.del(offset, length, done)
|
|
29
|
-
|
|
30
|
-
function done (err) {
|
|
31
|
-
if (err) reject(err)
|
|
32
|
-
else resolve()
|
|
33
|
-
}
|
|
34
|
-
})
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
close () {
|
|
38
|
-
return new Promise((resolve, reject) => {
|
|
39
|
-
this.storage.close((err) => {
|
|
40
|
-
if (err) reject(err)
|
|
41
|
-
else resolve()
|
|
42
|
-
})
|
|
43
|
-
})
|
|
44
|
-
}
|
|
14
|
+
putBatch (tx, i, blocks) {
|
|
15
|
+
if (blocks.length === 0) return Promise.resolve()
|
|
45
16
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (err) reject(err)
|
|
50
|
-
else resolve(data)
|
|
51
|
-
})
|
|
52
|
-
})
|
|
17
|
+
for (let j = 0; j < blocks.length; j++) {
|
|
18
|
+
tx.putBlock(i + j, blocks[j])
|
|
19
|
+
}
|
|
53
20
|
}
|
|
54
21
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
this.storage.write(offset, data, (err) => {
|
|
58
|
-
if (err) reject(WRITE_FAILED(err.message))
|
|
59
|
-
else resolve(offset + data.byteLength)
|
|
60
|
-
})
|
|
61
|
-
})
|
|
22
|
+
clear (tx, start = 0, end = -1) {
|
|
23
|
+
tx.deleteBlockRange(start, end)
|
|
62
24
|
}
|
|
63
25
|
}
|