hyperbee2 2.2.0 → 2.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/index.js +63 -59
- package/lib/cache.js +21 -1
- package/lib/changes.js +17 -19
- package/lib/compression.js +48 -11
- package/lib/context.js +40 -15
- package/lib/diff.js +8 -6
- package/lib/ranges.js +7 -16
- package/lib/session-config.js +23 -0
- package/lib/tree.js +39 -28
- package/lib/write.js +82 -66
- package/package.json +2 -1
package/lib/context.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
const b4a = require('b4a')
|
|
2
2
|
const ScopeLock = require('scope-lock')
|
|
3
|
+
const { BLOCK_NOT_AVAILABLE } = require('hypercore-errors')
|
|
4
|
+
const { TreeNodePointer } = require('./tree.js')
|
|
3
5
|
const { decodeBlock } = require('./encoding.js')
|
|
4
6
|
|
|
5
7
|
class CoreContext {
|
|
6
|
-
constructor(store, local, core, encryption, t, lock = new ScopeLock(), other = new Map()) {
|
|
8
|
+
constructor(store, local, cache, core, encryption, t, lock = new ScopeLock(), other = new Map()) {
|
|
7
9
|
this.store = store
|
|
8
10
|
this.local = local
|
|
11
|
+
this.cache = cache
|
|
9
12
|
this.core = core
|
|
10
13
|
this.t = t
|
|
11
14
|
this.minKeys = t - 1
|
|
@@ -25,20 +28,26 @@ class CoreContext {
|
|
|
25
28
|
// return '[CoreContext]'
|
|
26
29
|
// }
|
|
27
30
|
|
|
28
|
-
async update(
|
|
31
|
+
async update(config) {
|
|
29
32
|
await this.core.ready()
|
|
30
33
|
|
|
31
34
|
if (this.length === this.core.length || this.core.length === 0) return
|
|
32
35
|
|
|
33
36
|
const length = this.core.length
|
|
34
37
|
const seq = length - 1
|
|
35
|
-
const buffer = await this.core.get(seq,
|
|
38
|
+
const buffer = await this.core.get(seq, config)
|
|
36
39
|
const block = decodeBlock(buffer, seq)
|
|
40
|
+
|
|
41
|
+
await this._inflateCheckpoint(length, block)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async _inflateCheckpoint(length, block) {
|
|
37
45
|
const checkpoint = block.checkpoint
|
|
38
46
|
|
|
39
47
|
if (checkpoint > 0) {
|
|
40
|
-
const
|
|
41
|
-
const
|
|
48
|
+
const seq = checkpoint - 1
|
|
49
|
+
const buffer = await this.core.get(seq)
|
|
50
|
+
const block = decodeBlock(buffer, seq)
|
|
42
51
|
|
|
43
52
|
if (length < this.length) return
|
|
44
53
|
|
|
@@ -52,6 +61,14 @@ class CoreContext {
|
|
|
52
61
|
this.length = length
|
|
53
62
|
}
|
|
54
63
|
|
|
64
|
+
createTreeNode(core, seq, offset, changed, value) {
|
|
65
|
+
const ptr = new TreeNodePointer(this, core, seq, offset, changed, value)
|
|
66
|
+
if (ptr.value || changed) return ptr
|
|
67
|
+
const existing = this.cache.get(ptr)
|
|
68
|
+
if (existing) return existing
|
|
69
|
+
return ptr
|
|
70
|
+
}
|
|
71
|
+
|
|
55
72
|
getCoreOffsetLocal(context, core) {
|
|
56
73
|
if (context === this) return core
|
|
57
74
|
|
|
@@ -74,12 +91,12 @@ class CoreContext {
|
|
|
74
91
|
return this.cores.length
|
|
75
92
|
}
|
|
76
93
|
|
|
77
|
-
async getCoreOffset(context, core,
|
|
78
|
-
if (core !== 0 && core - 1 >= context.cores.length) await context.update(
|
|
94
|
+
async getCoreOffset(context, core, config) {
|
|
95
|
+
if (core !== 0 && core - 1 >= context.cores.length) await context.update(config)
|
|
79
96
|
return this.getCoreOffsetLocal(context, core)
|
|
80
97
|
}
|
|
81
98
|
|
|
82
|
-
async getCoreOffsetByKey(key,
|
|
99
|
+
async getCoreOffsetByKey(key, config) {
|
|
83
100
|
await this.core.ready()
|
|
84
101
|
|
|
85
102
|
if (b4a.equals(key, this.core.key)) return 0
|
|
@@ -91,7 +108,7 @@ class CoreContext {
|
|
|
91
108
|
}
|
|
92
109
|
}
|
|
93
110
|
|
|
94
|
-
await this.update(
|
|
111
|
+
await this.update(config)
|
|
95
112
|
|
|
96
113
|
for (let i = 0; i < this.cores.length; i++) {
|
|
97
114
|
const k = this.cores[i].key
|
|
@@ -125,11 +142,18 @@ class CoreContext {
|
|
|
125
142
|
return this.cores[index - 1].key
|
|
126
143
|
}
|
|
127
144
|
|
|
128
|
-
async getBlock(seq, core,
|
|
129
|
-
if (core !== 0 && core - 1 >= this.cores.length) await this.update(
|
|
145
|
+
async getBlock(seq, core, config) {
|
|
146
|
+
if (core !== 0 && core - 1 >= this.cores.length) await this.update(config)
|
|
147
|
+
|
|
130
148
|
const hc = this.getCore(core)
|
|
131
|
-
const buffer = await hc.get(seq,
|
|
149
|
+
const buffer = await hc.get(seq, config)
|
|
150
|
+
if (buffer === null) throw BLOCK_NOT_AVAILABLE()
|
|
132
151
|
const block = decodeBlock(buffer, seq)
|
|
152
|
+
|
|
153
|
+
if (block.checkpoint > this.checkpoint) {
|
|
154
|
+
await this._inflateCheckpoint(seq + 1, block)
|
|
155
|
+
}
|
|
156
|
+
|
|
133
157
|
return block
|
|
134
158
|
}
|
|
135
159
|
|
|
@@ -148,9 +172,9 @@ class CoreContext {
|
|
|
148
172
|
return ctx
|
|
149
173
|
}
|
|
150
174
|
|
|
151
|
-
async getContext(core,
|
|
175
|
+
async getContext(core, config) {
|
|
152
176
|
if (core === 0) return this
|
|
153
|
-
if (core > this.cores.length) await this.update(
|
|
177
|
+
if (core > this.cores.length) await this.update(config)
|
|
154
178
|
if (core > this.cores.length) throw new Error('Bad core index: ' + core)
|
|
155
179
|
|
|
156
180
|
const hex = b4a.toString(this.cores[core - 1].key, 'hex')
|
|
@@ -171,11 +195,12 @@ class CoreContext {
|
|
|
171
195
|
_createContext(core) {
|
|
172
196
|
const store = this.store
|
|
173
197
|
const local = this.local
|
|
198
|
+
const cache = this.cache
|
|
174
199
|
const encryption = this.encryption
|
|
175
200
|
const t = this.t
|
|
176
201
|
const lock = this.lock
|
|
177
202
|
const other = this.other
|
|
178
|
-
return new CoreContext(store, local, core, encryption, t, lock, other)
|
|
203
|
+
return new CoreContext(store, local, cache, core, encryption, t, lock, other)
|
|
179
204
|
}
|
|
180
205
|
}
|
|
181
206
|
|
package/lib/diff.js
CHANGED
|
@@ -3,7 +3,9 @@ const b4a = require('b4a')
|
|
|
3
3
|
const { RangeIterator } = require('./ranges.js')
|
|
4
4
|
|
|
5
5
|
class DiffIterator {
|
|
6
|
-
constructor(tree, left, right,
|
|
6
|
+
constructor(tree, left, right, opts = {}) {
|
|
7
|
+
const { limit = -1 } = opts
|
|
8
|
+
|
|
7
9
|
this.tree = tree
|
|
8
10
|
|
|
9
11
|
this.left = left
|
|
@@ -11,7 +13,7 @@ class DiffIterator {
|
|
|
11
13
|
|
|
12
14
|
this.left.limit = this.right.limit = -1
|
|
13
15
|
|
|
14
|
-
this.
|
|
16
|
+
this.config = tree.config.options(opts)
|
|
15
17
|
this.limit = limit
|
|
16
18
|
|
|
17
19
|
this.nextLeft = null
|
|
@@ -23,14 +25,14 @@ class DiffIterator {
|
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
async _shiftRight() {
|
|
26
|
-
const right = await this.tree.finalizeKeyPointer(this.nextRight, this.
|
|
28
|
+
const right = await this.tree.finalizeKeyPointer(this.nextRight, this.config)
|
|
27
29
|
this.nextRight = null
|
|
28
30
|
if (this.limit > 0) this.limit--
|
|
29
31
|
return { left: null, right }
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
async _shiftLeft() {
|
|
33
|
-
const left = await this.tree.finalizeKeyPointer(this.nextLeft, this.
|
|
35
|
+
const left = await this.tree.finalizeKeyPointer(this.nextLeft, this.config)
|
|
34
36
|
this.nextLeft = null
|
|
35
37
|
if (this.limit > 0) this.limit--
|
|
36
38
|
return { left, right: null }
|
|
@@ -77,8 +79,8 @@ class DiffIterator {
|
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
const [left, right] = await Promise.all([
|
|
80
|
-
this.tree.finalizeKeyPointer(leftKey, this.
|
|
81
|
-
this.tree.finalizeKeyPointer(rightKey, this.
|
|
82
|
+
this.tree.finalizeKeyPointer(leftKey, this.config),
|
|
83
|
+
this.tree.finalizeKeyPointer(rightKey, this.config)
|
|
82
84
|
])
|
|
83
85
|
|
|
84
86
|
if (this.limit > 0) this.limit--
|
package/lib/ranges.js
CHANGED
|
@@ -3,21 +3,12 @@ const b4a = require('b4a')
|
|
|
3
3
|
|
|
4
4
|
class RangeIterator {
|
|
5
5
|
constructor(tree, opts = {}) {
|
|
6
|
-
const {
|
|
7
|
-
activeRequests = [],
|
|
8
|
-
prefetch = true,
|
|
9
|
-
reverse = false,
|
|
10
|
-
limit = -1,
|
|
11
|
-
gte,
|
|
12
|
-
gt,
|
|
13
|
-
lte,
|
|
14
|
-
lt
|
|
15
|
-
} = opts
|
|
6
|
+
const { prefetch = true, reverse = false, limit = -1, gte, gt, lte, lt } = opts
|
|
16
7
|
|
|
17
8
|
this.tree = tree
|
|
18
9
|
this.root = null
|
|
19
10
|
this.stack = []
|
|
20
|
-
this.
|
|
11
|
+
this.config = tree.config.options(opts)
|
|
21
12
|
this.reverse = reverse
|
|
22
13
|
this.limit = limit
|
|
23
14
|
this.start = gte || gt || null
|
|
@@ -29,7 +20,7 @@ class RangeIterator {
|
|
|
29
20
|
}
|
|
30
21
|
|
|
31
22
|
async open() {
|
|
32
|
-
if (!this.root) this.root = await this.tree.bootstrap(this.
|
|
23
|
+
if (!this.root) this.root = await this.tree.bootstrap(this.config)
|
|
33
24
|
if (!this.root) return
|
|
34
25
|
|
|
35
26
|
if (this.limit === 0) return
|
|
@@ -44,7 +35,7 @@ class RangeIterator {
|
|
|
44
35
|
const top = this.stack[this.stack.length - 1]
|
|
45
36
|
const v = top.node.value
|
|
46
37
|
? this.tree.bump(top.node)
|
|
47
|
-
: await this.tree.inflate(top.node, this.
|
|
38
|
+
: await this.tree.inflate(top.node, this.config)
|
|
48
39
|
|
|
49
40
|
for (let i = 0; i < v.keys.length; i++) {
|
|
50
41
|
const j = this.reverse ? v.keys.length - 1 - i : i
|
|
@@ -75,7 +66,7 @@ class RangeIterator {
|
|
|
75
66
|
|
|
76
67
|
async next() {
|
|
77
68
|
const key = await this.nextKey()
|
|
78
|
-
return key === null ? key : this.tree.finalizeKeyPointer(key, this.
|
|
69
|
+
return key === null ? key : this.tree.finalizeKeyPointer(key, this.config)
|
|
79
70
|
}
|
|
80
71
|
|
|
81
72
|
async nextKey() {
|
|
@@ -83,7 +74,7 @@ class RangeIterator {
|
|
|
83
74
|
const top = this.stack.pop()
|
|
84
75
|
const v = top.node.value
|
|
85
76
|
? this.tree.bump(top.node)
|
|
86
|
-
: await this.tree.inflate(top.node, this.
|
|
77
|
+
: await this.tree.inflate(top.node, this.config)
|
|
87
78
|
|
|
88
79
|
const offset = top.offset++
|
|
89
80
|
const child = (offset & 1) === 0
|
|
@@ -150,7 +141,7 @@ class RangeIterator {
|
|
|
150
141
|
if (cmp > this.compare) break
|
|
151
142
|
|
|
152
143
|
const c = pv.children.get(i)
|
|
153
|
-
if (!c.value) this.tree.inflate(c, this.
|
|
144
|
+
if (!c.value) this.tree.inflate(c, this.config).catch(noop)
|
|
154
145
|
|
|
155
146
|
limit = Math.max(limit - this.tree.context.minKeys, 0)
|
|
156
147
|
if (limit === 0) break
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class SessionConfig {
|
|
2
|
+
constructor(activeRequests, timeout, wait) {
|
|
3
|
+
this.activeRequests = activeRequests
|
|
4
|
+
this.timeout = timeout
|
|
5
|
+
this.wait = wait
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
sub(activeRequests, timeout, wait) {
|
|
9
|
+
if (this.activeRequests === activeRequests && this.timeout === timeout && this.wait === wait) {
|
|
10
|
+
return this
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return new SessionConfig(activeRequests, timeout, wait)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
options(opts) {
|
|
17
|
+
if (!opts) return this
|
|
18
|
+
const { activeRequests = this.activeRequests, timeout = this.timeout, wait = this.wait } = opts
|
|
19
|
+
return this.sub(activeRequests, timeout, wait)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = SessionConfig
|
package/lib/tree.js
CHANGED
|
@@ -23,9 +23,14 @@ class Pointer {
|
|
|
23
23
|
this.seq = seq
|
|
24
24
|
this.offset = offset
|
|
25
25
|
this.changed = changed
|
|
26
|
+
this.retained = 0
|
|
26
27
|
|
|
27
28
|
this.changedBy = null
|
|
28
29
|
}
|
|
30
|
+
|
|
31
|
+
retain() {
|
|
32
|
+
this.retained = this.context.cache.retained + 1
|
|
33
|
+
}
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
class KeyPointer extends Pointer {
|
|
@@ -38,11 +43,9 @@ class KeyPointer extends Pointer {
|
|
|
38
43
|
}
|
|
39
44
|
|
|
40
45
|
// TODO: remove, left here for easier debugging for now
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// )
|
|
45
|
-
// }
|
|
46
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
47
|
+
return `[KeyPointer core=${this.core} seq=${this.seq}, offset=${this.offset}, key="${b4a.toString(this.key)}"]`
|
|
48
|
+
}
|
|
46
49
|
}
|
|
47
50
|
|
|
48
51
|
class TreeNodePointer extends Pointer {
|
|
@@ -55,12 +58,19 @@ class TreeNodePointer extends Pointer {
|
|
|
55
58
|
this.prev = null
|
|
56
59
|
}
|
|
57
60
|
|
|
61
|
+
commit() {
|
|
62
|
+
this.changed = false
|
|
63
|
+
const value = new TreeNode([], [])
|
|
64
|
+
value.keys = this.value.keys.commit()
|
|
65
|
+
value.children = this.value.children.commit()
|
|
66
|
+
const ptr = this.context.createTreeNode(this.core, 0, 0, true, value)
|
|
67
|
+
return ptr
|
|
68
|
+
}
|
|
69
|
+
|
|
58
70
|
// TODO: remove, left here for easier debugging for now
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
// )
|
|
63
|
-
// }
|
|
71
|
+
[Symbol.for('nodejs.util.inspect.custom')]() {
|
|
72
|
+
return `[TreeNodePointer core=${this.core} seq=${this.seq} offset=${this.offset} changed=${this.changed}]`
|
|
73
|
+
}
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
class TreeNode {
|
|
@@ -70,17 +80,17 @@ class TreeNode {
|
|
|
70
80
|
}
|
|
71
81
|
|
|
72
82
|
isEmpty() {
|
|
73
|
-
return this.keys.
|
|
83
|
+
return this.keys.ulength === 0 && this.children.ulength === 0
|
|
74
84
|
}
|
|
75
85
|
|
|
76
86
|
insertLeaf(context, key, value) {
|
|
77
87
|
let s = 0
|
|
78
|
-
let e = this.keys.
|
|
88
|
+
let e = this.keys.ulength
|
|
79
89
|
let c = 0
|
|
80
90
|
|
|
81
91
|
while (s < e) {
|
|
82
92
|
const mid = (s + e) >> 1
|
|
83
|
-
const k = this.keys.
|
|
93
|
+
const k = this.keys.uget(mid)
|
|
84
94
|
|
|
85
95
|
c = b4a.compare(key, k.key)
|
|
86
96
|
|
|
@@ -91,19 +101,20 @@ class TreeNode {
|
|
|
91
101
|
}
|
|
92
102
|
|
|
93
103
|
const i = c < 0 ? e : s
|
|
104
|
+
|
|
94
105
|
this.keys.insert(i, new KeyPointer(context, 0, 0, 0, true, key, value, null))
|
|
95
106
|
|
|
96
|
-
return this.keys.
|
|
107
|
+
return this.keys.ulength <= context.maxKeys ? INSERTED : NEEDS_SPLIT
|
|
97
108
|
}
|
|
98
109
|
|
|
99
110
|
insertNode(context, keyPointer, treePointer) {
|
|
100
111
|
let s = 0
|
|
101
|
-
let e = this.keys.
|
|
112
|
+
let e = this.keys.ulength
|
|
102
113
|
let c = 0
|
|
103
114
|
|
|
104
115
|
while (s < e) {
|
|
105
116
|
const mid = (s + e) >> 1
|
|
106
|
-
const k = this.keys.
|
|
117
|
+
const k = this.keys.uget(mid)
|
|
107
118
|
|
|
108
119
|
c = b4a.compare(keyPointer.key, k.key)
|
|
109
120
|
|
|
@@ -117,16 +128,16 @@ class TreeNode {
|
|
|
117
128
|
this.keys.insert(i, keyPointer)
|
|
118
129
|
this.children.insert(i + 1, treePointer)
|
|
119
130
|
|
|
120
|
-
return this.keys.
|
|
131
|
+
return this.keys.ulength <= context.maxKeys ? INSERTED : NEEDS_SPLIT
|
|
121
132
|
}
|
|
122
133
|
|
|
123
134
|
setValue(context, i, value) {
|
|
124
|
-
this.keys.set(i, new KeyPointer(context, 0, 0, 0, true, this.keys.
|
|
135
|
+
this.keys.set(i, new KeyPointer(context, 0, 0, 0, true, this.keys.uget(i).key, value, null))
|
|
125
136
|
}
|
|
126
137
|
|
|
127
138
|
removeKey(i) {
|
|
128
139
|
this.keys.delete(i)
|
|
129
|
-
if (this.children.
|
|
140
|
+
if (this.children.ulength) {
|
|
130
141
|
this.children.delete(i + 1)
|
|
131
142
|
}
|
|
132
143
|
}
|
|
@@ -134,11 +145,11 @@ class TreeNode {
|
|
|
134
145
|
siblings(parent) {
|
|
135
146
|
const pc = parent.children
|
|
136
147
|
|
|
137
|
-
for (let i = 0; i < pc.
|
|
138
|
-
if (pc.
|
|
148
|
+
for (let i = 0; i < pc.ulength; i++) {
|
|
149
|
+
if (pc.uget(i).value !== this) continue // TODO: move to a seq/offset check instead
|
|
139
150
|
|
|
140
|
-
const left = i ? pc.
|
|
141
|
-
const right = i < pc.
|
|
151
|
+
const left = i ? pc.uget(i - 1) : null
|
|
152
|
+
const right = i < pc.ulength - 1 ? pc.uget(i + 1) : null
|
|
142
153
|
return { left, index: i, right }
|
|
143
154
|
}
|
|
144
155
|
|
|
@@ -152,13 +163,13 @@ class TreeNode {
|
|
|
152
163
|
|
|
153
164
|
this.keys.push(median)
|
|
154
165
|
|
|
155
|
-
for (let i = 0; i < keys.
|
|
156
|
-
for (let i = 0; i < children.
|
|
166
|
+
for (let i = 0; i < keys.ulength; i++) this.keys.push(keys.uget(i))
|
|
167
|
+
for (let i = 0; i < children.ulength; i++) this.children.push(children.uget(i))
|
|
157
168
|
}
|
|
158
169
|
|
|
159
170
|
split(context) {
|
|
160
|
-
const len = this.keys.
|
|
161
|
-
const right =
|
|
171
|
+
const len = this.keys.ulength >> 1
|
|
172
|
+
const right = context.createTreeNode(0, 0, 0, true, new TreeNode([], []))
|
|
162
173
|
|
|
163
174
|
const k = []
|
|
164
175
|
while (k.length < len) k.push(this.keys.pop())
|
|
@@ -166,7 +177,7 @@ class TreeNode {
|
|
|
166
177
|
|
|
167
178
|
const median = this.keys.pop()
|
|
168
179
|
|
|
169
|
-
if (this.children.
|
|
180
|
+
if (this.children.ulength) {
|
|
170
181
|
const c = []
|
|
171
182
|
while (c.length < len + 1) c.push(this.children.pop())
|
|
172
183
|
for (let i = c.length - 1; i >= 0; i--) right.value.children.push(c[i])
|