hypercore 10.0.0-alpha.0 → 10.0.0-alpha.12

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 CHANGED
@@ -3,6 +3,8 @@ const raf = require('random-access-file')
3
3
  const isOptions = require('is-options')
4
4
  const hypercoreCrypto = require('hypercore-crypto')
5
5
  const c = require('compact-encoding')
6
+ const b4a = require('b4a')
7
+ const Xache = require('xache')
6
8
  const NoiseSecretStream = require('@hyperswarm/secret-stream')
7
9
  const codecs = require('codecs')
8
10
 
@@ -11,6 +13,8 @@ const fsctl = requireMaybe('fsctl') || { lock: noop, sparse: noop }
11
13
  const Replicator = require('./lib/replicator')
12
14
  const Extensions = require('./lib/extensions')
13
15
  const Core = require('./lib/core')
16
+ const BlockEncryption = require('./lib/block-encryption')
17
+ const { ReadStream } = require('./lib/streams')
14
18
 
15
19
  const promises = Symbol.for('hypercore.promises')
16
20
  const inspect = Symbol.for('nodejs.util.inspect.custom')
@@ -27,14 +31,17 @@ module.exports = class Hypercore extends EventEmitter {
27
31
  opts = key
28
32
  key = null
29
33
  }
34
+
30
35
  if (key && typeof key === 'string') {
31
- key = Buffer.from(key, 'hex')
36
+ key = b4a.from(key, 'hex')
32
37
  }
