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/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 T = 5
4
- const MIN_KEYS = T - 1
5
- const MAX_CHILDREN = MIN_KEYS * 2 + 1
4
+ const INSERTED = -1
5
+ const NEEDS_SPLIT = -2
6
6
 
7
- const UNCHANGED = 0
8
- const CHANGED = 1
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.changed = changed
19
-
20
- this.key = key
21
- this.value = value
14
+ this.split = split
22
15
  }
23
16
  }
24
17
 
25
- class TreeNodePointer {
26
- constructor(context, core, seq, offset, changed, value) {
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
- put(context, key, value, child) {
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[mid]
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.splice(i, 0, new DataPointer(context, 0, 0, 0, true, key, value))
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 < MAX_CHILDREN ? CHANGED : NEEDS_SPLIT
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[i] = new DataPointer(context, 0, 0, 0, true, this.keys[i].key, value)
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.splice(i, 1)
128
+ this.keys.delete(i)
85
129
  if (this.children.length) {
86
- this.children.splice(i + 1, 1)
130
+ this.children.delete(i + 1)
87
131
  }
88
132
  }
89
133
 
90
134
  siblings(parent) {
91
- for (let i = 0; i < parent.children.length; i++) {
92
- if (parent.children[i].value !== this) continue // TODO: move to a seq/offset check instead
135
+ const pc = parent.children
93
136
 
94
- const left = i ? parent.children[i - 1] : null
95
- const right = i < parent.children.length - 1 ? parent.children[i + 1] : null
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
- for (let i = 0; i < node.keys.length; i++) this.keys.push(node.keys[i])
106
- for (let i = 0; i < node.children.length; i++) this.children.push(node.children[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
- while (right.value.keys.length < len) right.value.keys.push(this.keys.pop())
114
- right.value.keys.reverse()
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
- while (right.value.children.length < len + 1) right.value.children.push(this.children.pop())
120
- right.value.children.reverse()
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.T = T
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.DataPointer = DataPointer
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)