hypercore 10.0.0-alpha.8 → 10.1.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/lib/bitfield.js CHANGED
@@ -1,6 +1,6 @@
1
- // TODO: needs massive improvements obvs
2
-
3
1
  const BigSparseArray = require('big-sparse-array')
2
+ const b4a = require('b4a')
3
+ const bits = require('bits-to-bytes')
4
4
 
5
5
  class FixedBitfield {
6
6
  constructor (index, bitfield) {
@@ -10,27 +10,26 @@ class FixedBitfield {
10
10
  }
11
11
 
12
12
  get (index) {
13
- const j = index & 31
14
- const i = (index - j) / 32
15
-
16
- return i < this.bitfield.length && (this.bitfield[i] & (1 << j)) !== 0
13
+ return bits.get(this.bitfield, index)
17
14
  }
18
15
 
19
16
  set (index, val) {
20
- const j = index & 31
21
- const i = (index - j) / 32
22
- const v = this.bitfield[i]
23
-
24
- if (val === ((v & (1 << j)) !== 0)) return false
17
+ return bits.set(this.bitfield, index, val)
18
+ }
25
19
 
26
- const u = val
27
- ? v | (1 << j)
28
- : v ^ (1 << j)
20
+ setRange (start, length, val) {
21
+ // Using fill instead of setRange is ~2 orders of magnitude faster, but does
22
+ // have the downside of not being able to tell if any bits actually changed.
23
+ bits.fill(this.bitfield, val, start, start + length)
24
+ return true
25
+ }
29
26
 
30
- if (u === v) return false
27
+ firstSet (position) {
28
+ return bits.indexOf(this.bitfield, true, position)
29
+ }
31
30
 
32
- this.bitfield[i] = u
33
- return true
31
+ lastSet (position) {
32
+ return bits.lastIndexOf(this.bitfield, true, position)
34
33
  }
35
34
  }
36
35
 
@@ -40,9 +39,14 @@ module.exports = class Bitfield {
40
39
  this.pages = new BigSparseArray()
41
40
  this.unflushed = []
42
41
  this.storage = storage
43
-
44
- const all = (buf && buf.byteLength >= 4)
45
- ? new Uint32Array(buf.buffer, buf.byteOffset, Math.floor(buf.byteLength / 4))
42
+ this.resumed = !!(buf && buf.byteLength >= 4)
43
+
44
+ const all = this.resumed
45
+ ? new Uint32Array(
46
+ buf.buffer,
47
+ buf.byteOffset,
48
+ Math.floor(buf.byteLength / 4)
49
+ )
46
50
  : new Uint32Array(1024)
47
51
 
48
52
  for (let i = 0; i < all.length; i += 1024) {
@@ -53,47 +57,106 @@ module.exports = class Bitfield {
53
57
  }
54
58
 
55
59
  get (index) {
56
- const j = index & 32767
57
- const i = (index - j) / 32768
60
+ const j = index & (this.pageSize - 1)
61
+ const i = (index - j) / this.pageSize
62
+
58
63
  const p = this.pages.get(i)
59
64
 
60
65
  return p ? p.get(j) : false
61
66
  }
62
67
 
63
68
  set (index, val) {
64
- const j = index & 32767
65
- const i = (index - j) / 32768
69
+ const j = index & (this.pageSize - 1)
70
+ const i = (index - j) / this.pageSize
66
71
 
67
72
  let p = this.pages.get(i)
68
73
 
69
- if (!p) {
70
- if (!val) return
74
+ if (!p && val) {
71
75
  p = this.pages.set(i, new FixedBitfield(i, new Uint32Array(1024)))
72
76
  }
73
77
 
74
- if (!p.set(j, val) || p.dirty) return
75
-
76
- p.dirty = true
77
- this.unflushed.push(p)
78
+ if (p && p.set(j, val) && !p.dirty) {
79
+ p.dirty = true
80
+ this.unflushed.push(p)
81
+ }
78
82
  }
79
83
 
80
84
  setRange (start, length, val) {
81
- for (let i = 0; i < length; i++) {
82
- this.set(start + i, val)
85
+ let j = start & (this.pageSize - 1)
86
+ let i = (start - j) / this.pageSize
87
+
88
+ while (length > 0) {
89
+ let p = this.pages.get(i)
90
+
91
+ if (!p && val) {
92
+ p = this.pages.set(i, new FixedBitfield(i, new Uint32Array(1024)))
93
+ }
94
+
95
+ const end = Math.min(j + length, this.pageSize)
96
+ const range = end - j
97
+
98
+ if (p && p.setRange(j, range, val) && !p.dirty) {
99
+ p.dirty = true
100
+ this.unflushed.push(p)
101
+ }
102
+
103
+ j = 0
104
+ i++
105
+ length -= range
83
106
  }
84
107
  }
85
108
 
86
- // Should prob be removed, when/if we re-add compression
87
- page (i) {
88
- const p = this.pages.get(i)
89
- return p ? p.bitfield : new Uint32Array(1024)
109
+ firstSet (position) {
110
+ let j = position & (this.pageSize - 1)
111
+ let i = (position - j) / this.pageSize
112
+
113
+ while (i < this.pages.factor) {
114
+ const p = this.pages.get(i)
115
+
116
+ if (p) {
117
+ const index = p.firstSet(j)
118
+
119
+ if (index !== -1) {
120
+ return i * this.pageSize + index
121
+ }
122
+ }
123
+
124
+ j = 0
125
+ i++
126
+ }
127
+
128
+ return -1
129
+ }
130
+
131
+ lastSet (position) {
132
+ let j = position & (this.pageSize - 1)
133
+ let i = (position - j) / this.pageSize
134
+
135
+ while (i >= 0) {
136
+ const p = this.pages.get(i)
137
+
138
+ if (p) {
139
+ const index = p.lastSet(j)
140
+
141
+ if (index !== -1) {
142
+ return i * this.pageSize + index
143
+ }
144
+ }
145
+
146
+ j = this.pageSize - 1
147
+ i--
148
+ }
149
+
150
+ return -1
90
151
  }
91
152
 
92
153
  clear () {
93
154
  return new Promise((resolve, reject) => {
94
- this.storage.del(0, Infinity, (err) => {
95
- if (err) reject(err)
96
- else resolve()
155
+ this.storage.truncate(0, (err) => {
156
+ if (err) return reject(err)
157
+ this.pages = new BigSparseArray()
158
+ this.unflushed = []
159
+ resolve()
97
160
  })
98
161
  })
99
162
  }
@@ -116,9 +179,14 @@ module.exports = class Bitfield {
116
179
  let error = null
117
180
 
118
181
  for (const page of this.unflushed) {
119
- const b = Buffer.from(page.bitfield.buffer, page.bitfield.byteOffset, page.bitfield.byteLength)
182
+ const buf = b4a.from(
183
+ page.bitfield.buffer,
184
+ page.bitfield.byteOffset,
185
+ page.bitfield.byteLength
186
+ )
187
+
120
188
  page.dirty = false
121
- this.storage.write(page.index * 4096, b, done)
189
+ this.storage.write(page.index * 4096, buf, done)
122
190
  }
123
191
 
124
192
  function done (err) {
@@ -147,7 +215,7 @@ module.exports = class Bitfield {
147
215
  }
148
216
 
149
217
  function ensureSize (uint32, size) {
150
- if (uint32.length === size) return uint32
218
+ if (uint32.byteLength === size) return uint32
151
219
  const a = new Uint32Array(1024)
152
220
  a.set(uint32, 0)
153
221
  return a
@@ -1,11 +1,12 @@
1
1
  const sodium = require('sodium-universal')
2
2
  const c = require('compact-encoding')
3
+ const b4a = require('b4a')
3
4
 
4
- const nonce = Buffer.alloc(sodium.crypto_stream_NONCEBYTES)
5
+ const nonce = b4a.alloc(sodium.crypto_stream_NONCEBYTES)
5
6
 
6
7
  module.exports = class BlockEncryption {
7
8
  constructor (encryptionKey, hypercoreKey) {
8
- const subKeys = Buffer.alloc(2 * sodium.crypto_stream_KEYBYTES)
9
+ const subKeys = b4a.alloc(2 * sodium.crypto_stream_KEYBYTES)
9
10
 
10
11
  this.key = encryptionKey
11
12
  this.blockKey = subKeys.subarray(0, sodium.crypto_stream_KEYBYTES)
@@ -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,15 +17,18 @@ 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
- clear () {
23
+ clear (offset = 0, length = -1) {
22
24
  return new Promise((resolve, reject) => {
23
- this.storage.del(0, Infinity, (err) => {
25
+ if (length === -1) this.storage.truncate(offset, done)
26
+ else this.storage.del(offset, length, done)
27
+
28
+ function done (err) {
24
29
  if (err) reject(err)
25
30
  else resolve()
26
- })
31
+ }
27
32
  })
28
33
  }
29
34
 
@@ -49,7 +54,7 @@ module.exports = class BlockStore {
49
54
  return new Promise((resolve, reject) => {
50
55
  this.storage.write(offset, data, (err) => {
51
56
  if (err) reject(err)
52
- else resolve()
57
+ else resolve(offset + data.byteLength)
53
58
  })
54
59
  })
55
60
  }
package/lib/caps.js ADDED
@@ -0,0 +1,32 @@
1
+ const crypto = require('hypercore-crypto')
2
+ const sodium = require('sodium-universal')
3
+ const b4a = require('b4a')
4
+ const c = require('compact-encoding')
5
+
6
+ // TODO: rename this to "crypto" and move everything hashing related etc in here
7
+ // Also lets move the tree stuff from hypercore-crypto here
8
+
9
+ const [TREE, REPLICATE_INITIATOR, REPLICATE_RESPONDER] = crypto.namespace('hypercore', 3)
10
+
11
+ exports.replicate = function (isInitiator, key, handshakeHash) {
12
+ const out = b4a.allocUnsafe(32)
13
+ sodium.crypto_generichash_batch(out, [isInitiator ? REPLICATE_INITIATOR : REPLICATE_RESPONDER, key], handshakeHash)
14
+ return out
15
+ }
16
+
17
+ exports.treeSignable = function (hash, length, fork) {
18
+ const state = { start: 0, end: 80, buffer: b4a.allocUnsafe(80) }
19
+ c.raw.encode(state, TREE)
20
+ c.raw.encode(state, hash)
21
+ c.uint64.encode(state, length)
22
+ c.uint64.encode(state, fork)
23
+ return state.buffer
24
+ }
25
+
26
+ exports.treeSignableLegacy = function (hash, length, fork) {
27
+ const state = { start: 0, end: 48, buffer: b4a.allocUnsafe(48) }
28
+ c.raw.encode(state, hash)
29
+ c.uint64.encode(state, length)
30
+ c.uint64.encode(state, fork)
31
+ return state.buffer
32
+ }