hypercore 10.4.0 → 10.4.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/lib/bitfield.js CHANGED
@@ -2,16 +2,28 @@ const BigSparseArray = require('big-sparse-array')
2
2
  const b4a = require('b4a')
3
3
  const quickbit = require('./compat').quickbit
4
4
 
5
- const BITS_PER_PAGE = 2097152
5
+ const BITS_PER_PAGE = 32768
6
6
  const BYTES_PER_PAGE = BITS_PER_PAGE / 8
7
7
  const WORDS_PER_PAGE = BYTES_PER_PAGE / 4
8
+ const BITS_PER_SEGMENT = 2097152
9
+ const BYTES_PER_SEGMENT = BITS_PER_SEGMENT / 8
10
+ const WORDS_PER_SEGMENT = BYTES_PER_SEGMENT / 4
11
+ const INITIAL_WORDS_PER_SEGMENT = 1024
12
+ const PAGES_PER_SEGMENT = BITS_PER_SEGMENT / BITS_PER_PAGE
13
+ const SEGMENT_GROWTH_FACTOR = 4
8
14
 
9
15
  class BitfieldPage {
10
- constructor (index, bitfield) {
16
+ constructor (index, segment) {
11
17
  this.dirty = false
12
18
  this.index = index
13
- this.bitfield = bitfield
14
- this.tree = quickbit.Index.from(this.bitfield)
19
+ this.bitfield = null
20
+ this.segment = segment
21
+
22
+ segment.add(this)
23
+ }
24
+
25
+ get tree () {
26
+ return this.segment.tree
15
27
  }
16
28
 
17
29
  get (index) {
@@ -20,7 +32,7 @@ class BitfieldPage {
20
32
 
21
33
  set (index, val) {
22
34
  if (quickbit.set(this.bitfield, index, val)) {
23
- this.tree.update(index)
35
+ this.tree.update(this.offset * 8 + index)
24
36
  }
25
37
  }
26
38
 
@@ -30,34 +42,144 @@ class BitfieldPage {
30
42
  let i = Math.floor(start / 32)
31
43
  const n = i + Math.ceil(length / 32)
32
44
 
33
- while (i < n) this.tree.update(i++ * 32)
45
+ while (i < n) this.tree.update(this.offset * 8 + i++ * 32)
34
46
  }
35
47
 
36
48
  findFirst (val, position) {
37
- return quickbit.findFirst(this.bitfield, val, this.tree.skipFirst(!val, position))
49
+ return quickbit.findFirst(this.bitfield, val, position)
50
+ }
51
+
52
+ findLast (val, position) {
53
+ return quickbit.findLast(this.bitfield, val, position)
54
+ }
55
+ }
56
+
57
+ class BitfieldSegment {
58
+ constructor (index, bitfield) {
59
+ this.index = index
60
+ this.offset = index * BYTES_PER_SEGMENT
61
+ this.tree = quickbit.Index.from(bitfield)
62
+ this.pages = new Array(PAGES_PER_SEGMENT)
63
+ }
64
+
65
+ get bitfield () {
66
+ return this.tree.field
67
+ }
68
+
69
+ add (page) {
70
+ const i = page.index - this.index * PAGES_PER_SEGMENT
71
+ this.pages[i] = page
72
+
73
+ const start = i * WORDS_PER_PAGE
74
+ const end = start + WORDS_PER_PAGE
75
+
76
+ if (end >= this.bitfield.length) this.reallocate(end)
77
+
78
+ page.bitfield = this.bitfield.subarray(start, end)
79
+ }
80
+
81
+ reallocate (length) {
82
+ let target = this.bitfield.length
83
+ while (target < length) target *= SEGMENT_GROWTH_FACTOR
84
+
85
+ const bitfield = new Uint32Array(target)
86
+ bitfield.set(this.bitfield)
87
+
88
+ this.tree = quickbit.Index.from(bitfield)
89
+
90
+ for (let i = 0; i < this.pages.length; i++) {
91
+ const page = this.pages[i]
92
+ if (!page) continue
93
+
94
+ const start = i * WORDS_PER_PAGE
95
+ const end = start + WORDS_PER_PAGE
96
+
97
+ page.bitfield = bitfield.subarray(start, end)
98
+ }
99
+ }
100
+
101
+ findFirst (val, position) {
102
+ position = this.tree.skipFirst(!val, position)
103
+
104
+ const j = position & (BITS_PER_PAGE - 1)
105
+ const i = (position - j) / BITS_PER_PAGE
106
+
107
+ if (i >= PAGES_PER_SEGMENT) return -1
108
+
109
+ const p = this.pages[i]
110
+
111
+ if (p) {
112
+ const index = p.findFirst(val, j)
113
+
114
+ if (index !== -1) {
115
+ return i * BITS_PER_PAGE + index
116
+ }
117
+ }
118
+
119
+ return -1
38
120
  }
39
121
 
40
122
  findLast (val, position) {
41
- return quickbit.findLast(this.bitfield, val, this.tree.skipLast(!val, position))
123
+ position = this.tree.skipLast(!val, position)
124
+
125
+ const j = position & (BITS_PER_PAGE - 1)
126
+ const i = (position - j) / BITS_PER_PAGE
127
+
128
+ if (i >= PAGES_PER_SEGMENT) return -1
129
+
130
+ const p = this.pages[i]
131
+
132
+ if (p) {
133
+ const index = p.findLast(val, j)
134
+
135
+ if (index !== -1) {
136
+ return i * BITS_PER_PAGE + index
137
+ }
138
+ }
139
+
140
+ return -1
42
141
  }
43
142
  }
44
143
 
45
144
  module.exports = class Bitfield {
46
- constructor (storage, buf) {
145
+ constructor (storage, buffer) {
47
146
  this.unflushed = []
48
147
  this.storage = storage
49
- this.resumed = !!(buf && buf.byteLength >= 4)
148
+ this.resumed = !!(buffer && buffer.byteLength >= 4)
50
149
 
51
150
  this._pages = new BigSparseArray()
151
+ this._segments = new BigSparseArray()
152
+
153
+ const view = this.resumed
154
+ ? new Uint32Array(
155
+ buffer.buffer,
156
+ buffer.byteOffset,
157
+ Math.floor(buffer.byteLength / 4)
158
+ )
159
+ : new Uint32Array(INITIAL_WORDS_PER_SEGMENT)
160
+
161
+ for (let i = 0; i < view.length; i += WORDS_PER_SEGMENT) {
162
+ let bitfield = view.subarray(i, i + (WORDS_PER_SEGMENT))
163
+ let length = WORDS_PER_SEGMENT
164
+
165
+ if (i === 0) {
166
+ length = INITIAL_WORDS_PER_SEGMENT
167
+ while (length < bitfield.length) length *= SEGMENT_GROWTH_FACTOR
168
+ }
52
169
 
53
- const all = this.resumed
54
- ? new Uint32Array(buf.buffer, buf.byteOffset, Math.floor(buf.byteLength / 4))
55
- : new Uint32Array(WORDS_PER_PAGE)
170
+ if (bitfield.length !== length) {
171
+ const copy = new Uint32Array(length)
172
+ copy.set(bitfield, 0)
173
+ bitfield = copy
174
+ }
175
+
176
+ const segment = new BitfieldSegment(i / (WORDS_PER_SEGMENT), bitfield)
177
+ this._segments.set(segment.index, segment)
56
178
 
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)
179
+ for (let j = 0; j < bitfield.length; j += WORDS_PER_PAGE) {
180
+ const page = new BitfieldPage((i + j) / WORDS_PER_PAGE, segment)
181
+ this._pages.set(page.index, page)
182
+ }
61
183
  }
62
184
  }
63
185
 
@@ -77,7 +199,10 @@ module.exports = class Bitfield {
77
199
  let p = this._pages.get(i)
78
200
 
79
201
  if (!p && val) {
80
- p = this._pages.set(i, new BitfieldPage(i, new Uint32Array(WORDS_PER_PAGE)))
202
+ const k = Math.floor(i / PAGES_PER_SEGMENT)
203
+ const s = this._segments.get(k) || this._segments.set(k, new BitfieldSegment(k, new Uint32Array(k === 0 ? INITIAL_WORDS_PER_SEGMENT : WORDS_PER_SEGMENT)))
204
+
205
+ p = this._pages.set(i, new BitfieldPage(i, s))
81
206
  }
82
207
 
83
208
  if (p) {
@@ -98,7 +223,10 @@ module.exports = class Bitfield {
98
223
  let p = this._pages.get(i)
99
224
 
100
225
  if (!p && val) {
101
- p = this._pages.set(i, new BitfieldPage(i, new Uint32Array(WORDS_PER_PAGE)))
226
+ const k = Math.floor(i / PAGES_PER_SEGMENT)
227
+ const s = this._segments.get(k) || this._segments.set(k, new BitfieldSegment(k, new Uint32Array(k === 0 ? INITIAL_WORDS_PER_SEGMENT : WORDS_PER_SEGMENT)))
228
+
229
+ p = this._pages.set(i, new BitfieldPage(i, s))
102
230
  }
103
231
 
104
232
  const end = Math.min(j + length, BITS_PER_PAGE)
@@ -120,17 +248,17 @@ module.exports = class Bitfield {
120
248
  }
121
249
 
122
250
  findFirst (val, position) {
123
- let j = position & (BITS_PER_PAGE - 1)
124
- let i = (position - j) / BITS_PER_PAGE
251
+ let j = position & (BITS_PER_SEGMENT - 1)
252
+ let i = (position - j) / BITS_PER_SEGMENT
125
253
 
126
- while (i < this._pages.maxLength) {
127
- const p = this._pages.get(i)
254
+ while (i < this._segments.maxLength) {
255
+ const s = this._segments.get(i)
128
256
 
129
- if (p) {
130
- const index = p.findFirst(val, j)
257
+ if (s) {
258
+ const index = s.findFirst(val, j)
131
259
 
132
260
  if (index !== -1) {
133
- return i * BITS_PER_PAGE + index
261
+ return i * BITS_PER_SEGMENT + index
134
262
  }
135
263
  }
136
264
 
@@ -150,21 +278,21 @@ module.exports = class Bitfield {
150
278
  }
151
279
 
152
280
  findLast (val, position) {
153
- let j = position & (BITS_PER_PAGE - 1)
154
- let i = (position - j) / BITS_PER_PAGE
281
+ let j = position & (BITS_PER_SEGMENT - 1)
282
+ let i = (position - j) / BITS_PER_SEGMENT
155
283
 
156
284
  while (i >= 0) {
157
- const p = this._pages.get(i)
285
+ const s = this._segments.get(i)
158
286
 
159
- if (p) {
160
- const index = p.findLast(val, j)
287
+ if (s) {
288
+ const index = s.findLast(val, j)
161
289
 
162
290
  if (index !== -1) {
163
- return i * BITS_PER_PAGE + index
291
+ return i * BITS_PER_SEGMENT + index
164
292
  }
165
293
  }
166
294
 
167
- j = BITS_PER_PAGE - 1
295
+ j = BITS_PER_SEGMENT - 1
168
296
  i--
169
297
  }
170
298
 
@@ -180,25 +308,25 @@ module.exports = class Bitfield {
180
308
  }
181
309
 
182
310
  * want (start, length) {
183
- const j = start & (BITS_PER_PAGE - 1)
184
- let i = (start - j) / BITS_PER_PAGE
311
+ const j = start & (BITS_PER_SEGMENT - 1)
312
+ let i = (start - j) / BITS_PER_SEGMENT
185
313
 
186
314
  while (length > 0) {
187
- const p = this._pages.get(i)
315
+ const s = this._segments.get(i)
188
316
 
189
- if (p) {
317
+ if (s) {
190
318
  // We always send at least 4 KiB worth of bitfield in a want, rounding
191
319
  // to the nearest 4 KiB.
192
- const end = ceilTo(clamp(length / 8, 4096, BYTES_PER_PAGE), 4096)
320
+ const end = ceilTo(clamp(length / 8, 4096, BYTES_PER_SEGMENT), 4096)
193
321
 
194
322
  yield {
195
- start: i * BITS_PER_PAGE,
196
- bitfield: p.bitfield.subarray(0, end / 4)
323
+ start: i * BITS_PER_SEGMENT,
324
+ bitfield: s.bitfield.subarray(0, end / 4)
197
325
  }
198
326
  }
199
327
 
200
328
  i++
201
- length -= BITS_PER_PAGE
329
+ length -= BITS_PER_SEGMENT
202
330
  }
203
331
  }
204
332
 
@@ -251,12 +379,13 @@ module.exports = class Bitfield {
251
379
  })
252
380
  }
253
381
 
254
- static open (storage) {
382
+ static open (storage, tree = null) {
255
383
  return new Promise((resolve, reject) => {
256
384
  storage.stat((err, st) => {
257
385
  if (err) return resolve(new Bitfield(storage, null))
258
- const size = st.size - (st.size & 3)
386
+ let size = st.size - (st.size & 3)
259
387
  if (!size) return resolve(new Bitfield(storage, null))
388
+ if (tree) size = Math.min(size, ceilTo(tree.length / 8, 4096))
260
389
  storage.read(0, size, (err, data) => {
261
390
  if (err) return reject(err)
262
391
  resolve(new Bitfield(storage, data))
@@ -266,13 +395,6 @@ module.exports = class Bitfield {
266
395
  }
267
396
  }
268
397
 
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
398
  function clamp (n, min, max) {
277
399
  return Math.min(Math.max(n, min), max)
278
400
  }
package/lib/core.js CHANGED
@@ -108,7 +108,7 @@ module.exports = class Core {
108
108
  }
109
109
 
110
110
  const tree = await MerkleTree.open(treeFile, { crypto, ...header.tree })
111
- const bitfield = await Bitfield.open(bitfieldFile)
111
+ const bitfield = await Bitfield.open(bitfieldFile, tree)
112
112
  const blocks = new BlockStore(dataFile, tree)
113
113
 
114
114
  if (overwrite) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore",
3
- "version": "10.4.0",
3
+ "version": "10.4.1",
4
4
  "description": "Hypercore is a secure, distributed append-only log",
5
5
  "main": "index.js",
6
6
  "scripts": {