hypercore-storage 0.0.21 → 0.0.23

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
@@ -5,8 +5,10 @@ const RW = require('read-write-mutexify')
5
5
  const b4a = require('b4a')
6
6
  const flat = require('flat-tree')
7
7
  const assert = require('nanoassert')
8
+
8
9
  const m = require('./lib/messages')
9
10
  const DependencyStream = require('./lib/dependency-stream')
11
+ const MemoryOverlay = require('./lib/memory-overlay')
10
12
 
11
13
  const INF = b4a.from([0xff])
12
14
 
@@ -181,6 +183,10 @@ class ReadBatch {
181
183
  return this._get(encodeCoreIndex(this.storage.corePointer, CORE.ENCRYPTION_KEY), null)
182
184
  }
183
185
 
186
+ getDataDependency () {
187
+ return this._get(encodeDataIndex(this.storage.dataPointer, DATA.DEPENDENCY), m.DataDependency)
188
+ }
189
+
184
190
  getDataInfo (info) {
185
191
  return this._get(encodeDataIndex(this.storage.dataPointer, DATA.INFO), m.DataInfo)
186
192
  }
@@ -426,23 +432,32 @@ class HypercoreStorage {
426
432
  return this.dbSnapshot !== null
427
433
  }
428
434
 
429
- async registerBatch (name, length, overwrite) {
430
- // todo: make sure opened
435
+ dependencyLength () {
436
+ return this.dependencies.length
437
+ ? this.dependencies[this.dependencies.length - 1].length
438
+ : -1
439
+ }
440
+
441
+ async openBatch (name) {
431
442
  const existing = await this.db.get(encodeBatch(this.corePointer, CORE.BATCHES, name))
432
- const storage = new HypercoreStorage(this.root, this.discoveryKey, this.corePointer, this.dataPointer, null)
443
+ if (!existing) return null
433
444
 
434
- if (existing && !overwrite) {
435
- const dataPointer = c.decode(m.DataPointer, existing)
436
- storage.dataPointer = dataPointer
437
- storage.dependencies = await addDependencies(this.db, storage.dataPointer, length)
438
- return storage
439
- }
445
+ const storage = new HypercoreStorage(this.root, this.discoveryKey, this.corePointer, this.dataPointer, this.dbSnapshot)
446
+ const dataPointer = c.decode(m.DataPointer, existing)
440
447
 
448
+ storage.dataPointer = dataPointer
449
+ storage.dependencies = await addDependencies(this.db, storage.dataPointer, -1)
450
+
451
+ return storage
452
+ }
453
+
454
+ async registerBatch (name, head) {
441
455
  await this.mutex.write.lock()
442
456
 
457
+ const storage = new HypercoreStorage(this.root, this.discoveryKey, this.corePointer, this.dataPointer, null)
458
+
443
459
  try {
444
460
  const info = await getStorageInfo(this.db)
445
-
446
461
  const write = this.db.write()
447
462
 
448
463
  storage.dataPointer = info.free++
@@ -453,18 +468,23 @@ class HypercoreStorage {
453
468
 
454
469
  initialiseCoreData(batch)
455
470
 
456
- batch.setDataDependency({ data: this.dataPointer, length })
471
+ batch.setDataDependency({ data: this.dataPointer, length: head.length })
457
472
  batch.setBatchPointer(name, storage.dataPointer)
473
+ if (head.rootHash) batch.setCoreHead(head) // if no root hash its the empty core - no head yet
458
474
 
459
475
  await write.flush()
460
476
 
461
- storage.dependencies = await addDependencies(this.db, storage.dataPointer, length)
477
+ storage.dependencies = await addDependencies(this.db, storage.dataPointer, head.length)
462
478
  return storage
463
479
  } finally {
464
480
  this.mutex.write.unlock()
465
481
  }
466
482
  }
467
483
 
484
+ createMemoryOverlay () {
485
+ return new MemoryOverlay(this)
486
+ }
487
+
468
488
  snapshot () {
469
489
  assert(this.closed === false)
470
490
  return new HypercoreStorage(this.root, this.discoveryKey, this.corePointer, this.dataPointer, this.db.snapshot())
@@ -515,7 +535,7 @@ class HypercoreStorage {
515
535
  return s
516
536
  }
517
537
 
518
- async peakLastTreeNode () {
538
+ async peekLastTreeNode () {
519
539
  assert(this.closed === false)
520
540
 
521
541
  const last = await this.db.peek(encodeIndexRange(this.dataPointer, DATA.TREE, this.dbSnapshot, { reverse: true }))
@@ -523,7 +543,7 @@ class HypercoreStorage {
523
543
  return c.decode(m.TreeNode, last.value)
524
544
  }
525
545
 
526
- async peakLastBitfieldPage () {
546
+ async peekLastBitfieldPage () {
527
547
  assert(this.closed === false)
528
548
 
529
549
  const last = await this.db.peek(encodeIndexRange(this.dataPointer, DATA.BITFIELD, this.dbSnapshot, { reverse: true }))
@@ -709,7 +729,7 @@ async function addDependencies (db, dataPointer, treeLength) {
709
729
  let dep = await db.get(encodeDataIndex(dataPointer, DATA.DEPENDENCY))
710
730
  while (dep) {
711
731
  const { data, length } = c.decode(m.DataDependency, dep)
712
- if (length <= treeLength) dependencies.push({ data, length })
732
+ if (treeLength === -1 || length <= treeLength) dependencies.push({ data, length })
713
733
 
714
734
  dep = await db.get(encodeDataIndex(data, DATA.DEPENDENCY))
715
735
  }
@@ -0,0 +1,285 @@
1
+ const { ASSERTION } = require('hypercore-errors')
2
+
3
+ const TipList = require('./tip-list')
4
+
5
+ class MemoryOverlay {
6
+ constructor (storage) {
7
+ this.storage = storage
8
+ this.head = null
9
+ this.auth = null
10
+ this.localKeyPair = null
11
+ this.encryptionKey = null
12
+ this.dataDependency = null
13
+ this.dataInfo = null
14
+ this.userData = null
15
+ this.blocks = null
16
+ this.treeNodes = null
17
+ this.bitfields = null
18
+
19
+ this.snapshotted = false
20
+ }
21
+
22
+ async registerBatch (name, length, overwrite) {
23
+ todo()
24
+ }
25
+
26
+ snapshot () {
27
+ todo()
28
+ }
29
+
30
+ get dependencies () {
31
+ return this.storage.dependencies
32
+ }
33
+
34
+ dependencyLength () {
35
+ if (this.dataDependency) return this.dataDependency.length
36
+ return this.storage.dependencyLength()
37
+ }
38
+
39
+ createReadBatch () {
40
+ return new MemoryOverlayReadBatch(this, this.storage.createReadBatch())
41
+ }
42
+
43
+ createWriteBatch () {
44
+ return new MemoryOverlayWriteBatch(this)
45
+ }
46
+
47
+ createBlockStream () {
48
+ todo()
49
+ }
50
+
51
+ createUserDataStream () {
52
+ todo()
53
+ }
54
+
55
+ createTreeNodeStream () {
56
+ todo()
57
+ }
58
+
59
+ createBitfieldPageStream () {
60
+ todo()
61
+ }
62
+
63
+ async peekLastTreeNode () {
64
+ todo()
65
+ }
66
+
67
+ async peekLastBitfieldPage () {
68
+ const mem = this.bitfields !== null ? this.bitfields.get(this.bitfields.length() - 1) : { valid: false }
69
+
70
+ const page = mem.valid ? mem.value : null
71
+ const index = page ? this.bitfields.length() - 1 : -1
72
+
73
+ const disk = await this.storage.peekLastBitfieldPage()
74
+
75
+ return (page && (!disk || index > disk.index)) ? { index, page } : disk
76
+ }
77
+
78
+ close () {
79
+ return Promise.resolve()
80
+ }
81
+
82
+ merge (overlay) {
83
+ if (overlay.head !== null) this.head = overlay.head
84
+ if (overlay.auth !== null) this.auth = overlay.auth
85
+ if (overlay.localKeyPair !== null) this.localKeyPair = overlay.localKeyPair
86
+ if (overlay.encryptionKey !== null) this.encryptionKey = overlay.encryptionKey
87
+ if (overlay.dataDependency !== null) this.dataDependency = overlay.dataDependency
88
+ if (overlay.dataInfo !== null) this.dataInfo = overlay.dataInfo
89
+ if (overlay.userData !== null) this.userData = mergeMap(this.userData, overlay.userData)
90
+ if (overlay.blocks !== null) this.blocks = mergeTip(this.blocks, overlay.blocks)
91
+ if (overlay.treeNodes !== null) this.treeNodes = mergeMap(this.treeNodes, overlay.treeNodes)
92
+ if (overlay.bitfields !== null) this.bitfields = mergeTip(this.bitfields, overlay.bitfields)
93
+ }
94
+ }
95
+
96
+ module.exports = MemoryOverlay
97
+
98
+ class MemoryOverlayReadBatch {
99
+ constructor (overlay, read) {
100
+ this.read = read
101
+ this.overlay = overlay
102
+ }
103
+
104
+ async getCoreHead () {
105
+ return this.overlay.head !== null ? this.overlay.head : this.read.getCoreHead()
106
+ }
107
+
108
+ async getCoreAuth () {
109
+ return this.overlay.auth !== null ? this.overlay.auth : this.read.getCoreAuth()
110
+ }
111
+
112
+ async getDataDependency () {
113
+ return this.overlay.dataDependency !== null ? this.overlay.dataDependency : this.read.getDataDependency()
114
+ }
115
+
116
+ async getLocalKeyPair () {
117
+ return this.overlay.localKeyPair !== null ? this.overlay.localKeyPair : this.read.getLocalKeyPair()
118
+ }
119
+
120
+ async getEncryptionKey () {
121
+ return this.overlay.encryptionKey !== null ? this.overlay.encryptionKey : this.read.getEncryptionKey()
122
+ }
123
+
124
+ async getDataInfo () {
125
+ return this.overlay.dataInfo !== null ? this.overlay.dataInfo : this.read.getDataInfo()
126
+ }
127
+
128
+ async getUserData (key) {
129
+ return this.overlay.userData !== null && this.overlay.userData.has(key)
130
+ ? this.overlay.userData.get(key)
131
+ : this.read.getUserData(key)
132
+ }
133
+
134
+ async hasBlock (index) {
135
+ if (this.overlay.blocks !== null && index >= this.overlay.blocks.offset) {
136
+ const blk = this.overlay.blocks.get(index)
137
+ if (blk.valid) return true
138
+ }
139
+ return this.read.hasBlock(index)
140
+ }
141
+
142
+ async getBlock (index, error) {
143
+ if (this.overlay.blocks !== null && index >= this.overlay.blocks.offset) {
144
+ const blk = this.overlay.blocks.get(index)
145
+ if (blk.valid) return blk.value
146
+ }
147
+ return this.read.getBlock(index, error)
148
+ }
149
+
150
+ async hasTreeNode (index) {
151
+ return this.overlay.treeNodes !== null
152
+ ? this.overlay.treeNodes.has(index)
153
+ : this.read.hasTreeNode(index)
154
+ }
155
+
156
+ async getTreeNode (index, error) {
157
+ return this.overlay.treeNodes !== null && this.overlay.treeNodes.has(index)
158
+ ? this.overlay.treeNodes.get(index)
159
+ : this.read.getTreeNode(index, error)
160
+ }
161
+
162
+ async getBitfieldPage (index) {
163
+ if (this.overlay.bitfields !== null && index >= this.overlay.bitfields.offset) {
164
+ const page = this.overlay.bitfields.get(index)
165
+ if (page.valid) return page.value
166
+ }
167
+ return this.read.getBitfieldPage(index)
168
+ }
169
+
170
+ destroy () {
171
+ this.read.destroy()
172
+ }
173
+
174
+ flush () {
175
+ return this.read.flush()
176
+ }
177
+
178
+ tryFlush () {
179
+ this.read.tryFlush()
180
+ }
181
+ }
182
+
183
+ class MemoryOverlayWriteBatch {
184
+ constructor (storage) {
185
+ this.storage = storage
186
+ this.overlay = new MemoryOverlay()
187
+ }
188
+
189
+ setCoreHead (head) {
190
+ this.overlay.head = head
191
+ }
192
+
193
+ setCoreAuth (auth) {
194
+ this.overlay.auth = auth
195
+ }
196
+
197
+ setBatchPointer (name, pointer) {
198
+ todo()
199
+ }
200
+
201
+ setDataDependency (dependency) {
202
+ this.overlay.dataDependency = dependency
203
+ }
204
+
205
+ setLocalKeyPair (keyPair) {
206
+ this.overlay.localKeyPair = keyPair
207
+ }
208
+
209
+ setEncryptionKey (encryptionKey) {
210
+ this.overlay.encryptionKey = encryptionKey
211
+ }
212
+
213
+ setDataInfo (info) {
214
+ this.overlay.dataInfo = info
215
+ }
216
+
217
+ setUserData (key, value) {
218
+ if (this.overlay.userData === null) this.overlay.userData = new Map()
219
+ this.overlay.userData.set(key, value)
220
+ }
221
+
222
+ putBlock (index, data) {
223
+ if (this.overlay.blocks === null) this.overlay.blocks = new TipList()
224
+ this.overlay.blocks.put(index, data)
225
+ }
226
+
227
+ deleteBlock (index) {
228
+ todo()
229
+ }
230
+
231
+ deleteBlockRange (start, end) {
232
+ if (this.overlay.blocks === null) this.overlay.blocks = new TipList()
233
+ this.overlay.blocks.delete(start, end)
234
+ }
235
+
236
+ putTreeNode (node) {
237
+ if (this.overlay.treeNodes === null) this.overlay.treeNodes = new Map()
238
+ this.overlay.treeNodes.set(node.index, node)
239
+ }
240
+
241
+ deleteTreeNode (index) {
242
+ todo()
243
+ }
244
+
245
+ deleteTreeNodeRange (start, end) {
246
+ todo()
247
+ }
248
+
249
+ putBitfieldPage (index, page) {
250
+ if (this.overlay.bitfields === null) this.overlay.bitfields = new TipList()
251
+ this.overlay.bitfields.put(index, page)
252
+ }
253
+
254
+ deleteBitfieldPage (index) {
255
+ todo()
256
+ }
257
+
258
+ deleteBitfieldPageRange (start, end) {
259
+ if (this.overlay.bitfields === null) this.overlay.bitfields = new TipList()
260
+ this.overlay.bitfields.delete(start, end)
261
+ }
262
+
263
+ destroy () {}
264
+
265
+ flush () {
266
+ this.storage.merge(this.overlay)
267
+ return Promise.resolve()
268
+ }
269
+ }
270
+
271
+ function mergeMap (a, b) {
272
+ if (a === null) return b
273
+ for (const [key, value] of b) a.set(key, value)
274
+ return a
275
+ }
276
+
277
+ function mergeTip (a, b) {
278
+ if (a === null) return b
279
+ a.merge(b)
280
+ return a
281
+ }
282
+
283
+ function todo () {
284
+ throw ASSERTION('Not supported yet, but will be')
285
+ }
@@ -0,0 +1,93 @@
1
+ const { ASSERTION } = require('hypercore-errors')
2
+
3
+ module.exports = class TipList {
4
+ constructor () {
5
+ this.offset = 0
6
+ this.removed = 0
7
+ this.data = []
8
+ }
9
+
10
+ length () {
11
+ return this.offset + this.data.length
12
+ }
13
+
14
+ end () {
15
+ if (this.removed) return this.removed
16
+ return this.offset + this.data.length
17
+ }
18
+
19
+ delete (start, end) {
20
+ if (end !== -1) {
21
+ if (end < this.length() || (this.length() < start && this.length() !== 0)) {
22
+ throw ASSERTION('Invalid delete on tip list')
23
+ }
24
+ }
25
+
26
+ if (this.end() === 0) this.offset = start
27
+ if (this.removed < end || end === -1) this.removed = end
28
+
29
+ if (start < this.offset) {
30
+ this.offset = start
31
+ this.data = [] // clear everything
32
+ return
33
+ }
34
+
35
+ while (this.length() > start) this.data.pop()
36
+ }
37
+
38
+ put (index, value) {
39
+ if (this.end() === 0) {
40
+ this.offset = index
41
+ this.data.push(value)
42
+ return
43
+ }
44
+
45
+ if (!this.removed && this.end() === index) {
46
+ this.data.push(value)
47
+ return
48
+ }
49
+
50
+ throw ASSERTION('Invalid put on tip list')
51
+ }
52
+
53
+ get (index) {
54
+ if (index < this.offset) {
55
+ return {
56
+ valid: false,
57
+ value: null
58
+ }
59
+ }
60
+
61
+ if (index - this.offset < this.data.length) {
62
+ return {
63
+ valid: true,
64
+ value: this.data[index - this.offset]
65
+ }
66
+ }
67
+
68
+ return {
69
+ valid: index < this.end(),
70
+ value: null
71
+ }
72
+ }
73
+
74
+ * [Symbol.iterator] () {
75
+ for (let i = 0; i < this.data.length; i++) {
76
+ yield [i + this.offset, this.data[i]]
77
+ }
78
+ }
79
+
80
+ merge (tip) {
81
+ const invalidDeletion = tip.removed && tip.end() !== -1 && tip.end() < this.end()
82
+ const invalidTip = (tip.removed !== -1 && tip.end() < this.offset) || this.end() < tip.offset
83
+
84
+ if (invalidTip || invalidDeletion) throw ASSERTION('Cannot merge tip list')
85
+
86
+ while (this.end() !== tip.offset && tip.offset >= this.offset && tip.end() >= this.end()) this.data.pop()
87
+ while (tip.removed && this.end() > tip.length() && this.data.length) this.data.pop()
88
+
89
+ for (const data of tip.data) this.data.push(data)
90
+
91
+ return this
92
+ }
93
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hypercore-storage",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "main": "index.js",
5
5
  "files": [
6
6
  "index.js",
@@ -16,6 +16,7 @@
16
16
  "b4a": "^1.6.6",
17
17
  "compact-encoding": "^2.15.0",
18
18
  "flat-tree": "^1.10.0",
19
+ "hypercore-errors": "^1.3.0",
19
20
  "index-encoder": "^3.0.1",
20
21
  "nanoassert": "^2.0.0",
21
22
  "read-write-mutexify": "^2.1.0",