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
package/lib/ranges.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
const { Readable } = require('streamx')
|
|
2
|
+
const b4a = require('b4a')
|
|
3
|
+
|
|
4
|
+
class RangeIterator {
|
|
5
|
+
constructor(tree, { activeRequests = [], reverse = false, limit = -1, gte, gt, lte, lt } = {}) {
|
|
6
|
+
this.tree = tree
|
|
7
|
+
this.root = null
|
|
8
|
+
this.stack = []
|
|
9
|
+
this.activeRequests = activeRequests
|
|
10
|
+
this.reverse = reverse
|
|
11
|
+
this.limit = limit
|
|
12
|
+
this.start = gte || gt || null
|
|
13
|
+
this.end = lte || lt || null
|
|
14
|
+
this.inclusive = !!(reverse ? lte : gte)
|
|
15
|
+
this.compare = (reverse ? gte : lte) ? 0 : -1
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async open() {
|
|
19
|
+
if (!this.root) this.root = await this.tree.bootstrap(this.activeRequests)
|
|
20
|
+
if (!this.root) return
|
|
21
|
+
|
|
22
|
+
if (this.limit === 0) return
|
|
23
|
+
|
|
24
|
+
this.stack.push({ node: this.root, offset: 0 })
|
|
25
|
+
|
|
26
|
+
if (this.reverse ? !this.end : !this.start) return
|
|
27
|
+
|
|
28
|
+
const offset = this.inclusive ? 0 : 1
|
|
29
|
+
|
|
30
|
+
while (true) {
|
|
31
|
+
const top = this.stack[this.stack.length - 1]
|
|
32
|
+
const v = top.node.value
|
|
33
|
+
? this.tree.bump(top.node)
|
|
34
|
+
: await this.tree.inflate(top.node, this.activeRequests)
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < v.keys.length; i++) {
|
|
37
|
+
const j = this.reverse ? v.keys.length - 1 - i : i
|
|
38
|
+
|
|
39
|
+
const c = this.reverse
|
|
40
|
+
? b4a.compare(v.keys.get(j).key, this.end)
|
|
41
|
+
: b4a.compare(this.start, v.keys.get(j).key)
|
|
42
|
+
|
|
43
|
+
if (c < 0) break
|
|
44
|
+
top.offset = 2 * i + 1 + (c === 0 ? offset : 1)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const child = (top.offset & 1) === 0
|
|
48
|
+
const k = top.offset >> 1
|
|
49
|
+
|
|
50
|
+
if (!child || k >= v.children.length) break
|
|
51
|
+
|
|
52
|
+
const j = this.reverse ? v.children.length - 1 - k : k
|
|
53
|
+
|
|
54
|
+
this.stack.push({
|
|
55
|
+
node: v.children.get(j),
|
|
56
|
+
offset: 0
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
top.offset++
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
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() {
|
|
69
|
+
while (this.stack.length && (this.limit === -1 || this.limit > 0)) {
|
|
70
|
+
const top = this.stack.pop()
|
|
71
|
+
const v = top.node.value
|
|
72
|
+
? this.tree.bump(top.node)
|
|
73
|
+
: await this.tree.inflate(top.node, this.activeRequests)
|
|
74
|
+
|
|
75
|
+
const offset = top.offset++
|
|
76
|
+
const child = (offset & 1) === 0
|
|
77
|
+
const k = offset >> 1
|
|
78
|
+
|
|
79
|
+
if (child) {
|
|
80
|
+
this.stack.push(top)
|
|
81
|
+
if (k < v.children.length) {
|
|
82
|
+
const j = this.reverse ? v.children.length - 1 - k : k
|
|
83
|
+
this.stack.push({ node: v.children.get(j), offset: 0 })
|
|
84
|
+
}
|
|
85
|
+
continue
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (k < v.keys.length) {
|
|
89
|
+
const j = this.reverse ? v.keys.length - 1 - k : k
|
|
90
|
+
|
|
91
|
+
const data = top.node.value.keys.get(j)
|
|
92
|
+
|
|
93
|
+
const c = this.reverse
|
|
94
|
+
? this.start
|
|
95
|
+
? b4a.compare(this.start, data.key)
|
|
96
|
+
: -1
|
|
97
|
+
: this.end
|
|
98
|
+
? b4a.compare(data.key, this.end)
|
|
99
|
+
: -1
|
|
100
|
+
|
|
101
|
+
if (c > this.compare) break
|
|
102
|
+
|
|
103
|
+
this.stack.push(top)
|
|
104
|
+
if (this.limit !== -1) this.limit--
|
|
105
|
+
return data
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
class RangeStream extends Readable {
|
|
114
|
+
constructor(tree, options = {}) {
|
|
115
|
+
const { highWaterMark } = options
|
|
116
|
+
super({ eagerOpen: true, highWaterMark })
|
|
117
|
+
|
|
118
|
+
this.tree = tree
|
|
119
|
+
this.iterator = new RangeIterator(tree, options)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async _open(cb) {
|
|
123
|
+
try {
|
|
124
|
+
await this.iterator.open()
|
|
125
|
+
} catch (err) {
|
|
126
|
+
cb(err)
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
cb(null)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async _read(cb) {
|
|
133
|
+
try {
|
|
134
|
+
this.push(await this.iterator.next())
|
|
135
|
+
} catch (err) {
|
|
136
|
+
cb(err)
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
cb(null)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
exports.RangeIterator = RangeIterator
|
|
144
|
+
exports.RangeStream = RangeStream
|
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)
|