33
- if (key && key.byteLength !== 32) {
38
+
39
+ if (!opts) opts = {}
40
+
41
+ if (!opts.crypto && key && key.byteLength !== 32) {
34
42
  throw new Error('Hypercore key should be 32 bytes')
35
43
  }
36
44
 
37
- if (!opts) opts = {}
38
45
  if (!storage) storage = opts.storage
39
46
 
40
47
  this[promises] = true
@@ -43,7 +50,9 @@ module.exports = class Hypercore extends EventEmitter {
43
50
  this.crypto = opts.crypto || hypercoreCrypto
44
51
  this.core = null
45
52
  this.replicator = null
53
+ this.encryption = null
46
54
  this.extensions = opts.extensions || new Extensions()
55
+ this.cache = opts.cache === true ? new Xache({ maxSize: 65536, maxAge: 0 }) : (opts.cache || null)
47
56
 
48
57
  this.valueEncoding = null
49
58
  this.key = key || null
@@ -59,6 +68,8 @@ module.exports = class Hypercore extends EventEmitter {
59
68
  this.closing = null
60
69
  this.opening = opts._opening || this._open(key, storage, opts)
61
70
  this.opening.catch(noop)
71
+
72
+ this._preappend = preappend.bind(this)
62
73
  }
63
74
 
64
75
  [inspect] (depth, opts) {
@@ -84,12 +95,14 @@ module.exports = class Hypercore extends EventEmitter {
84
95
  return noiseStream.rawStream
85
96
  }
86
97
 
87
- static defaultStorage (storage) {
98
+ static defaultStorage (storage, opts = {}) {
88
99
  if (typeof storage !== 'string') return storage
89
100
  const directory = storage
101
+ const toLock = opts.lock || 'oplog'
90
102
  return function createFile (name) {
91
- const lock = name === 'oplog' ? fsctl.lock : null
92
- const sparse = name !== 'oplog' ? fsctl.sparse : null
103
+ const locked = name === toLock || name.endsWith('/' + toLock)
104
+ const lock = locked ? fsctl.lock : null
105
+ const sparse = locked ? null : null // fsctl.sparse, disable sparse on windows - seems to fail for some people. TODO: investigate
93
106
  return raf(name, { directory, lock, sparse })
94
107
  }
95
108
  }
@@ -131,6 +144,7 @@ module.exports = class Hypercore extends EventEmitter {
131
144
  this.discoveryKey = o.discoveryKey
132
145
  this.core = o.core
133
146
  this.replicator = o.replicator
147
+ this.encryption = o.encryption
134
148
  this.writable = !!this.sign
135
149
  this.autoClose = o.autoClose
136
150
  }
@@ -200,7 +214,7 @@ module.exports = class Hypercore extends EventEmitter {
200
214
  }
201
215
 
202
216
  get byteLength () {
203
- return this.core === null ? 0 : this.core.tree.byteLength
217
+ return this.core === null ? 0 : this.core.tree.byteLength - (this.core.tree.length * this.padding)
204
218
  }
205
219
 
206
220
  get fork () {
@@ -211,6 +225,14 @@ module.exports = class Hypercore extends EventEmitter {
211
225
  return this.replicator === null ? [] : this.replicator.peers
212
226
  }
213
227
 
228
+ get encryptionKey () {
229
+ return this.encryption && this.encryption.key
230
+ }
231
+
232
+ get padding () {
233
+ return this.encryption === null ? 0 : this.encryption.padding
234
+ }
235
+
214
236
  ready () {
215
237
  return this.opening
216
238
  }
@@ -264,6 +286,10 @@ module.exports = class Hypercore extends EventEmitter {
264
286
  this.key = this.core.header.signer.publicKey
265
287
  this.writable = !!this.sign
266
288
 
289
+ if (!this.encryption && opts.encryptionKey) {
290
+ this.encryption = new BlockEncryption(opts.encryptionKey, this.key)
291
+ }
292
+
267
293
  this.extensions.attach(this.replicator)
268
294
  this.opened = true
269
295
 
@@ -279,8 +305,13 @@ module.exports = class Hypercore extends EventEmitter {
279
305
  _oncoreupdate (status, bitfield, value, from) {
280
306
  if (status !== 0) {
281
307
  for (let i = 0; i < this.sessions.length; i++) {
282
- if ((status & 0b10) !== 0) this.sessions[i].emit('truncate', this.core.tree.fork)
283
- if ((status & 0b01) !== 0) this.sessions[i].emit('append')
308
+ if ((status & 0b10) !== 0) {
309
+ if (this.cache) this.cache.clear()
310
+ this.sessions[i].emit('truncate', this.core.tree.fork)
311
+ }
312
+ if ((status & 0b01) !== 0) {
313
+ this.sessions[i].emit('append')
314
+ }
284
315
  }
285
316
 
286
317
  this.replicator.broadcastInfo()
@@ -293,8 +324,10 @@ module.exports = class Hypercore extends EventEmitter {
293
324
  }
294
325
 
295
326
  if (value) {
327
+ const byteLength = value.byteLength - this.padding
328
+
296
329
  for (let i = 0; i < this.sessions.length; i++) {
297
- this.sessions[i].emit('download', bitfield.start, value, from)
330
+ this.sessions[i].emit('download', bitfield.start, byteLength, from)
298
331
  }
299
332
  }
300
333
  }
@@ -331,7 +364,7 @@ module.exports = class Hypercore extends EventEmitter {
331
364
  async seek (bytes) {
332
365
  if (this.opened === false) await this.opening
333
366
 
334
- const s = this.core.tree.seek(bytes)
367
+ const s = this.core.tree.seek(bytes, this.padding)
335
368
 
336
369
  return (await s.update()) || this.replicator.requestSeek(s)
337
370
  }
@@ -344,25 +377,59 @@ module.exports = class Hypercore extends EventEmitter {
344
377
 
345
378
  async get (index, opts) {
346
379
  if (this.opened === false) await this.opening
380
+ const c = this.cache && this.cache.get(index)
381
+ if (c) return c
382
+ const fork = this.core.tree.fork
383
+ const b = await this._get(index, opts)
384
+ if (this.cache && fork === this.core.tree.fork && b) this.cache.set(index, b)
385
+ return b
386
+ }
387
+
388
+ async _get (index, opts) {
347
389
  const encoding = (opts && opts.valueEncoding && c.from(codecs(opts.valueEncoding))) || this.valueEncoding
348
390
 
349
- if (this.core.bitfield.get(index)) return decode(encoding, await this.core.blocks.get(index))
350
- if (opts && opts.onwait) opts.onwait(index)
391
+ let block
392
+
393
+ if (this.core.bitfield.get(index)) {
394
+ block = await this.core.blocks.get(index)
395
+ } else {
396
+ if (opts && opts.onwait) opts.onwait(index)
397
+ block = await this.replicator.requestBlock(index)
398
+ }
399
+
400
+ if (this.encryption) this.encryption.decrypt(index, block)
401
+ return this._decode(encoding, block)
402
+ }
351
403
 
352
- return decode(encoding, await this.replicator.requestBlock(index))
404
+ createReadStream (opts) {
405
+ return new ReadStream(this, opts)
353
406
  }
354
407
 
355
408
  download (range) {
356
- const start = (range && range.start) || 0
357
- const end = typeof (range && range.end) === 'number' ? range.end : -1 // download all
358
409
  const linear = !!(range && range.linear)
359
410
 
360
- // TODO: support range.blocks
411
+ let start
412
+ let end
413
+ let filter
414
+
415
+ if (range && range.blocks) {
416
+ const blocks = range.blocks instanceof Set
417
+ ? range.blocks
418
+ : new Set(range.blocks)
419
+
420
+ start = range.start || (blocks.size ? min(range.blocks) : 0)
421
+ end = range.end || (blocks.size ? max(range.blocks) + 1 : 0)
422
+
423
+ filter = (i) => blocks.has(i)
424
+ } else {
425
+ start = (range && range.start) || 0
426
+ end = typeof (range && range.end) === 'number' ? range.end : -1 // download all
427
+ }
361
428
 
362
- const r = Replicator.createRange(start, end, linear)
429
+ const r = Replicator.createRange(start, end, filter, linear)
363
430
 
364
431
  if (this.opened) this.replicator.addRange(r)
365
- else this.opening.then(() => this.replicator.addRange(r))
432
+ else this.opening.then(() => this.replicator.addRange(r), noop)
366
433
 
367
434
  return r
368
435
  }
@@ -392,22 +459,16 @@ module.exports = class Hypercore extends EventEmitter {
392
459
  if (this.opened === false) await this.opening
393
460
  if (this.writable === false) throw new Error('Core is not writable')
394
461
 
395
- const blks = Array.isArray(blocks) ? blocks : [blocks]
396
- const buffers = new Array(blks.length)
397
-
398
- for (let i = 0; i < blks.length; i++) {
399
- const blk = blks[i]
462
+ blocks = Array.isArray(blocks) ? blocks : [blocks]
400
463
 
401
- const buf = Buffer.isBuffer(blk)
402
- ? blk
403
- : this.valueEncoding
404
- ? c.encode(this.valueEncoding, blk)
405
- : Buffer.from(blk)
464
+ const preappend = this.encryption && this._preappend
465
+ const buffers = new Array(blocks.length)
406
466
 
407
- buffers[i] = buf
467
+ for (let i = 0; i < blocks.length; i++) {
468
+ buffers[i] = this._encode(this.valueEncoding, blocks[i])
408
469
  }
409
470
 
410
- return await this.core.append(buffers, this.sign)
471
+ return await this.core.append(buffers, this.sign, { preappend })
411
472
  }
412
473
 
413
474
  registerExtension (name, handlers) {
@@ -418,14 +479,38 @@ module.exports = class Hypercore extends EventEmitter {
418
479
  onextensionupdate () {
419
480
  if (this.replicator !== null) this.replicator.broadcastOptions()
420
481
  }
421
- }
422
482
 
423
- function noop () {}
483
+ _encode (enc, val) {
484
+ const state = { start: this.padding, end: this.padding, buffer: null }
485
+
486
+ if (b4a.isBuffer(val)) {
487
+ if (state.start === 0) return val
488
+ state.end += val.byteLength
489
+ } else if (enc) {
490
+ enc.preencode(state, val)
491
+ } else {
492
+ val = b4a.from(val)
493
+ if (state.start === 0) return val
494
+ state.end += val.byteLength
495
+ }
496
+
497
+ state.buffer = b4a.allocUnsafe(state.end)
498
+
499
+ if (enc) enc.encode(state, val)
500
+ else state.buffer.set(val, state.start)
424
501
 
425
- function decode (enc, buf) {
426
- return enc ? c.decode(enc, buf) : buf
502
+ return state.buffer
503
+ }
504
+
505
+ _decode (enc, block) {
506
+ block = block.subarray(this.padding)
507
+ if (enc) return c.decode(enc, block)
508
+ return block
509
+ }
427
510
  }
428
511
 
512
+ function noop () {}
513
+
429
514
  function isStream (s) {
430
515
  return typeof s === 'object' && s && typeof s.pipe === 'function'
431
516
  }
@@ -439,5 +524,27 @@ function requireMaybe (name) {
439
524
  }
440
525
 
441
526
  function toHex (buf) {
442
- return buf && buf.toString('hex')
527
+ return buf && b4a.toString(buf, 'hex')
528
+ }
529
+
530
+ function reduce (iter, fn, acc) {
531
+ for (const item of iter) acc = fn(acc, item)
532
+ return acc
533
+ }
534
+
535
+ function min (arr) {
536
+ return reduce(arr, (a, b) => Math.min(a, b), Infinity)
537
+ }
538
+
539
+ function max (arr) {
540
+ return reduce(arr, (a, b) => Math.max(a, b), -Infinity)
541
+ }
542
+
543
+ function preappend (blocks) {
544
+ const offset = this.core.tree.length
545
+ const fork = this.core.tree.fork
546
+
547
+ for (let i = 0; i < blocks.length; i++) {
548
+ this.encryption.encrypt(offset + i, blocks[i], fork)
549
+ }
443
550
  }
package/lib/bitfield.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // TODO: needs massive improvements obvs
2
2
 
3
3
  const BigSparseArray = require('big-sparse-array')
4
+ const b4a = require('b4a')
4
5
 
5
6
  class FixedBitfield {
6
7
  constructor (index, bitfield) {
@@ -116,9 +117,9 @@ module.exports = class Bitfield {
116
117
  let error = null
117
118
 
118
119
  for (const page of this.unflushed) {
119
- const b = Buffer.from(page.bitfield.buffer, page.bitfield.byteOffset, page.bitfield.byteLength)
120
+ const buf = b4a.from(page.bitfield.buffer, page.bitfield.byteOffset, page.bitfield.byteLength)
120
121
  page.dirty = false
121
- this.storage.write(page.index * 4096, b, done)
122
+ this.storage.write(page.index * 4096, buf, done)
122
123
  }
123
124
 
124
125
  function done (err) {
@@ -0,0 +1,68 @@
1
+ const sodium = require('sodium-universal')
2
+ const c = require('compact-encoding')
3
+ const b4a = require('b4a')
4
+
5
+ const nonce = b4a.alloc(sodium.crypto_stream_NONCEBYTES)
6
+
7
+ module.exports = class BlockEncryption {
8
+ constructor (encryptionKey, hypercoreKey) {
9
+ const subKeys = b4a.alloc(2 * sodium.crypto_stream_KEYBYTES)
10
+
11
+ this.key = encryptionKey
12
+ this.blockKey = subKeys.subarray(0, sodium.crypto_stream_KEYBYTES)
13
+ this.blindingKey = subKeys.subarray(sodium.crypto_stream_KEYBYTES)
14
+ this.padding = 8
15
+
16
+ sodium.crypto_generichash(this.blockKey, encryptionKey, hypercoreKey)
17
+ sodium.crypto_generichash(this.blindingKey, this.blockKey)
18
+ }
19
+
20
+ encrypt (index, block, fork) {
21
+ const padding = block.subarray(0, this.padding)
22
+ block = block.subarray(this.padding)
23
+
24
+ c.uint64.encode({ start: 0, end: 8, buffer: padding }, fork)
25
+ c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
26
+
27
+ // Zero out any previous padding.
28
+ nonce.fill(0, 8, 8 + padding.byteLength)
29
+
30
+ // Blind the fork ID, possibly risking reusing the nonce on a reorg of the
31
+ // Hypercore. This is fine as the blinding is best-effort and the latest
32
+ // fork ID shared on replication anyway.
33
+ sodium.crypto_stream_xor(
34
+ padding,
35
+ padding,
36
+ nonce,
37
+ this.blindingKey
38
+ )
39
+
40
+ nonce.set(padding, 8)
41
+
42
+ // The combination of a (blinded) fork ID and a block index is unique for a
43
+ // given Hypercore and is therefore a valid nonce for encrypting the block.
44
+ sodium.crypto_stream_xor(
45
+ block,
46
+ block,
47
+ nonce,
48
+ this.blockKey
49
+ )
50
+ }
51
+
52
+ decrypt (index, block) {
53
+ const padding = block.subarray(0, this.padding)
54
+ block = block.subarray(this.padding)
55
+
56
+ c.uint64.encode({ start: 0, end: 8, buffer: nonce }, index)
57
+
58
+ nonce.set(padding, 8)
59
+
60
+ // Decrypt the block using the blinded fork ID.
61
+ sodium.crypto_stream_xor(
62
+ block,
63
+ block,
64
+ nonce,
65
+ this.blockKey
66
+ )
67
+ }
68
+ }
@@ -1,3 +1,5 @@
1
+ const b4a = require('b4a')
2
+
1
3
  module.exports = class BlockStore {
2
4
  constructor (storage, tree) {
3
5
  this.storage = storage
@@ -15,7 +17,7 @@ module.exports = class BlockStore {
15
17
 
16
18
  putBatch (i, batch, offset) {
17
19
  if (batch.length === 0) return Promise.resolve()
18
- return this.put(i, batch.length === 1 ? batch[0] : Buffer.concat(batch), offset)
20
+ return this.put(i, batch.length === 1 ? batch[0] : b4a.concat(batch), offset)
19
21
  }
20
22
 
21
23
  clear () {
package/lib/core.js CHANGED
@@ -1,4 +1,3 @@
1
- const fsctl = requireMaybe('fsctl')
2
1
  const hypercoreCrypto = require('hypercore-crypto')
3
2
  const Oplog = require('./oplog')
4
3
  const Mutex = require('./mutex')
@@ -7,8 +6,6 @@ const BlockStore = require('./block-store')
7
6
  const Bitfield = require('./bitfield')
8
7
  const { oplogHeader, oplogEntry } = require('./messages')
9
8
 
10
- const lock = fsctl ? fsctl.lock : null
11
-
12
9
  module.exports = class Core {
13
10
  constructor (header, crypto, oplog, tree, blocks, bitfield, sign, onupdate) {
14
11
  this.onupdate = onupdate
@@ -29,7 +26,7 @@ module.exports = class Core {
29
26
  }
30
27
 
31
28
  static async open (storage, opts = {}) {
32
- const oplogFile = storage('oplog', { lock })
29
+ const oplogFile = storage('oplog')
33
30
  const treeFile = storage('tree')
34
31
  const bitfieldFile = storage('bitfield')
35
32
  const dataFile = storage('data')
@@ -217,10 +214,12 @@ module.exports = class Core {
217
214
  }
218
215
  }
219
216
 
220
- async append (values, sign = this.defaultSign) {
217
+ async append (values, sign = this.defaultSign, hooks = {}) {
221
218
  await this._mutex.lock()
222
219
 
223
220
  try {
221
+ if (hooks.preappend) await hooks.preappend(values)
222
+
224
223
  if (!values.length) return this.tree.length
225
224
 
226
225
  const batch = this.tree.batch()
@@ -467,11 +466,3 @@ function updateUserData (list, key, value) {
467
466
  }
468
467
 
469
468
  function noop () {}
470
-
471
- function requireMaybe (name) {
472
- try {
473
- return require(name)
474
- } catch {
475
- return null
476
- }
477
- }
@@ -1,9 +1,10 @@
1
1
  const flat = require('flat-tree')
2
2
  const crypto = require('hypercore-crypto')
3
- const uint64le = require('uint64le')
3
+ const c = require('compact-encoding')
4
+ const b4a = require('b4a')
4
5
 
5
- const BLANK_HASH = Buffer.alloc(32)
6
- const OLD_TREE = Buffer.from([5, 2, 87, 2, 0, 0, 40, 7, 66, 76, 65, 75, 69, 50, 98])
6
+ const BLANK_HASH = b4a.alloc(32)
7
+ const OLD_TREE = b4a.from([5, 2, 87, 2, 0, 0, 40, 7, 66, 76, 65, 75, 69, 50, 98])
7
8
 
8
9
  class NodeQueue {
9
10
  constructor (nodes, extra = null) {
@@ -272,11 +273,15 @@ class ReorgBatch extends MerkleTreeBatch {
272
273
  }
273
274
 
274
275
  class ByteSeeker {
275
- constructor (tree, bytes) {
276
+ constructor (tree, bytes, padding = 0) {
276
277
  this.tree = tree
277
278
  this.bytes = bytes
278
- this.start = bytes >= tree.byteLength ? tree.length : 0
279
- this.end = bytes < tree.byteLength ? tree.length : 0
279
+ this.padding = padding
280
+
281
+ const size = tree.byteLength - (tree.length * padding)
282
+
283
+ this.start = bytes >= size ? tree.length : 0
284
+ this.end = bytes < size ? tree.length : 0
280
285
  }
281
286
 
282
287
  nodes () {
@@ -287,12 +292,12 @@ class ByteSeeker {
287
292
  if (!bytes) return [0, 0]
288
293
 
289
294
  for (const node of this.tree.roots) { // all async ticks happen once we find the root so safe
290
- if (bytes === node.size) {
291
- return [flat.rightSpan(node.index) + 2, 0]
292
- }
295
+ let size = node.size
296
+ if (this.padding > 0) size -= this.padding * flat.countLeaves(node.index)
293
297
 
294
- if (bytes > node.size) {
295
- bytes -= node.size
298
+ if (bytes === size) return [flat.rightSpan(node.index) + 2, 0]
299
+ if (bytes > size) {
300
+ bytes -= size
296
301
  continue
297
302
  }
298
303
 
@@ -301,9 +306,12 @@ class ByteSeeker {
301
306
  while ((ite.index & 1) !== 0) {
302
307
  const l = await this.tree.get(ite.leftChild(), false)
303
308
  if (l) {
304
- if (l.size === bytes) return [ite.rightSpan() + 2, 0]
305
- if (l.size > bytes) continue
306
- bytes -= l.size
309
+ let size = l.size
310
+ if (this.padding > 0) size -= this.padding * ite.countLeaves()
311
+
312
+ if (size === bytes) return [ite.rightSpan() + 2, 0]
313
+ if (size > bytes) continue
314
+ bytes -= size
307
315
  ite.sibling()
308
316
  } else {
309
317
  ite.parent()
@@ -355,8 +363,8 @@ module.exports = class MerkleTree {
355
363
  return new MerkleTreeBatch(this)
356
364
  }
357
365
 
358
- seek (bytes) {
359
- return new ByteSeeker(this, bytes)
366
+ seek (bytes, padding) {
367
+ return new ByteSeeker(this, bytes, padding)
360
368
  }
361
369
 
362
370
  hash () {
@@ -433,17 +441,23 @@ module.exports = class MerkleTree {
433
441
  // TODO: write neighbors together etc etc
434
442
  // TODO: bench loading a full disk page and copy to that instead
435
443
  return new Promise((resolve, reject) => {
436
- const slab = Buffer.allocUnsafe(40 * this.flushing.size)
444
+ const slab = b4a.allocUnsafe(40 * this.flushing.size)
437
445
 
438
446
  let error = null
439
447
  let missing = this.flushing.size + 1
440
448
  let offset = 0
441
449
 
442
450
  for (const node of this.flushing.values()) {
443
- const b = slab.slice(offset, offset += 40)
444
- uint64le.encode(node.size, b, 0)
445
- node.hash.copy(b, 8)
446
- this.storage.write(node.index * 40, b, done)
451
+ const state = {
452
+ start: 0,
453
+ end: 40,
454
+ buffer: slab.subarray(offset, offset += 40)
455
+ }
456
+
457
+ c.uint64.encode(state, node.size)
458
+ c.raw.encode(state, node.hash)
459
+
460
+ this.storage.write(node.index * 40, state.buffer, done)
447
461
  }
448
462
 
449
463
  done(null)
@@ -1038,10 +1052,10 @@ function getStoredNode (storage, index, error) {
1038
1052
  return
1039
1053
  }
1040
1054
 
1041
- const hash = data.slice(8)
1042
- const size = uint64le.decode(data, 0)
1055
+ const hash = data.subarray(8)
1056
+ const size = c.decode(c.uint64, data)
1043
1057
 
1044
- if (size === 0 && Buffer.compare(hash, BLANK_HASH) === 0) {
1058
+ if (size === 0 && b4a.compare(hash, BLANK_HASH) === 0) {
1045
1059
  if (error) reject(new Error('Could not load node: ' + index))
1046
1060
  else resolve(null)
1047
1061
  return
@@ -1088,9 +1102,9 @@ function log2 (n) {
1088
1102
  }
1089
1103
 
1090
1104
  function signable (hash, length, fork) {
1091
- const buf = Buffer.alloc(48)
1092
- hash.copy(buf)
1093
- uint64le.encode(length, buf, 32)
1094
- uint64le.encode(fork, buf, 40)
1095
- return buf
1105
+ const state = { start: 0, end: 48, buffer: b4a.alloc(48) }
1106
+ c.raw.encode(state, hash)
1107
+ c.uint64.encode(state, length)
1108
+ c.uint64.encode(state, fork)
1109
+ return state.buffer
1096
1110
  }
package/lib/oplog.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const cenc = require('compact-encoding')
2
+ const b4a = require('b4a')
2
3
  const crc32 = require('crc32-universal')
3
4
 
4
5
  module.exports = class Oplog {
@@ -133,7 +134,7 @@ module.exports = class Oplog {
133
134
  return new Promise((resolve, reject) => {
134
135
  this.storage.open(err => {
135
136
  if (err && err.code !== 'ENOENT') return reject(err)
136
- if (err) return resolve(Buffer.alloc(0))
137
+ if (err) return resolve(b4a.alloc(0))
137
138
  this.storage.stat((err, stat) => {
138
139
  if (err && err.code !== 'ENOENT') return reject(err)
139
140
  this.storage.read(0, stat.size, (err, buf) => {
@@ -151,7 +152,7 @@ module.exports = class Oplog {
151
152
  const bit = (this._headers[i] + 1) & 1
152
153
 
153
154
  this.headerEncoding.preencode(state, header)
154
- state.buffer = Buffer.allocUnsafe(state.end)
155
+ state.buffer = b4a.allocUnsafe(state.end)
155
156
  this.headerEncoding.encode(state, header)
156
157
  this._addHeader(state, state.end - 8, bit, 0)
157
158
 
@@ -187,7 +188,7 @@ module.exports = class Oplog {
187
188
  this.entryEncoding.preencode(state, batch[i])
188
189
  }
189
190
 
190
- state.buffer = Buffer.allocUnsafe(state.end)
191
+ state.buffer = b4a.allocUnsafe(state.end)
191
192
 
192
193
  for (let i = 0; i < batch.length; i++) {
193
194
  const start = state.start += 8 // space for header