hypercore-storage 0.0.22 → 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 +18 -2
- package/lib/memory-overlay.js +285 -0
- package/lib/tip-list.js +93 -0
- package/package.json +2 -1
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,6 +432,12 @@ class HypercoreStorage {
|
|
|
426
432
|
return this.dbSnapshot !== null
|
|
427
433
|
}
|
|
428
434
|
|
|
435
|
+
dependencyLength () {
|
|
436
|
+
return this.dependencies.length
|
|
437
|
+
? this.dependencies[this.dependencies.length - 1].length
|
|
438
|
+
: -1
|
|
439
|
+
}
|
|
440
|
+
|
|
429
441
|
async openBatch (name) {
|
|
430
442
|
const existing = await this.db.get(encodeBatch(this.corePointer, CORE.BATCHES, name))
|
|
431
443
|
if (!existing) return null
|
|
@@ -469,6 +481,10 @@ class HypercoreStorage {
|
|
|
469
481
|
}
|
|
470
482
|
}
|
|
471
483
|
|
|
484
|
+
createMemoryOverlay () {
|
|
485
|
+
return new MemoryOverlay(this)
|
|
486
|
+
}
|
|
487
|
+
|
|
472
488
|
snapshot () {
|
|
473
489
|
assert(this.closed === false)
|
|
474
490
|
return new HypercoreStorage(this.root, this.discoveryKey, this.corePointer, this.dataPointer, this.db.snapshot())
|
|
@@ -519,7 +535,7 @@ class HypercoreStorage {
|
|
|
519
535
|
return s
|
|
520
536
|
}
|
|
521
537
|
|
|
522
|
-
async
|
|
538
|
+
async peekLastTreeNode () {
|
|
523
539
|
assert(this.closed === false)
|
|
524
540
|
|
|
525
541
|
const last = await this.db.peek(encodeIndexRange(this.dataPointer, DATA.TREE, this.dbSnapshot, { reverse: true }))
|
|
@@ -527,7 +543,7 @@ class HypercoreStorage {
|
|
|
527
543
|
return c.decode(m.TreeNode, last.value)
|
|
528
544
|
}
|
|
529
545
|
|
|
530
|
-
async
|
|
546
|
+
async peekLastBitfieldPage () {
|
|
531
547
|
assert(this.closed === false)
|
|
532
548
|
|
|
533
549
|
const last = await this.db.peek(encodeIndexRange(this.dataPointer, DATA.BITFIELD, this.dbSnapshot, { reverse: true }))
|
|
@@ -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
|
+
}
|
package/lib/tip-list.js
ADDED
|
@@ -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.
|
|
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",
|