hyperbee2 1.2.0 → 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 +156 -22
- package/lib/cache.js +7 -0
- package/lib/compat.js +47 -43
- package/lib/compression.js +163 -0
- package/lib/context.js +38 -9
- package/lib/diff.js +20 -10
- package/lib/encoding.js +132 -7
- package/lib/ranges.js +10 -5
- package/lib/tree.js +97 -48
- package/lib/write.js +239 -98
- package/package.json +4 -2
- package/spec/hyperschema/index.js +356 -47
- package/spec/hyperschema/schema.json +208 -5
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
|
|
@@ -65,6 +74,11 @@ class CoreContext {
|
|
|
65
74
|
return this.cores.length
|
|
66
75
|
}
|
|
67
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
|
+
|
|
68
82
|
async getCoreOffsetByKey(key, activeRequests) {
|
|
69
83
|
await this.core.ready()
|
|
70
84
|
|
|
@@ -112,7 +126,7 @@ class CoreContext {
|
|
|
112
126
|
}
|
|
113
127
|
|
|
114
128
|
async getBlock(seq, core, activeRequests) {
|
|
115
|
-
if (core !== 0) await this.update(activeRequests)
|
|
129
|
+
if (core !== 0 && core - 1 >= this.cores.length) await this.update(activeRequests)
|
|
116
130
|
const hc = this.getCore(core)
|
|
117
131
|
const buffer = await hc.get(seq, { activeRequests })
|
|
118
132
|
const block = decodeBlock(buffer, seq)
|
|
@@ -129,8 +143,7 @@ class CoreContext {
|
|
|
129
143
|
const hex = b4a.toString(key, 'hex')
|
|
130
144
|
if (this.other.has(hex)) return this.other.get(hex)
|
|
131
145
|
|
|
132
|
-
const
|
|
133
|
-
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 }))
|
|
134
147
|
this.other.set(hex, ctx)
|
|
135
148
|
return ctx
|
|
136
149
|
}
|
|
@@ -143,11 +156,27 @@ class CoreContext {
|
|
|
143
156
|
const hex = b4a.toString(this.cores[core - 1].key, 'hex')
|
|
144
157
|
if (this.other.has(hex)) return this.other.get(hex)
|
|
145
158
|
|
|
146
|
-
const
|
|
147
|
-
const ctx = new CoreContext(this.store, this.local, hc, this.encryption, this.lock, this.other)
|
|
159
|
+
const ctx = this._createContext(this.getCore(core))
|
|
148
160
|
this.other.set(hex, ctx)
|
|
149
161
|
return ctx
|
|
150
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
|
+
}
|
|
151
180
|
}
|
|
152
181
|
|
|
153
182
|
module.exports = CoreContext
|
package/lib/diff.js
CHANGED
|
@@ -3,11 +3,15 @@ const b4a = require('b4a')
|
|
|
3
3
|
const { RangeIterator } = require('./ranges.js')
|
|
4
4
|
|
|
5
5
|
class DiffIterator {
|
|
6
|
-
constructor(left, right, { limit = -1 } = {}) {
|
|
6
|
+
constructor(tree, left, right, { limit = -1, activeRequests = [] } = {}) {
|
|
7
|
+
this.tree = tree
|
|
8
|
+
|
|
7
9
|
this.left = left
|
|
8
10
|
this.right = right
|
|
9
11
|
|
|
10
12
|
this.left.limit = this.right.limit = -1
|
|
13
|
+
|
|
14
|
+
this.activeRequests = activeRequests
|
|
11
15
|
this.limit = limit
|
|
12
16
|
|
|
13
17
|
this.nextLeft = null
|
|
@@ -18,15 +22,15 @@ class DiffIterator {
|
|
|
18
22
|
await Promise.all([this.left.open(), this.right.open()])
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
_shiftRight() {
|
|
22
|
-
const right = this.nextRight
|
|
25
|
+
async _shiftRight() {
|
|
26
|
+
const right = await this.tree.finalizeKeyPointer(this.nextRight, this.activeRequests)
|
|
23
27
|
this.nextRight = null
|
|
24
28
|
if (this.limit > 0) this.limit--
|
|
25
29
|
return { left: null, right }
|
|
26
30
|
}
|
|
27
31
|
|
|
28
|
-
_shiftLeft() {
|
|
29
|
-
const left = this.nextLeft
|
|
32
|
+
async _shiftLeft() {
|
|
33
|
+
const left = await this.tree.finalizeKeyPointer(this.nextLeft, this.activeRequests)
|
|
30
34
|
this.nextLeft = null
|
|
31
35
|
if (this.limit > 0) this.limit--
|
|
32
36
|
return { left, right: null }
|
|
@@ -47,8 +51,8 @@ class DiffIterator {
|
|
|
47
51
|
|
|
48
52
|
async next() {
|
|
49
53
|
while (this.limit === -1 || this.limit > 0) {
|
|
50
|
-
const leftPromise = this.nextLeft ? null : this.left.
|
|
51
|
-
const rightPromise = this.nextRight ? null : this.right.
|
|
54
|
+
const leftPromise = this.nextLeft ? null : this.left.nextKey()
|
|
55
|
+
const rightPromise = this.nextRight ? null : this.right.nextKey()
|
|
52
56
|
|
|
53
57
|
const [l, r] = await Promise.all([leftPromise, rightPromise])
|
|
54
58
|
|
|
@@ -62,16 +66,21 @@ class DiffIterator {
|
|
|
62
66
|
const cmp = b4a.compare(this.nextLeft.key, this.nextRight.key)
|
|
63
67
|
|
|
64
68
|
if (cmp === 0) {
|
|
65
|
-
const
|
|
66
|
-
const
|
|
69
|
+
const leftKey = this.nextLeft
|
|
70
|
+
const rightKey = this.nextRight
|
|
67
71
|
|
|
68
72
|
this.nextRight = this.nextLeft = null
|
|
69
73
|
|
|
70
|
-
if (isSame(
|
|
74
|
+
if (isSame(leftKey, rightKey)) {
|
|
71
75
|
this._fastForward()
|
|
72
76
|
continue
|
|
73
77
|
}
|
|
74
78
|
|
|
79
|
+
const [left, right] = await Promise.all([
|
|
80
|
+
this.tree.finalizeKeyPointer(leftKey, this.activeRequests),
|
|
81
|
+
this.tree.finalizeKeyPointer(rightKey, this.activeRequests)
|
|
82
|
+
])
|
|
83
|
+
|
|
75
84
|
if (this.limit > 0) this.limit--
|
|
76
85
|
return { left, right }
|
|
77
86
|
}
|
|
@@ -92,6 +101,7 @@ class DiffStream extends Readable {
|
|
|
92
101
|
this.left = left
|
|
93
102
|
this.right = right
|
|
94
103
|
this.iterator = new DiffIterator(
|
|
104
|
+
left,
|
|
95
105
|
new RangeIterator(left, options),
|
|
96
106
|
new RangeIterator(right, options),
|
|
97
107
|
options
|
package/lib/encoding.js
CHANGED
|
@@ -1,19 +1,144 @@
|
|
|
1
1
|
const c = require('compact-encoding')
|
|
2
2
|
const { getEncoding } = require('../spec/hyperschema')
|
|
3
|
+
const { OP_COHORT, OP_INSERT } = require('./compression.js')
|
|
3
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
|
|
8
15
|
exports.encodeBlock = encodeBlock
|
|
9
16
|
exports.decodeBlock = decodeBlock
|
|
10
17
|
|
|
11
|
-
function encodeBlock(block
|
|
12
|
-
|
|
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)
|
|
13
32
|
}
|
|
14
33
|
|
|
15
34
|
function decodeBlock(buffer, seq) {
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
|
|
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
|
+
]
|
|
19
144
|
}
|
package/lib/ranges.js
CHANGED
|
@@ -37,8 +37,8 @@ class RangeIterator {
|
|
|
37
37
|
const j = this.reverse ? v.keys.length - 1 - i : i
|
|
38
38
|
|
|
39
39
|
const c = this.reverse
|
|
40
|
-
? b4a.compare(v.keys
|
|
41
|
-
: b4a.compare(this.start, v.keys
|
|
40
|
+
? b4a.compare(v.keys.get(j).key, this.end)
|
|
41
|
+
: b4a.compare(this.start, v.keys.get(j).key)
|
|
42
42
|
|
|
43
43
|
if (c < 0) break
|
|
44
44
|
top.offset = 2 * i + 1 + (c === 0 ? offset : 1)
|
|
@@ -52,7 +52,7 @@ class RangeIterator {
|
|
|
52
52
|
const j = this.reverse ? v.children.length - 1 - k : k
|
|
53
53
|
|
|
54
54
|
this.stack.push({
|
|
55
|
-
node: v.children
|
|
55
|
+
node: v.children.get(j),
|
|
56
56
|
offset: 0
|
|
57
57
|
})
|
|
58
58
|
|
|
@@ -61,6 +61,11 @@ class RangeIterator {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
async next() {
|
|
64
|
+
const key = await this.nextKey()
|
|
65
|
+
return key === null ? key : this.tree.finalizeKeyPointer(key, this.activeRequests)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async nextKey() {
|
|
64
69
|
while (this.stack.length && (this.limit === -1 || this.limit > 0)) {
|
|
65
70
|
const top = this.stack.pop()
|
|
66
71
|
const v = top.node.value
|
|
@@ -75,7 +80,7 @@ class RangeIterator {
|
|
|
75
80
|
this.stack.push(top)
|
|
76
81
|
if (k < v.children.length) {
|
|
77
82
|
const j = this.reverse ? v.children.length - 1 - k : k
|
|
78
|
-
this.stack.push({ node: v.children
|
|
83
|
+
this.stack.push({ node: v.children.get(j), offset: 0 })
|
|
79
84
|
}
|
|
80
85
|
continue
|
|
81
86
|
}
|
|
@@ -83,7 +88,7 @@ class RangeIterator {
|
|
|
83
88
|
if (k < v.keys.length) {
|
|
84
89
|
const j = this.reverse ? v.keys.length - 1 - k : k
|
|
85
90
|
|
|
86
|
-
const data = top.node.value.keys
|
|
91
|
+
const data = top.node.value.keys.get(j)
|
|
87
92
|
|
|
88
93
|
const c = this.reverse
|
|
89
94
|
? this.start
|
package/lib/tree.js
CHANGED
|
@@ -1,29 +1,22 @@
|
|
|
1
1
|
const b4a = require('b4a')
|
|
2
|
+
const { CompressedArray } = require('./compression.js')
|
|
2
3
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const MAX_CHILDREN = MIN_KEYS * 2 + 1
|
|
4
|
+
const INSERTED = -1
|
|
5
|
+
const NEEDS_SPLIT = -2
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const NEEDS_SPLIT = 2
|
|
10
|
-
|
|
11
|
-
class DataPointer {
|
|
12
|
-
constructor(context, core, seq, offset, changed, key, value) {
|
|
7
|
+
class ValuePointer {
|
|
8
|
+
constructor(context, core, seq, offset, split) {
|
|
13
9
|
this.context = context
|
|
14
10
|
|
|
15
11
|
this.core = core
|
|
16
12
|
this.seq = seq
|
|
17
13
|
this.offset = offset
|
|
18
|
-
this.
|
|
19
|
-
|
|
20
|
-
this.key = key
|
|
21
|
-
this.value = value
|
|
14
|
+
this.split = split
|
|
22
15
|
}
|
|
23
16
|
}
|
|
24
17
|
|
|
25
|
-
class
|
|
26
|
-
constructor(context, core, seq, offset, changed
|
|
18
|
+
class Pointer {
|
|
19
|
+
constructor(context, core, seq, offset, changed) {
|
|
27
20
|
this.context = context
|
|
28
21
|
|
|
29
22
|
this.core = core
|
|
@@ -31,68 +24,121 @@ class TreeNodePointer {
|
|
|
31
24
|
this.offset = offset
|
|
32
25
|
this.changed = changed
|
|
33
26
|
|
|
27
|
+
this.changedBy = null
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
class KeyPointer extends Pointer {
|
|
32
|
+
constructor(context, core, seq, offset, changed, key, value, valuePointer) {
|
|
33
|
+
super(context, core, seq, offset, changed)
|
|
34
|
+
|
|
35
|
+
this.key = key
|
|
36
|
+
this.value = value
|
|
37
|
+
this.valuePointer = valuePointer
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// TODO: remove, left here for easier debugging for now
|
|
41
|
+
// [Symbol.for('nodejs.util.inspect.custom')]() {
|
|
42
|
+
// return (
|
|
43
|
+
// `[KeyPointer core=${this.core} seq=${this.seq}, offset=${this.offset}, key="${b4a.toString(this.key)}"]`
|
|
44
|
+
// )
|
|
45
|
+
// }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
class TreeNodePointer extends Pointer {
|
|
49
|
+
constructor(context, core, seq, offset, changed, value) {
|
|
50
|
+
super(context, core, seq, offset, changed)
|
|
51
|
+
|
|
34
52
|
this.value = value
|
|
35
53
|
|
|
36
54
|
this.next = null
|
|
37
55
|
this.prev = null
|
|
38
56
|
}
|
|
57
|
+
|
|
58
|
+
// TODO: remove, left here for easier debugging for now
|
|
59
|
+
// [Symbol.for('nodejs.util.inspect.custom')]() {
|
|
60
|
+
// return (
|
|
61
|
+
// `[TreeNodePointer core=${this.core} seq=${this.seq} offset=${this.offset} changed=${this.changed}]`
|
|
62
|
+
// )
|
|
63
|
+
// }
|
|
39
64
|
}
|
|
40
65
|
|
|
41
66
|
class TreeNode {
|
|
42
67
|
constructor(keys, children) {
|
|
43
|
-
this.keys = keys
|
|
44
|
-
this.children = children
|
|
68
|
+
this.keys = new CompressedArray(keys)
|
|
69
|
+
this.children = new CompressedArray(children)
|
|
45
70
|
}
|
|
46
71
|
|
|
47
72
|
isEmpty() {
|
|
48
73
|
return this.keys.length === 0 && this.children.length === 0
|
|
49
74
|
}
|
|
50
75
|
|
|
51
|
-
|
|
76
|
+
insertLeaf(context, key, value) {
|
|
52
77
|
let s = 0
|
|
53
78
|
let e = this.keys.length
|
|
54
|
-
let c
|
|
79
|
+
let c = 0
|
|
55
80
|
|
|
56
81
|
while (s < e) {
|
|
57
82
|
const mid = (s + e) >> 1
|
|
58
|
-
const k = this.keys
|
|
83
|
+
const k = this.keys.get(mid)
|
|
59
84
|
|
|
60
85
|
c = b4a.compare(key, k.key)
|
|
61
86
|
|
|
62
|
-
if (c === 0)
|
|
63
|
-
if (b4a.equals(k.value, value)) return UNCHANGED
|
|
64
|
-
this.keys[mid] = new DataPointer(context, 0, 0, 0, true, key, value)
|
|
65
|
-
return CHANGED
|
|
66
|
-
}
|
|
87
|
+
if (c === 0) return mid
|
|
67
88
|
|
|
68
89
|
if (c < 0) e = mid
|
|
69
90
|
else s = mid + 1
|
|
70
91
|
}
|
|
71
92
|
|
|
72
93
|
const i = c < 0 ? e : s
|
|
73
|
-
this.keys.
|
|
74
|
-
if (child) this.children.splice(i + 1, 0, child)
|
|
94
|
+
this.keys.insert(i, new KeyPointer(context, 0, 0, 0, true, key, value, null))
|
|
75
95
|
|
|
76
|
-
return this.keys.length
|
|
96
|
+
return this.keys.length <= context.maxKeys ? INSERTED : NEEDS_SPLIT
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
insertNode(context, keyPointer, treePointer) {
|
|
100
|
+
let s = 0
|
|
101
|
+
let e = this.keys.length
|
|
102
|
+
let c = 0
|
|
103
|
+
|
|
104
|
+
while (s < e) {
|
|
105
|
+
const mid = (s + e) >> 1
|
|
106
|
+
const k = this.keys.get(mid)
|
|
107
|
+
|
|
108
|
+
c = b4a.compare(keyPointer.key, k.key)
|
|
109
|
+
|
|
110
|
+
if (c === 0) return mid
|
|
111
|
+
|
|
112
|
+
if (c < 0) e = mid
|
|
113
|
+
else s = mid + 1
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const i = c < 0 ? e : s
|
|
117
|
+
this.keys.insert(i, keyPointer)
|
|
118
|
+
this.children.insert(i + 1, treePointer)
|
|
119
|
+
|
|
120
|
+
return this.keys.length <= context.maxKeys ? INSERTED : NEEDS_SPLIT
|
|
77
121
|
}
|
|
78
122
|
|
|
79
123
|
setValue(context, i, value) {
|
|
80
|
-
this.keys
|
|
124
|
+
this.keys.set(i, new KeyPointer(context, 0, 0, 0, true, this.keys.get(i).key, value, null))
|
|
81
125
|
}
|
|
82
126
|
|
|
83
127
|
removeKey(i) {
|
|
84
|
-
this.keys.
|
|
128
|
+
this.keys.delete(i)
|
|
85
129
|
if (this.children.length) {
|
|
86
|
-
this.children.
|
|
130
|
+
this.children.delete(i + 1)
|
|
87
131
|
}
|
|
88
132
|
}
|
|
89
133
|
|
|
90
134
|
siblings(parent) {
|
|
91
|
-
|
|
92
|
-
if (parent.children[i].value !== this) continue // TODO: move to a seq/offset check instead
|
|
135
|
+
const pc = parent.children
|
|
93
136
|
|
|
94
|
-
|
|
95
|
-
|
|
137
|
+
for (let i = 0; i < pc.length; i++) {
|
|
138
|
+
if (pc.get(i).value !== this) continue // TODO: move to a seq/offset check instead
|
|
139
|
+
|
|
140
|
+
const left = i ? pc.get(i - 1) : null
|
|
141
|
+
const right = i < pc.length - 1 ? pc.get(i + 1) : null
|
|
96
142
|
return { left, index: i, right }
|
|
97
143
|
}
|
|
98
144
|
|
|
@@ -101,23 +147,29 @@ class TreeNode {
|
|
|
101
147
|
}
|
|
102
148
|
|
|
103
149
|
merge(node, median) {
|
|
150
|
+
const keys = node.keys
|
|
151
|
+
const children = node.children
|
|
152
|
+
|
|
104
153
|
this.keys.push(median)
|
|
105
|
-
|
|
106
|
-
for (let i = 0; i <
|
|
154
|
+
|
|
155
|
+
for (let i = 0; i < keys.length; i++) this.keys.push(keys.get(i))
|
|
156
|
+
for (let i = 0; i < children.length; i++) this.children.push(children.get(i))
|
|
107
157
|
}
|
|
108
158
|
|
|
109
159
|
split(context) {
|
|
110
160
|
const len = this.keys.length >> 1
|
|
111
161
|
const right = new TreeNodePointer(context, 0, 0, 0, true, new TreeNode([], []))
|
|
112
162
|
|
|
113
|
-
|
|
114
|
-
|
|
163
|
+
const k = []
|
|
164
|
+
while (k.length < len) k.push(this.keys.pop())
|
|
165
|
+
for (let i = k.length - 1; i >= 0; i--) right.value.keys.push(k[i])
|
|
115
166
|
|
|
116
167
|
const median = this.keys.pop()
|
|
117
168
|
|
|
118
169
|
if (this.children.length) {
|
|
119
|
-
|
|
120
|
-
|
|
170
|
+
const c = []
|
|
171
|
+
while (c.length < len + 1) c.push(this.children.pop())
|
|
172
|
+
for (let i = c.length - 1; i >= 0; i--) right.value.children.push(c[i])
|
|
121
173
|
}
|
|
122
174
|
|
|
123
175
|
return {
|
|
@@ -128,16 +180,13 @@ class TreeNode {
|
|
|
128
180
|
}
|
|
129
181
|
}
|
|
130
182
|
|
|
131
|
-
exports.
|
|
132
|
-
exports.MIN_KEYS = MIN_KEYS
|
|
133
|
-
exports.MAX_CHILDREN = MAX_CHILDREN
|
|
134
|
-
|
|
135
|
-
exports.UNCHANGED = UNCHANGED
|
|
136
|
-
exports.CHANGED = CHANGED
|
|
183
|
+
exports.INSERTED = INSERTED
|
|
137
184
|
exports.NEEDS_SPLIT = NEEDS_SPLIT
|
|
138
185
|
|
|
139
186
|
exports.TreeNodePointer = TreeNodePointer
|
|
140
187
|
exports.TreeNode = TreeNode
|
|
141
|
-
exports.
|
|
188
|
+
exports.KeyPointer = KeyPointer
|
|
189
|
+
exports.ValuePointer = ValuePointer
|
|
190
|
+
exports.Pointer = Pointer
|
|
142
191
|
|
|
143
192
|
exports.EMPTY = new TreeNodePointer(null, 0, 0, 0, false, null)
|