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/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(activeRequests) {
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, { activeRequests })
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 buffer = await this.core.get(checkpoint - 1)
41
- const block = decodeBlock(buffer, checkpoint - 1)
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, activeRequests) {
78
- if (core !== 0 && core - 1 >= context.cores.length) await context.update(activeRequests)
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, activeRequests) {
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(activeRequests)
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, activeRequests) {
129
- if (core !== 0 && core - 1 >= this.cores.length) await this.update(activeRequests)
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, { activeRequests })
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, activeRequests) {
175
+ async getContext(core, config) {
152
176
  if (core === 0) return this
153
- if (core > this.cores.length) await this.update(activeRequests)
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, { limit = -1, activeRequests = [] } = {}) {
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.activeRequests = activeRequests
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.activeRequests)
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.activeRequests)
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.activeRequests),
81
- this.tree.finalizeKeyPointer(rightKey, this.activeRequests)
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.activeRequests = activeRequests
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.activeRequests)
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.activeRequests)
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.activeRequests)
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.activeRequests)
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.activeRequests).catch(noop)
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
- // [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
+ [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
- // [Symbol.for('nodejs.util.inspect.custom')]() {
60
- // return (
61
- // `[TreeNodePointer core=${this.core} seq=${this.seq} offset=${this.offset} changed=${this.changed}]`
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.length === 0 && this.children.length === 0
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.length
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.get(mid)
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.length <= context.maxKeys ? INSERTED : NEEDS_SPLIT
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.length
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.get(mid)
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.length <= context.maxKeys ? INSERTED : NEEDS_SPLIT
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.get(i).key, value, null))
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.length) {
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.length; i++) {
138
- if (pc.get(i).value !== this) continue // TODO: move to a seq/offset check instead
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.get(i - 1) : null
141
- const right = i < pc.length - 1 ? pc.get(i + 1) : null
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.length; i++) this.keys.push(keys.get(i))
156
- for (let i = 0; i < children.length; i++) this.children.push(children.get(i))
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.length >> 1
161
- const right = new TreeNodePointer(context, 0, 0, 0, true, new TreeNode([], []))
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.length) {
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])