hyperbee2 1.1.2 → 2.0.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/index.js +161 -22
- package/lib/cache.js +7 -0
- package/lib/{changes-stream.js → changes.js} +5 -2
- package/lib/compat.js +196 -13
- package/lib/compression.js +163 -0
- package/lib/context.js +50 -9
- package/lib/diff.js +142 -0
- package/lib/encoding.js +136 -6
- package/lib/ranges.js +144 -0
- package/lib/tree.js +97 -48
- package/lib/write.js +270 -101
- package/package.json +4 -2
- package/spec/hyperschema/index.js +356 -47
- package/spec/hyperschema/schema.json +208 -5
- package/lib/key-value-stream.js +0 -134
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
const OP_SET = 0
|
|
2
|
+
const OP_INSERT = 1
|
|
3
|
+
const OP_DEL = 2
|
|
4
|
+
const OP_COHORT = 3
|
|
5
|
+
|
|
6
|
+
class DeltaOp {
|
|
7
|
+
constructor(changed, type, index, pointer) {
|
|
8
|
+
this.type = type
|
|
9
|
+
this.index = index
|
|
10
|
+
this.pointer = pointer
|
|
11
|
+
this.changed = changed
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class DeltaCohort extends DeltaOp {
|
|
16
|
+
constructor(changed, pointer, deltas) {
|
|
17
|
+
super(changed, OP_COHORT, 0, pointer)
|
|
18
|
+
this.deltas = deltas
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class CompressedArray {
|
|
23
|
+
constructor(delta) {
|
|
24
|
+
this.entries = []
|
|
25
|
+
this.delta = delta
|
|
26
|
+
|
|
27
|
+
for (const d of delta) {
|
|
28
|
+
if (d.type === OP_COHORT) {
|
|
29
|
+
for (const dd of d.deltas) {
|
|
30
|
+
apply(this.entries, dd.type, dd.index, dd.pointer)
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
apply(this.entries, d.type, d.index, d.pointer)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get length() {
|
|
39
|
+
return this.entries.length
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
touch(index) {
|
|
43
|
+
const pointer = this.entries[index]
|
|
44
|
+
if (pointer.changedBy === this) return
|
|
45
|
+
this.set(index, pointer)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get(index) {
|
|
49
|
+
return this.entries[index]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
push(pointer) {
|
|
53
|
+
this.insert(this.entries.length, pointer)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
unshift(pointer) {
|
|
57
|
+
this.insert(0, pointer)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
pop() {
|
|
61
|
+
if (this.entries.length === 0) return
|
|
62
|
+
const head = this.entries[this.entries.length - 1]
|
|
63
|
+
this.delete(this.entries.length - 1)
|
|
64
|
+
return head
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
shift() {
|
|
68
|
+
if (this.entries.length === 0) return
|
|
69
|
+
const tail = this.entries[0]
|
|
70
|
+
this.delete(0)
|
|
71
|
+
return tail
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
_touch(pointer) {
|
|
75
|
+
if (pointer) pointer.changedBy = this
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
insert(index, pointer) {
|
|
79
|
+
if (!insert(this.entries, index, pointer)) return
|
|
80
|
+
this._touch(pointer)
|
|
81
|
+
this.delta.push(new DeltaOp(true, OP_INSERT, index, pointer))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
delete(index) {
|
|
85
|
+
if (!del(this.entries, index)) return
|
|
86
|
+
this._touch(null)
|
|
87
|
+
this.delta.push(new DeltaOp(true, OP_DEL, index, null))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
set(index, pointer) {
|
|
91
|
+
if (!set(this.entries, index, pointer)) return
|
|
92
|
+
this._touch(pointer)
|
|
93
|
+
this.delta.push(new DeltaOp(true, OP_SET, index, pointer))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
flush(max, min) {
|
|
97
|
+
if (this.delta.length <= max) return this.delta
|
|
98
|
+
|
|
99
|
+
const direct = []
|
|
100
|
+
while (this.delta.length && this.delta[this.delta.length - 1].type !== OP_COHORT) {
|
|
101
|
+
direct.push(this.delta.pop())
|
|
102
|
+
}
|
|
103
|
+
direct.reverse()
|
|
104
|
+
|
|
105
|
+
if (direct.length > min && direct.length < this.entries.length) {
|
|
106
|
+
const co = new DeltaCohort(true, null, [])
|
|
107
|
+
for (const d of direct) {
|
|
108
|
+
co.deltas.push(d)
|
|
109
|
+
}
|
|
110
|
+
this.delta.push(co)
|
|
111
|
+
} else {
|
|
112
|
+
const co = new DeltaCohort(true, null, [])
|
|
113
|
+
for (let i = 0; i < this.entries.length; i++) {
|
|
114
|
+
const d = new DeltaOp(true, OP_INSERT, i, this.entries[i])
|
|
115
|
+
co.deltas.push(d)
|
|
116
|
+
}
|
|
117
|
+
this.delta = [co]
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return this.delta
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
exports.CompressedArray = CompressedArray
|
|
125
|
+
exports.DeltaOp = DeltaOp
|
|
126
|
+
exports.DeltaCohort = DeltaCohort
|
|
127
|
+
|
|
128
|
+
exports.OP_SET = OP_SET
|
|
129
|
+
exports.OP_INSERT = OP_INSERT
|
|
130
|
+
exports.OP_DEL = OP_DEL
|
|
131
|
+
exports.OP_COHORT = OP_COHORT
|
|
132
|
+
|
|
133
|
+
function del(entries, index) {
|
|
134
|
+
if (index >= entries.length) return false
|
|
135
|
+
entries.splice(index, 1)
|
|
136
|
+
return true
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function insert(entries, index, pointer) {
|
|
140
|
+
if (index >= entries.length + 1) return false
|
|
141
|
+
entries.splice(index, 0, pointer)
|
|
142
|
+
return true
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function set(entries, index, pointer) {
|
|
146
|
+
if (index >= entries.length) return false
|
|
147
|
+
// if (entries[index] === pointer) return false
|
|
148
|
+
entries[index] = pointer
|
|
149
|
+
return true
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function apply(entries, type, index, pointer) {
|
|
153
|
+
if (type === OP_INSERT) {
|
|
154
|
+
return insert(entries, index, pointer)
|
|
155
|
+
}
|
|
156
|
+
if (type === OP_DEL) {
|
|
157
|
+
return del(entries, index)
|
|
158
|
+
}
|
|
159
|
+
if (type === OP_SET) {
|
|
160
|
+
return set(entries, index, pointer)
|
|
161
|
+
}
|
|
162
|
+
return false
|
|
163
|
+
}
|
package/lib/context.js
CHANGED
|
@@ -3,10 +3,13 @@ const ScopeLock = require('scope-lock')
|
|
|
3
3
|
const { decodeBlock } = require('./encoding.js')
|
|
4
4
|
|
|
5
5
|
class CoreContext {
|
|
6
|
-
constructor(store, local, core, encryption, lock = new ScopeLock(), other = new Map()) {
|
|
6
|
+
constructor(store, local, core, encryption, t, lock = new ScopeLock(), other = new Map()) {
|
|
7
7
|
this.store = store
|
|
8
8
|
this.local = local
|
|
9
9
|
this.core = core
|
|
10
|
+
this.t = t
|
|
11
|
+
this.minKeys = t - 1
|
|
12
|
+
this.maxKeys = 2 * t - 1
|
|
10
13
|
this.encryption = encryption
|
|
11
14
|
this.lock = lock
|
|
12
15
|
this.other = other
|
|
@@ -17,6 +20,11 @@ class CoreContext {
|
|
|
17
20
|
this.changed = false
|
|
18
21
|
}
|
|
19
22
|
|
|
23
|
+
// TODO: remove, left here for easier debugging for now
|
|
24
|
+
// [Symbol.for('nodejs.util.inspect.custom')]() {
|
|
25
|
+
// return '[CoreContext]'
|
|
26
|
+
// }
|
|
27
|
+
|
|
20
28
|
async update(activeRequests) {
|
|
21
29
|
await this.core.ready()
|
|
22
30
|
|
|
@@ -34,7 +42,7 @@ class CoreContext {
|
|
|
34
42
|
|
|
35
43
|
if (length < this.length) return
|
|
36
44
|
|
|
37
|
-
this.cores = block.cores
|
|
45
|
+
this.cores = block.metadata ? block.metadata.cores : []
|
|
38
46
|
while (this.opened.length < this.cores.length) this.opened.push(null)
|
|
39
47
|
}
|
|
40
48
|
|
|
@@ -44,8 +52,9 @@ class CoreContext {
|
|
|
44
52
|
this.length = length
|
|
45
53
|
}
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
if (
|
|
55
|
+
getCoreOffsetLocal(context, core) {
|
|
56
|
+
if (context === this) return core
|
|
57
|
+
|
|
49
58
|
const key = core === 0 ? context.core.key : context.cores[core - 1].key
|
|
50
59
|
|
|
51
60
|
if (b4a.equals(key, this.core.key)) return 0
|
|
@@ -60,9 +69,16 @@ class CoreContext {
|
|
|
60
69
|
|
|
61
70
|
this.changed = true
|
|
62
71
|
this.cores.push({ key, fork: 0, length: 0, treeHash: null })
|
|
72
|
+
this.opened.push(null)
|
|
73
|
+
|
|
63
74
|
return this.cores.length
|
|
64
75
|
}
|
|
65
76
|
|
|
77
|
+
async getCoreOffset(context, core, activeRequests) {
|
|
78
|
+
if (core !== 0 && core - 1 >= context.cores.length) await context.update(activeRequests)
|
|
79
|
+
return this.getCoreOffsetLocal(context, core)
|
|
80
|
+
}
|
|
81
|
+
|
|
66
82
|
async getCoreOffsetByKey(key, activeRequests) {
|
|
67
83
|
await this.core.ready()
|
|
68
84
|
|
|
@@ -86,6 +102,7 @@ class CoreContext {
|
|
|
86
102
|
|
|
87
103
|
this.changed = true
|
|
88
104
|
this.cores.push({ key, fork: 0, length: 0, treeHash: null })
|
|
105
|
+
this.opened.push(null)
|
|
89
106
|
return this.cores.length
|
|
90
107
|
}
|
|
91
108
|
|
|
@@ -99,8 +116,17 @@ class CoreContext {
|
|
|
99
116
|
return this.opened[index - 1]
|
|
100
117
|
}
|
|
101
118
|
|
|
119
|
+
getCoreKey(index) {
|
|
120
|
+
if (index === 0) return this.core.key
|
|
121
|
+
if (index > this.cores.length) throw new Error('Bad core index: ' + index)
|
|
122
|
+
if (this.opened[index - 1] !== null && this.opened[index - 1].key) {
|
|
123
|
+
return this.opened[index - 1].key
|
|
124
|
+
}
|
|
125
|
+
return this.cores[index - 1].key
|
|
126
|
+
}
|
|
127
|
+
|
|
102
128
|
async getBlock(seq, core, activeRequests) {
|
|
103
|
-
if (core !== 0) await this.update(activeRequests)
|
|
129
|
+
if (core !== 0 && core - 1 >= this.cores.length) await this.update(activeRequests)
|
|
104
130
|
const hc = this.getCore(core)
|
|
105
131
|
const buffer = await hc.get(seq, { activeRequests })
|
|
106
132
|
const block = decodeBlock(buffer, seq)
|
|
@@ -117,8 +143,7 @@ class CoreContext {
|
|
|
117
143
|
const hex = b4a.toString(key, 'hex')
|
|
118
144
|
if (this.other.has(hex)) return this.other.get(hex)
|
|
119
145
|
|
|
120
|
-
const
|
|
121
|
-
const ctx = new CoreContext(this.store, this.local, hc, this.encryption, this.lock, this.other)
|
|
146
|
+
const ctx = this._createContext(this.store.get({ key, encryption: this.encryption }))
|
|
122
147
|
this.other.set(hex, ctx)
|
|
123
148
|
return ctx
|
|
124
149
|
}
|
|
@@ -131,11 +156,27 @@ class CoreContext {
|
|
|
131
156
|
const hex = b4a.toString(this.cores[core - 1].key, 'hex')
|
|
132
157
|
if (this.other.has(hex)) return this.other.get(hex)
|
|
133
158
|
|
|
134
|
-
const
|
|
135
|
-
const ctx = new CoreContext(this.store, this.local, hc, this.encryption, this.lock, this.other)
|
|
159
|
+
const ctx = this._createContext(this.getCore(core))
|
|
136
160
|
this.other.set(hex, ctx)
|
|
137
161
|
return ctx
|
|
138
162
|
}
|
|
163
|
+
|
|
164
|
+
flush() {
|
|
165
|
+
this.changed = false
|
|
166
|
+
return {
|
|
167
|
+
cores: this.cores
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
_createContext(core) {
|
|
172
|
+
const store = this.store
|
|
173
|
+
const local = this.local
|
|
174
|
+
const encryption = this.encryption
|
|
175
|
+
const t = this.t
|
|
176
|
+
const lock = this.lock
|
|
177
|
+
const other = this.other
|
|
178
|
+
return new CoreContext(store, local, core, encryption, t, lock, other)
|
|
179
|
+
}
|
|
139
180
|
}
|
|
140
181
|
|
|
141
182
|
module.exports = CoreContext
|
package/lib/diff.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const { Readable } = require('streamx')
|
|
2
|
+
const b4a = require('b4a')
|
|
3
|
+
const { RangeIterator } = require('./ranges.js')
|
|
4
|
+
|
|
5
|
+
class DiffIterator {
|
|
6
|
+
constructor(tree, left, right, { limit = -1, activeRequests = [] } = {}) {
|
|
7
|
+
this.tree = tree
|
|
8
|
+
|
|
9
|
+
this.left = left
|
|
10
|
+
this.right = right
|
|
11
|
+
|
|
12
|
+
this.left.limit = this.right.limit = -1
|
|
13
|
+
|
|
14
|
+
this.activeRequests = activeRequests
|
|
15
|
+
this.limit = limit
|
|
16
|
+
|
|
17
|
+
this.nextLeft = null
|
|
18
|
+
this.nextRight = null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async open() {
|
|
22
|
+
await Promise.all([this.left.open(), this.right.open()])
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async _shiftRight() {
|
|
26
|
+
const right = await this.tree.finalizeKeyPointer(this.nextRight, this.activeRequests)
|
|
27
|
+
this.nextRight = null
|
|
28
|
+
if (this.limit > 0) this.limit--
|
|
29
|
+
return { left: null, right }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async _shiftLeft() {
|
|
33
|
+
const left = await this.tree.finalizeKeyPointer(this.nextLeft, this.activeRequests)
|
|
34
|
+
this.nextLeft = null
|
|
35
|
+
if (this.limit > 0) this.limit--
|
|
36
|
+
return { left, right: null }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
_fastForward() {
|
|
40
|
+
while (this.left.stack.length && this.right.stack.length) {
|
|
41
|
+
const a = this.left.stack[this.left.stack.length - 1]
|
|
42
|
+
const b = this.right.stack[this.right.stack.length - 1]
|
|
43
|
+
|
|
44
|
+
if (a.offset !== b.offset) return
|
|
45
|
+
if (!isSame(a.node, b.node)) return
|
|
46
|
+
|
|
47
|
+
this.left.stack.pop()
|
|
48
|
+
this.right.stack.pop()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async next() {
|
|
53
|
+
while (this.limit === -1 || this.limit > 0) {
|
|
54
|
+
const leftPromise = this.nextLeft ? null : this.left.nextKey()
|
|
55
|
+
const rightPromise = this.nextRight ? null : this.right.nextKey()
|
|
56
|
+
|
|
57
|
+
const [l, r] = await Promise.all([leftPromise, rightPromise])
|
|
58
|
+
|
|
59
|
+
if (leftPromise) this.nextLeft = l
|
|
60
|
+
if (rightPromise) this.nextRight = r
|
|
61
|
+
|
|
62
|
+
if (!this.nextLeft && !this.nextRight) return null
|
|
63
|
+
if (!this.nextLeft) return this._shiftRight()
|
|
64
|
+
if (!this.nextRight) return this._shiftLeft()
|
|
65
|
+
|
|
66
|
+
const cmp = b4a.compare(this.nextLeft.key, this.nextRight.key)
|
|
67
|
+
|
|
68
|
+
if (cmp === 0) {
|
|
69
|
+
const leftKey = this.nextLeft
|
|
70
|
+
const rightKey = this.nextRight
|
|
71
|
+
|
|
72
|
+
this.nextRight = this.nextLeft = null
|
|
73
|
+
|
|
74
|
+
if (isSame(leftKey, rightKey)) {
|
|
75
|
+
this._fastForward()
|
|
76
|
+
continue
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const [left, right] = await Promise.all([
|
|
80
|
+
this.tree.finalizeKeyPointer(leftKey, this.activeRequests),
|
|
81
|
+
this.tree.finalizeKeyPointer(rightKey, this.activeRequests)
|
|
82
|
+
])
|
|
83
|
+
|
|
84
|
+
if (this.limit > 0) this.limit--
|
|
85
|
+
return { left, right }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (cmp < 0) return this._shiftLeft()
|
|
89
|
+
return this._shiftRight()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
class DiffStream extends Readable {
|
|
97
|
+
constructor(left, right, options = {}) {
|
|
98
|
+
const { highWaterMark } = options
|
|
99
|
+
super({ eagerOpen: true, highWaterMark })
|
|
100
|
+
|
|
101
|
+
this.left = left
|
|
102
|
+
this.right = right
|
|
103
|
+
this.iterator = new DiffIterator(
|
|
104
|
+
left,
|
|
105
|
+
new RangeIterator(left, options),
|
|
106
|
+
new RangeIterator(right, options),
|
|
107
|
+
options
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async _open(cb) {
|
|
112
|
+
try {
|
|
113
|
+
await this.iterator.open()
|
|
114
|
+
} catch (err) {
|
|
115
|
+
cb(err)
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
cb(null)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async _read(cb) {
|
|
122
|
+
try {
|
|
123
|
+
this.push(await this.iterator.next())
|
|
124
|
+
} catch (err) {
|
|
125
|
+
cb(err)
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
cb(null)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
exports.DiffIterator = DiffIterator
|
|
133
|
+
exports.DiffStream = DiffStream
|
|
134
|
+
|
|
135
|
+
function isSame(a, b) {
|
|
136
|
+
if (a.seq !== b.seq || a.offset !== b.offset) return false
|
|
137
|
+
|
|
138
|
+
const k1 = a.context.getCoreKey(a.core)
|
|
139
|
+
const k2 = b.context.getCoreKey(b.core)
|
|
140
|
+
|
|
141
|
+
return k1 === k2 || b4a.equals(k1, k2) ? true : false
|
|
142
|
+
}
|
package/lib/encoding.js
CHANGED
|
@@ -1,14 +1,144 @@
|
|
|
1
1
|
const c = require('compact-encoding')
|
|
2
2
|
const { getEncoding } = require('../spec/hyperschema')
|
|
3
|
-
const
|
|
3
|
+
const { OP_COHORT, OP_INSERT } = require('./compression.js')
|
|
4
|
+
const compat = require('./compat.js')
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
+
const Block0 = getEncoding('@bee/block-0')
|
|
7
|
+
const Block1 = getEncoding('@bee/block-1')
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
const TYPE_COMPAT = 10 // compat with bee1, all blocks starts with 0x10
|
|
10
|
+
|
|
11
|
+
exports.TYPE_COMPAT = TYPE_COMPAT
|
|
12
|
+
exports.TYPE_LATEST = 1
|
|
13
|
+
|
|
14
|
+
exports.Block = Block1
|
|
15
|
+
exports.encodeBlock = encodeBlock
|
|
8
16
|
exports.decodeBlock = decodeBlock
|
|
9
17
|
|
|
18
|
+
function encodeBlock(block) {
|
|
19
|
+
if (block.type === 1) {
|
|
20
|
+
return c.encode(Block1, block)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (block.type === 0) {
|
|
24
|
+
return encodeBlock0(block)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (block.type === TYPE_COMPAT) {
|
|
28
|
+
return compat.encode(block)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
throw new Error('Unknown block type: ' + block.type)
|
|
32
|
+
}
|
|
33
|
+
|
|
10
34
|
function decodeBlock(buffer, seq) {
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
|
|
35
|
+
const state = { start: 0, end: buffer.byteLength, buffer }
|
|
36
|
+
const type = state.end > 0 ? c.uint.decode(state) : 0
|
|
37
|
+
|
|
38
|
+
state.start = 0
|
|
39
|
+
|
|
40
|
+
if (type === 1) {
|
|
41
|
+
return Block1.decode(state)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (type === 0) {
|
|
45
|
+
return decodeBlock0(state, seq)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (type === TYPE_COMPAT) {
|
|
49
|
+
return compat.decode(buffer, seq)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
throw new Error('Unknown block type: ' + type)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function dataToKey(d) {
|
|
56
|
+
return { ...d, valuePointer: null }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function keyToData(d) {
|
|
60
|
+
return { key: d.key, value: d.value }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function decodeBlock0(state, seq) {
|
|
64
|
+
const cohorts = []
|
|
65
|
+
const blk = Block0.decode(state)
|
|
66
|
+
const tree = []
|
|
67
|
+
|
|
68
|
+
for (const t of blk.tree) {
|
|
69
|
+
const next = {
|
|
70
|
+
keys: toCohort(seq, t.keys, cohorts),
|
|
71
|
+
children: toCohort(seq, t.children, cohorts)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
tree.push(next)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
type: 0,
|
|
79
|
+
checkpoint: blk.checkpoint,
|
|
80
|
+
batch: blk.batch,
|
|
81
|
+
previous: blk.previous,
|
|
82
|
+
metadata: { cores: blk.cores },
|
|
83
|
+
tree,
|
|
84
|
+
keys: blk.data.map(dataToKey),
|
|
85
|
+
values: null,
|
|
86
|
+
cohorts
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function encodeBlock0(blk) {
|
|
91
|
+
const tree = []
|
|
92
|
+
|
|
93
|
+
for (const t of blk.tree) {
|
|
94
|
+
tree.push({
|
|
95
|
+
keys: fromCohort(t.keys, blk.cohorts),
|
|
96
|
+
children: fromCohort(t.children, blk.cohorts)
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return c.encode(Block0, {
|
|
101
|
+
type: 0,
|
|
102
|
+
checkpoint: blk.checkpoint,
|
|
103
|
+
batch: blk.batch,
|
|
104
|
+
previous: blk.previous,
|
|
105
|
+
tree,
|
|
106
|
+
data: blk.keys.map(keyToData),
|
|
107
|
+
cores: blk.metadata ? blk.metadata.cores : null
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function fromCohort(deltas, cohorts) {
|
|
112
|
+
if (deltas.length === 0) return []
|
|
113
|
+
const cohort = cohorts[deltas[0].pointer.offset]
|
|
114
|
+
const pointers = []
|
|
115
|
+
for (const d of cohort) pointers.push(d.pointer)
|
|
116
|
+
return pointers
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function toCohort(seq, pointers, cohorts) {
|
|
120
|
+
if (!pointers.length) return []
|
|
121
|
+
|
|
122
|
+
const cohort = []
|
|
123
|
+
const offset = cohorts.push(cohort) - 1
|
|
124
|
+
|
|
125
|
+
for (let i = 0; i < pointers.length; i++) {
|
|
126
|
+
cohort.push({
|
|
127
|
+
type: OP_INSERT,
|
|
128
|
+
index: i,
|
|
129
|
+
pointer: pointers[i]
|
|
130
|
+
})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return [
|
|
134
|
+
{
|
|
135
|
+
type: OP_COHORT,
|
|
136
|
+
index: 0,
|
|
137
|
+
pointer: {
|
|
138
|
+
core: 0,
|
|
139
|
+
seq,
|
|
140
|
+
offset
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
]
|
|
14
144
|
}
|