@frostpillar/frostpillar-btree 0.2.5 → 0.2.7
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/README-JA.md +117 -64
- package/README.md +117 -64
- package/dist/InMemoryBTree.d.ts +3 -3
- package/dist/btree/autoScale.d.ts +2 -0
- package/dist/btree/entry-lookup.d.ts +8 -0
- package/dist/btree/mutations.d.ts +1 -3
- package/dist/btree/node-ops.d.ts +15 -0
- package/dist/btree/rangeQuery.d.ts +5 -1
- package/dist/btree/rebalance-branch.d.ts +4 -0
- package/dist/btree/rebalance.d.ts +5 -2
- package/dist/btree/serialization.d.ts +3 -1
- package/dist/btree/split.d.ts +3 -0
- package/dist/btree/traversal.d.ts +7 -0
- package/dist/btree/types.d.ts +23 -26
- package/dist/{chunk-CZFRT2NN.js → chunk-UGGWGP4E.js} +823 -499
- package/dist/concurrency/ConcurrentInMemoryBTree.d.ts +22 -29
- package/dist/concurrency/coordinator.d.ts +21 -0
- package/dist/concurrency/helpers.d.ts +8 -2
- package/dist/concurrency/syncLogValidation.d.ts +2 -0
- package/dist/concurrency/types.d.ts +14 -1
- package/dist/concurrency/writeOps.d.ts +48 -0
- package/dist/core.cjs +823 -499
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1 -1
- package/dist/errors.d.ts +3 -0
- package/dist/frostpillar-btree-core.min.js +1 -1
- package/dist/frostpillar-btree.min.js +1 -1
- package/dist/index.cjs +1212 -670
- package/dist/index.d.ts +1 -1
- package/dist/index.js +397 -179
- package/package.json +1 -1
|
@@ -21,54 +21,10 @@ var BTreeConcurrencyError = class extends Error {
|
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
// src/btree/
|
|
25
|
-
var DEFAULT_MAX_LEAF_ENTRIES = 64;
|
|
26
|
-
var DEFAULT_MAX_BRANCH_CHILDREN = 64;
|
|
27
|
-
var MIN_NODE_CAPACITY = 3;
|
|
28
|
-
var MAX_NODE_CAPACITY = 16384;
|
|
29
|
-
var NODE_LEAF = 0;
|
|
30
|
-
var NODE_BRANCH = 1;
|
|
31
|
-
var normalizeDuplicateKeyPolicy = (value) => {
|
|
32
|
-
if (value === void 0) {
|
|
33
|
-
return "replace";
|
|
34
|
-
}
|
|
35
|
-
if (value !== "allow" && value !== "reject" && value !== "replace") {
|
|
36
|
-
throw new BTreeValidationError(
|
|
37
|
-
`Invalid duplicateKeys option.`
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
return value;
|
|
41
|
-
};
|
|
42
|
-
var isLeafNode = (node) => {
|
|
43
|
-
return node.kind === NODE_LEAF;
|
|
44
|
-
};
|
|
45
|
-
var writeMinKeyTo = (node, target) => {
|
|
46
|
-
if (node.kind === NODE_LEAF) {
|
|
47
|
-
if (node.entryOffset >= node.entries.length) return false;
|
|
48
|
-
const e = node.entries[node.entryOffset];
|
|
49
|
-
target.key = e.key;
|
|
50
|
-
target.sequence = e.entryId;
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
if (node.childOffset >= node.keys.length) return false;
|
|
54
|
-
target.key = node.keys[node.childOffset].key;
|
|
55
|
-
target.sequence = node.keys[node.childOffset].sequence;
|
|
56
|
-
return true;
|
|
57
|
-
};
|
|
58
|
-
var normalizeNodeCapacity = (value, field, defaultValue) => {
|
|
59
|
-
if (value === void 0) {
|
|
60
|
-
return defaultValue;
|
|
61
|
-
}
|
|
62
|
-
if (!Number.isInteger(value) || value < MIN_NODE_CAPACITY || value > MAX_NODE_CAPACITY) {
|
|
63
|
-
throw new BTreeValidationError(
|
|
64
|
-
`${field}: integer ${MIN_NODE_CAPACITY}\u2013${MAX_NODE_CAPACITY} required.`
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
return value;
|
|
68
|
-
};
|
|
24
|
+
// src/btree/node-ops.ts
|
|
69
25
|
var createLeafNode = (entries, parent) => {
|
|
70
26
|
return {
|
|
71
|
-
kind:
|
|
27
|
+
kind: 0,
|
|
72
28
|
entries,
|
|
73
29
|
entryOffset: 0,
|
|
74
30
|
parent,
|
|
@@ -80,7 +36,7 @@ var createLeafNode = (entries, parent) => {
|
|
|
80
36
|
var createBranchNode = (children, parent) => {
|
|
81
37
|
const keys = [];
|
|
82
38
|
const branch = {
|
|
83
|
-
kind:
|
|
39
|
+
kind: 1,
|
|
84
40
|
children,
|
|
85
41
|
keys,
|
|
86
42
|
childOffset: 0,
|
|
@@ -91,11 +47,12 @@ var createBranchNode = (children, parent) => {
|
|
|
91
47
|
const child = children[i];
|
|
92
48
|
child.parent = branch;
|
|
93
49
|
child.indexInParent = i;
|
|
94
|
-
const target = {
|
|
50
|
+
const target = {
|
|
51
|
+
key: void 0,
|
|
52
|
+
sequence: 0
|
|
53
|
+
};
|
|
95
54
|
if (!writeMinKeyTo(child, target)) {
|
|
96
|
-
throw new BTreeInvariantError(
|
|
97
|
-
"branch child has no min key"
|
|
98
|
-
);
|
|
55
|
+
throw new BTreeInvariantError("branch child has no min key");
|
|
99
56
|
}
|
|
100
57
|
keys.push(target);
|
|
101
58
|
}
|
|
@@ -149,7 +106,14 @@ var leafInsertAt = (leaf, logicalIndex, entry) => {
|
|
|
149
106
|
leaf.entryOffset -= 1;
|
|
150
107
|
leaf.entries[phys - 1] = entry;
|
|
151
108
|
} else {
|
|
152
|
-
leaf.entries.
|
|
109
|
+
const len = leaf.entries.length;
|
|
110
|
+
if (phys >= len) {
|
|
111
|
+
leaf.entries.push(entry);
|
|
112
|
+
} else {
|
|
113
|
+
leaf.entries.push(leaf.entries[len - 1]);
|
|
114
|
+
leaf.entries.copyWithin(phys + 1, phys, len);
|
|
115
|
+
leaf.entries[phys] = entry;
|
|
116
|
+
}
|
|
153
117
|
}
|
|
154
118
|
};
|
|
155
119
|
var leafCompact = (leaf) => {
|
|
@@ -178,7 +142,11 @@ var branchInsertAt = (branch, logicalIndex, child, key) => {
|
|
|
178
142
|
const phys = branch.childOffset + logicalIndex;
|
|
179
143
|
const count = branch.children.length - branch.childOffset;
|
|
180
144
|
if (branch.childOffset > 0 && logicalIndex < count >>> 1) {
|
|
181
|
-
branch.children.copyWithin(
|
|
145
|
+
branch.children.copyWithin(
|
|
146
|
+
branch.childOffset - 1,
|
|
147
|
+
branch.childOffset,
|
|
148
|
+
phys
|
|
149
|
+
);
|
|
182
150
|
branch.keys.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
|
|
183
151
|
branch.childOffset -= 1;
|
|
184
152
|
branch.children[phys - 1] = child;
|
|
@@ -199,8 +167,16 @@ var branchRemoveAt = (branch, physIndex) => {
|
|
|
199
167
|
const logicalIndex = physIndex - branch.childOffset;
|
|
200
168
|
const count = branch.children.length - branch.childOffset;
|
|
201
169
|
if (logicalIndex < count - 1 - logicalIndex) {
|
|
202
|
-
branch.children.copyWithin(
|
|
203
|
-
|
|
170
|
+
branch.children.copyWithin(
|
|
171
|
+
branch.childOffset + 1,
|
|
172
|
+
branch.childOffset,
|
|
173
|
+
physIndex
|
|
174
|
+
);
|
|
175
|
+
branch.keys.copyWithin(
|
|
176
|
+
branch.childOffset + 1,
|
|
177
|
+
branch.childOffset,
|
|
178
|
+
physIndex
|
|
179
|
+
);
|
|
204
180
|
branch.childOffset += 1;
|
|
205
181
|
for (let i = branch.childOffset; i <= physIndex; i += 1) {
|
|
206
182
|
branch.children[i].indexInParent = i;
|
|
@@ -219,6 +195,60 @@ var branchRemoveAt = (branch, physIndex) => {
|
|
|
219
195
|
}
|
|
220
196
|
};
|
|
221
197
|
|
|
198
|
+
// src/btree/types.ts
|
|
199
|
+
var DEFAULT_MAX_LEAF_ENTRIES = 64;
|
|
200
|
+
var DEFAULT_MAX_BRANCH_CHILDREN = 64;
|
|
201
|
+
var MIN_NODE_CAPACITY = 3;
|
|
202
|
+
var MAX_NODE_CAPACITY = 16384;
|
|
203
|
+
var NODE_LEAF = 0;
|
|
204
|
+
var normalizeDuplicateKeyPolicy = (value) => {
|
|
205
|
+
if (value === void 0) {
|
|
206
|
+
return "replace";
|
|
207
|
+
}
|
|
208
|
+
if (value !== "allow" && value !== "reject" && value !== "replace") {
|
|
209
|
+
throw new BTreeValidationError(`Invalid duplicateKeys option.`);
|
|
210
|
+
}
|
|
211
|
+
return value;
|
|
212
|
+
};
|
|
213
|
+
var normalizeDeleteRebalancePolicy = (value) => {
|
|
214
|
+
if (value === void 0) {
|
|
215
|
+
return "standard";
|
|
216
|
+
}
|
|
217
|
+
if (value !== "standard" && value !== "lazy") {
|
|
218
|
+
throw new BTreeValidationError(`Invalid deleteRebalancePolicy option.`);
|
|
219
|
+
}
|
|
220
|
+
return value;
|
|
221
|
+
};
|
|
222
|
+
var freezeEntry = (entry) => Object.freeze(entry);
|
|
223
|
+
var createEntry = (key, entryId, value) => Object.freeze({ key, entryId, value });
|
|
224
|
+
var isLeafNode = (node) => {
|
|
225
|
+
return node.kind === NODE_LEAF;
|
|
226
|
+
};
|
|
227
|
+
var writeMinKeyTo = (node, target) => {
|
|
228
|
+
if (node.kind === NODE_LEAF) {
|
|
229
|
+
if (node.entryOffset >= node.entries.length) return false;
|
|
230
|
+
const e = node.entries[node.entryOffset];
|
|
231
|
+
target.key = e.key;
|
|
232
|
+
target.sequence = e.entryId;
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
if (node.childOffset >= node.keys.length) return false;
|
|
236
|
+
target.key = node.keys[node.childOffset].key;
|
|
237
|
+
target.sequence = node.keys[node.childOffset].sequence;
|
|
238
|
+
return true;
|
|
239
|
+
};
|
|
240
|
+
var normalizeNodeCapacity = (value, field, defaultValue) => {
|
|
241
|
+
if (value === void 0) {
|
|
242
|
+
return defaultValue;
|
|
243
|
+
}
|
|
244
|
+
if (!Number.isInteger(value) || value < MIN_NODE_CAPACITY || value > MAX_NODE_CAPACITY) {
|
|
245
|
+
throw new BTreeValidationError(
|
|
246
|
+
`${field}: integer ${MIN_NODE_CAPACITY}\u2013${MAX_NODE_CAPACITY} required.`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
return value;
|
|
250
|
+
};
|
|
251
|
+
|
|
222
252
|
// src/btree/navigation.ts
|
|
223
253
|
var selectBranchChild = (compare, branch, userKey, sequence) => {
|
|
224
254
|
const off = branch.childOffset;
|
|
@@ -232,7 +262,7 @@ var selectBranchChild = (compare, branch, userKey, sequence) => {
|
|
|
232
262
|
const mid = lower + upper >>> 1;
|
|
233
263
|
const k = branch.keys[mid];
|
|
234
264
|
const cmp = compare(k.key, userKey);
|
|
235
|
-
if ((cmp !== 0 ? cmp : k.sequence - sequence) <= 0) {
|
|
265
|
+
if ((cmp !== 0 ? cmp : k.sequence < sequence ? -1 : k.sequence > sequence ? 1 : 0) <= 0) {
|
|
236
266
|
selectedIndex = mid;
|
|
237
267
|
lower = mid + 1;
|
|
238
268
|
} else {
|
|
@@ -257,7 +287,7 @@ var lowerBoundInLeaf = (state, leaf, userKey, sequence) => {
|
|
|
257
287
|
const mid = lower + upper >>> 1;
|
|
258
288
|
const e = leaf.entries[mid];
|
|
259
289
|
const cmp = compare(e.key, userKey);
|
|
260
|
-
if ((cmp !== 0 ? cmp : e.entryId - sequence) < 0) {
|
|
290
|
+
if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) < 0) {
|
|
261
291
|
lower = mid + 1;
|
|
262
292
|
} else {
|
|
263
293
|
upper = mid;
|
|
@@ -273,7 +303,7 @@ var upperBoundInLeaf = (state, leaf, userKey, sequence) => {
|
|
|
273
303
|
const mid = lower + upper >>> 1;
|
|
274
304
|
const e = leaf.entries[mid];
|
|
275
305
|
const cmp = compare(e.key, userKey);
|
|
276
|
-
if ((cmp !== 0 ? cmp : e.entryId - sequence) <= 0) {
|
|
306
|
+
if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) <= 0) {
|
|
277
307
|
lower = mid + 1;
|
|
278
308
|
} else {
|
|
279
309
|
upper = mid;
|
|
@@ -340,7 +370,11 @@ var hasKeyEntry = (state, key) => {
|
|
|
340
370
|
var findNextHigherKey = (state, key) => {
|
|
341
371
|
if (state.entryCount === 0) return null;
|
|
342
372
|
const compare = state.compareKeys;
|
|
343
|
-
let leaf = findLeafForKey(
|
|
373
|
+
let leaf = findLeafForKey(
|
|
374
|
+
state,
|
|
375
|
+
key,
|
|
376
|
+
Number.MAX_SAFE_INTEGER
|
|
377
|
+
);
|
|
344
378
|
let idx = upperBoundInLeaf(state, leaf, key, Number.MAX_SAFE_INTEGER);
|
|
345
379
|
while (leaf !== null) {
|
|
346
380
|
if (idx < leafEntryCount(leaf)) {
|
|
@@ -410,7 +444,175 @@ var findPairOrNextLower = (state, key) => {
|
|
|
410
444
|
return null;
|
|
411
445
|
};
|
|
412
446
|
|
|
413
|
-
// src/btree/
|
|
447
|
+
// src/btree/rangeQuery.ts
|
|
448
|
+
function isEmptyRange(compare, startKey, endKey, options) {
|
|
449
|
+
const cmp = compare(startKey, endKey);
|
|
450
|
+
if (cmp > 0) return true;
|
|
451
|
+
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
452
|
+
const upperExclusive = options?.upperBound === "exclusive";
|
|
453
|
+
return lowerExclusive && upperExclusive && cmp === 0;
|
|
454
|
+
}
|
|
455
|
+
var initRangeCursor = (state, startKey, endKey, options) => {
|
|
456
|
+
if (state.entryCount === 0) return null;
|
|
457
|
+
const compare = state.compareKeys;
|
|
458
|
+
if (isEmptyRange(compare, startKey, endKey, options)) return null;
|
|
459
|
+
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
460
|
+
const upperExclusive = options?.upperBound === "exclusive";
|
|
461
|
+
const startSeq = lowerExclusive ? Number.MAX_SAFE_INTEGER : 0;
|
|
462
|
+
const leaf = findLeafForKey(state, startKey, startSeq);
|
|
463
|
+
const index = lowerExclusive ? upperBoundInLeaf(state, leaf, startKey, Number.MAX_SAFE_INTEGER) : lowerBoundInLeaf(state, leaf, startKey, 0);
|
|
464
|
+
return { leaf, index, compare, upperExclusive };
|
|
465
|
+
};
|
|
466
|
+
var countRangeEntries = (state, startKey, endKey, options) => {
|
|
467
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
468
|
+
if (cursor === null) return 0;
|
|
469
|
+
let cursorLeaf = cursor.leaf;
|
|
470
|
+
let cursorIndex = cursor.index;
|
|
471
|
+
const { compare, upperExclusive } = cursor;
|
|
472
|
+
let count = 0;
|
|
473
|
+
while (cursorLeaf !== null) {
|
|
474
|
+
const leafCount = leafEntryCount(cursorLeaf);
|
|
475
|
+
if (cursorIndex >= leafCount) {
|
|
476
|
+
cursorLeaf = cursorLeaf.next;
|
|
477
|
+
cursorIndex = 0;
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
481
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
482
|
+
count += leafCount - cursorIndex;
|
|
483
|
+
cursorLeaf = cursorLeaf.next;
|
|
484
|
+
cursorIndex = 0;
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
487
|
+
count += findBoundaryEnd(state, cursorLeaf, endKey, upperExclusive, leafCount) - cursorIndex;
|
|
488
|
+
return count;
|
|
489
|
+
}
|
|
490
|
+
return count;
|
|
491
|
+
};
|
|
492
|
+
var isLastEntryInRange = (lastKey, endKey, compare, upperExclusive) => {
|
|
493
|
+
const cmp = compare(lastKey, endKey);
|
|
494
|
+
return upperExclusive ? cmp < 0 : cmp <= 0;
|
|
495
|
+
};
|
|
496
|
+
var findBoundaryEnd = (state, leaf, endKey, upperExclusive, leafCount) => {
|
|
497
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
498
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
|
|
499
|
+
return endBound < leafCount ? endBound : leafCount;
|
|
500
|
+
};
|
|
501
|
+
var RANGE_PREALLOC_THRESHOLD = 200;
|
|
502
|
+
var allocateRangeOutput = (state, cursor, startKey, endKey, options) => {
|
|
503
|
+
const firstLeafCount = leafEntryCount(cursor.leaf);
|
|
504
|
+
const firstLeafRemainder = firstLeafCount - cursor.index;
|
|
505
|
+
if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursor.leaf.next !== null) {
|
|
506
|
+
const lastKey = leafEntryAt(cursor.leaf, firstLeafCount - 1).key;
|
|
507
|
+
if (isLastEntryInRange(lastKey, endKey, cursor.compare, cursor.upperExclusive)) {
|
|
508
|
+
return new Array(
|
|
509
|
+
countRangeEntries(state, startKey, endKey, options)
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return [];
|
|
514
|
+
};
|
|
515
|
+
var appendLeafSlicePublic = (leaf, from, to, output, useIndexed, writeIdx) => {
|
|
516
|
+
if (useIndexed) {
|
|
517
|
+
for (let i = from; i < to; i += 1) {
|
|
518
|
+
output[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
|
|
519
|
+
}
|
|
520
|
+
} else {
|
|
521
|
+
for (let i = from; i < to; i += 1) {
|
|
522
|
+
output.push(freezeEntry(leafEntryAt(leaf, i)));
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return writeIdx;
|
|
526
|
+
};
|
|
527
|
+
var collectPublicEntries = (state, cursor, endKey, output) => {
|
|
528
|
+
const { compare, upperExclusive } = cursor;
|
|
529
|
+
let cursorLeaf = cursor.leaf;
|
|
530
|
+
let cursorIndex = cursor.index;
|
|
531
|
+
let writeIdx = 0;
|
|
532
|
+
const useIndexed = output.length > 0;
|
|
533
|
+
while (cursorLeaf !== null) {
|
|
534
|
+
const leafCount = leafEntryCount(cursorLeaf);
|
|
535
|
+
if (cursorIndex >= leafCount) {
|
|
536
|
+
cursorLeaf = cursorLeaf.next;
|
|
537
|
+
cursorIndex = 0;
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
541
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
542
|
+
writeIdx = appendLeafSlicePublic(
|
|
543
|
+
cursorLeaf,
|
|
544
|
+
cursorIndex,
|
|
545
|
+
leafCount,
|
|
546
|
+
output,
|
|
547
|
+
useIndexed,
|
|
548
|
+
writeIdx
|
|
549
|
+
);
|
|
550
|
+
cursorLeaf = cursorLeaf.next;
|
|
551
|
+
cursorIndex = 0;
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
554
|
+
const limit = findBoundaryEnd(
|
|
555
|
+
state,
|
|
556
|
+
cursorLeaf,
|
|
557
|
+
endKey,
|
|
558
|
+
upperExclusive,
|
|
559
|
+
leafCount
|
|
560
|
+
);
|
|
561
|
+
appendLeafSlicePublic(
|
|
562
|
+
cursorLeaf,
|
|
563
|
+
cursorIndex,
|
|
564
|
+
limit,
|
|
565
|
+
output,
|
|
566
|
+
useIndexed,
|
|
567
|
+
writeIdx
|
|
568
|
+
);
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
var rangeQueryPublicEntries = (state, startKey, endKey, options) => {
|
|
573
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
574
|
+
if (cursor === null) return [];
|
|
575
|
+
const output = allocateRangeOutput(state, cursor, startKey, endKey, options);
|
|
576
|
+
collectPublicEntries(state, cursor, endKey, output);
|
|
577
|
+
return output;
|
|
578
|
+
};
|
|
579
|
+
var forEachRangeEntries = (state, startKey, endKey, callback, options) => {
|
|
580
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
581
|
+
if (cursor === null) return;
|
|
582
|
+
let cursorLeaf = cursor.leaf;
|
|
583
|
+
let cursorIndex = cursor.index;
|
|
584
|
+
const { compare, upperExclusive } = cursor;
|
|
585
|
+
while (cursorLeaf !== null) {
|
|
586
|
+
const leafCount = leafEntryCount(cursorLeaf);
|
|
587
|
+
if (cursorIndex >= leafCount) {
|
|
588
|
+
cursorLeaf = cursorLeaf.next;
|
|
589
|
+
cursorIndex = 0;
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
593
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
594
|
+
for (let i = cursorIndex; i < leafCount; i += 1) {
|
|
595
|
+
callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
|
|
596
|
+
}
|
|
597
|
+
cursorLeaf = cursorLeaf.next;
|
|
598
|
+
cursorIndex = 0;
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
const limit = findBoundaryEnd(
|
|
602
|
+
state,
|
|
603
|
+
cursorLeaf,
|
|
604
|
+
endKey,
|
|
605
|
+
upperExclusive,
|
|
606
|
+
leafCount
|
|
607
|
+
);
|
|
608
|
+
for (let i = cursorIndex; i < limit; i += 1) {
|
|
609
|
+
callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
|
|
610
|
+
}
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
|
|
615
|
+
// src/btree/rebalance-branch.ts
|
|
414
616
|
var updateMinKeyInAncestors = (node) => {
|
|
415
617
|
let current = node;
|
|
416
618
|
while (current.parent !== null) {
|
|
@@ -426,12 +628,9 @@ var requireParent = (node) => {
|
|
|
426
628
|
}
|
|
427
629
|
return node.parent;
|
|
428
630
|
};
|
|
429
|
-
var requireLeafNode = (node) => {
|
|
430
|
-
if (!isLeafNode(node)) throw new BTreeInvariantError("expected leaf, got branch");
|
|
431
|
-
return node;
|
|
432
|
-
};
|
|
433
631
|
var requireBranchNode = (node) => {
|
|
434
|
-
if (isLeafNode(node))
|
|
632
|
+
if (isLeafNode(node))
|
|
633
|
+
throw new BTreeInvariantError("expected branch, got leaf");
|
|
435
634
|
return node;
|
|
436
635
|
};
|
|
437
636
|
var removeChildFromBranch = (branch, childIndex) => {
|
|
@@ -440,27 +639,18 @@ var removeChildFromBranch = (branch, childIndex) => {
|
|
|
440
639
|
}
|
|
441
640
|
branchRemoveAt(branch, childIndex);
|
|
442
641
|
};
|
|
443
|
-
var detachLeafFromChain = (state, leaf) => {
|
|
444
|
-
if (leaf.prev !== null) {
|
|
445
|
-
leaf.prev.next = leaf.next;
|
|
446
|
-
} else if (leaf.next !== null) {
|
|
447
|
-
state.leftmostLeaf = leaf.next;
|
|
448
|
-
}
|
|
449
|
-
if (leaf.next !== null) {
|
|
450
|
-
leaf.next.prev = leaf.prev;
|
|
451
|
-
} else if (leaf.prev !== null) {
|
|
452
|
-
state.rightmostLeaf = leaf.prev;
|
|
453
|
-
}
|
|
454
|
-
leaf.prev = null;
|
|
455
|
-
leaf.next = null;
|
|
456
|
-
};
|
|
457
642
|
var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
|
|
458
643
|
const borrowedChild = leftSibling.children.pop();
|
|
459
|
-
if (borrowedChild === void 0)
|
|
644
|
+
if (borrowedChild === void 0)
|
|
645
|
+
throw new BTreeInvariantError("left branch borrow failed");
|
|
460
646
|
leftSibling.keys.pop();
|
|
461
647
|
borrowedChild.parent = branch;
|
|
462
|
-
const borrowedMinKey = {
|
|
463
|
-
|
|
648
|
+
const borrowedMinKey = {
|
|
649
|
+
key: void 0,
|
|
650
|
+
sequence: 0
|
|
651
|
+
};
|
|
652
|
+
if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
|
|
653
|
+
throw new BTreeInvariantError("borrowed child has no min key");
|
|
464
654
|
if (branch.childOffset > 0) {
|
|
465
655
|
branch.childOffset -= 1;
|
|
466
656
|
branch.children[branch.childOffset] = borrowedChild;
|
|
@@ -469,15 +659,20 @@ var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
|
|
|
469
659
|
} else {
|
|
470
660
|
branch.children.unshift(borrowedChild);
|
|
471
661
|
branch.keys.unshift(borrowedMinKey);
|
|
472
|
-
for (let i = 0; i < branch.children.length; i += 1)
|
|
662
|
+
for (let i = 0; i < branch.children.length; i += 1)
|
|
663
|
+
branch.children[i].indexInParent = i;
|
|
473
664
|
}
|
|
474
665
|
const parent = requireParent(branch);
|
|
475
|
-
parent.keys[branchIndex] = {
|
|
666
|
+
parent.keys[branchIndex] = {
|
|
667
|
+
key: borrowedMinKey.key,
|
|
668
|
+
sequence: borrowedMinKey.sequence
|
|
669
|
+
};
|
|
476
670
|
updateMinKeyInAncestors(branch);
|
|
477
671
|
};
|
|
478
672
|
var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
|
|
479
673
|
const shiftIdx = rightSibling.childOffset;
|
|
480
|
-
if (shiftIdx >= rightSibling.children.length)
|
|
674
|
+
if (shiftIdx >= rightSibling.children.length)
|
|
675
|
+
throw new BTreeInvariantError("right branch borrow failed");
|
|
481
676
|
const borrowedChild = rightSibling.children[shiftIdx];
|
|
482
677
|
rightSibling.childOffset += 1;
|
|
483
678
|
if (rightSibling.childOffset >= rightSibling.children.length >>> 1) {
|
|
@@ -485,8 +680,12 @@ var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
|
|
|
485
680
|
}
|
|
486
681
|
branch.children.push(borrowedChild);
|
|
487
682
|
borrowedChild.parent = branch;
|
|
488
|
-
const borrowedMinKey = {
|
|
489
|
-
|
|
683
|
+
const borrowedMinKey = {
|
|
684
|
+
key: void 0,
|
|
685
|
+
sequence: 0
|
|
686
|
+
};
|
|
687
|
+
if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
|
|
688
|
+
throw new BTreeInvariantError("borrowed child has no min key");
|
|
490
689
|
branch.keys.push(borrowedMinKey);
|
|
491
690
|
borrowedChild.indexInParent = branch.children.length - 1;
|
|
492
691
|
const parent = requireParent(branch);
|
|
@@ -539,7 +738,10 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
|
|
|
539
738
|
const parent = branch.parent;
|
|
540
739
|
if (parent === null) throw new BTreeInvariantError("branch has no parent");
|
|
541
740
|
const branchIndex = branch.indexInParent;
|
|
542
|
-
const { left: leftSibling, right: rightSibling } = findBranchSiblings(
|
|
741
|
+
const { left: leftSibling, right: rightSibling } = findBranchSiblings(
|
|
742
|
+
parent,
|
|
743
|
+
branchIndex
|
|
744
|
+
);
|
|
543
745
|
if (rightSibling !== null && branchChildCount(rightSibling) > state.minBranchChildren) {
|
|
544
746
|
borrowFromRightBranch(branch, rightSibling, branchIndex);
|
|
545
747
|
return;
|
|
@@ -558,44 +760,68 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
|
|
|
558
760
|
}
|
|
559
761
|
throw new BTreeInvariantError("no branch siblings to rebalance");
|
|
560
762
|
};
|
|
763
|
+
|
|
764
|
+
// src/btree/rebalance.ts
|
|
765
|
+
var requireLeafNode = (node) => {
|
|
766
|
+
if (!isLeafNode(node))
|
|
767
|
+
throw new BTreeInvariantError("expected leaf, got branch");
|
|
768
|
+
return node;
|
|
769
|
+
};
|
|
770
|
+
var detachLeafFromChain = (state, leaf) => {
|
|
771
|
+
if (leaf.prev !== null) {
|
|
772
|
+
leaf.prev.next = leaf.next;
|
|
773
|
+
} else if (leaf.next !== null) {
|
|
774
|
+
state.leftmostLeaf = leaf.next;
|
|
775
|
+
}
|
|
776
|
+
if (leaf.next !== null) {
|
|
777
|
+
leaf.next.prev = leaf.prev;
|
|
778
|
+
} else if (leaf.prev !== null) {
|
|
779
|
+
state.rightmostLeaf = leaf.prev;
|
|
780
|
+
}
|
|
781
|
+
leaf.prev = null;
|
|
782
|
+
leaf.next = null;
|
|
783
|
+
};
|
|
561
784
|
var mergeLeafEntries = (target, source) => {
|
|
562
785
|
if (target.entryOffset > 0) {
|
|
563
786
|
target.entries.copyWithin(0, target.entryOffset);
|
|
564
787
|
target.entries.length = target.entries.length - target.entryOffset;
|
|
565
788
|
target.entryOffset = 0;
|
|
566
789
|
}
|
|
567
|
-
|
|
568
|
-
for (let i = source.entryOffset; i < src.length; i += 1) target.entries.push(src[i]);
|
|
790
|
+
target.entries.push(...source.entries.slice(source.entryOffset));
|
|
569
791
|
};
|
|
570
|
-
var
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
state.rightmostLeaf = leaf;
|
|
575
|
-
}
|
|
576
|
-
return;
|
|
792
|
+
var applyLazyThreshold = (min) => Math.max(1, Math.ceil(min / 4));
|
|
793
|
+
var leafRebalanceThreshold = (state) => {
|
|
794
|
+
if (state.deleteRebalancePolicy === "lazy") {
|
|
795
|
+
return applyLazyThreshold(state.minLeafEntries);
|
|
577
796
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
const
|
|
582
|
-
const
|
|
583
|
-
|
|
797
|
+
return state.minLeafEntries;
|
|
798
|
+
};
|
|
799
|
+
var findLeafSiblings = (parent, leafIndex) => {
|
|
800
|
+
const left = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
|
|
801
|
+
const right = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
|
|
802
|
+
return { left, right };
|
|
803
|
+
};
|
|
804
|
+
var tryBorrowFromLeafSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
|
|
584
805
|
if (rightSibling !== null && leafEntryCount(rightSibling) > state.minLeafEntries) {
|
|
585
806
|
const borrowed = leafShiftEntry(rightSibling);
|
|
586
|
-
if (borrowed === void 0)
|
|
807
|
+
if (borrowed === void 0)
|
|
808
|
+
throw new BTreeInvariantError("right leaf borrow failed");
|
|
587
809
|
leaf.entries.push(borrowed);
|
|
588
810
|
writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
|
|
589
|
-
return;
|
|
811
|
+
return true;
|
|
590
812
|
}
|
|
591
813
|
if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
|
|
592
814
|
const borrowed = leftSibling.entries.pop();
|
|
593
|
-
if (borrowed === void 0)
|
|
815
|
+
if (borrowed === void 0)
|
|
816
|
+
throw new BTreeInvariantError("left leaf borrow failed");
|
|
594
817
|
leafUnshiftEntry(leaf, borrowed);
|
|
595
818
|
parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
|
|
596
819
|
updateMinKeyInAncestors(leaf);
|
|
597
|
-
return;
|
|
820
|
+
return true;
|
|
598
821
|
}
|
|
822
|
+
return false;
|
|
823
|
+
};
|
|
824
|
+
var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
|
|
599
825
|
if (leftSibling !== null) {
|
|
600
826
|
mergeLeafEntries(leftSibling, leaf);
|
|
601
827
|
detachLeafFromChain(state, leaf);
|
|
@@ -612,6 +838,24 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
|
612
838
|
}
|
|
613
839
|
throw new BTreeInvariantError("no leaf siblings to rebalance");
|
|
614
840
|
};
|
|
841
|
+
var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
842
|
+
if (leaf === state.root) {
|
|
843
|
+
if (state.entryCount === 0) {
|
|
844
|
+
state.leftmostLeaf = leaf;
|
|
845
|
+
state.rightmostLeaf = leaf;
|
|
846
|
+
}
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
|
|
850
|
+
const parent = leaf.parent;
|
|
851
|
+
if (parent === null)
|
|
852
|
+
throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
|
|
853
|
+
const leafIndex = leaf.indexInParent;
|
|
854
|
+
const { left, right } = findLeafSiblings(parent, leafIndex);
|
|
855
|
+
if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
|
|
856
|
+
return;
|
|
857
|
+
mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
|
|
858
|
+
};
|
|
615
859
|
|
|
616
860
|
// src/btree/deleteRange.ts
|
|
617
861
|
var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
@@ -624,27 +868,18 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
|
624
868
|
}
|
|
625
869
|
return { leaf, idx };
|
|
626
870
|
};
|
|
627
|
-
var findRemoveEnd = (state, leaf,
|
|
871
|
+
var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
|
|
628
872
|
const count = leafEntryCount(leaf);
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
|
|
634
|
-
removeEnd += 1;
|
|
635
|
-
}
|
|
636
|
-
return removeEnd;
|
|
637
|
-
};
|
|
638
|
-
var computeTreeHeight = (state) => {
|
|
639
|
-
let h = 0;
|
|
640
|
-
let n = state.root;
|
|
641
|
-
while (!isLeafNode(n)) {
|
|
642
|
-
n = n.children[n.childOffset];
|
|
643
|
-
h += 1;
|
|
873
|
+
const lastEntry = leafEntryAt(leaf, count - 1);
|
|
874
|
+
const cmpLast = state.compareKeys(lastEntry.key, endKey);
|
|
875
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
876
|
+
return count;
|
|
644
877
|
}
|
|
645
|
-
|
|
878
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
879
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
|
|
880
|
+
return endBound < count ? endBound : count;
|
|
646
881
|
};
|
|
647
|
-
var spliceLeafAndRebalance = (state, leaf, idx, removeCount
|
|
882
|
+
var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
648
883
|
if (state.entryKeys !== null) {
|
|
649
884
|
for (let i = idx; i < idx + removeCount; i += 1) {
|
|
650
885
|
state.entryKeys.delete(leafEntryAt(leaf, i).entryId);
|
|
@@ -659,11 +894,13 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
|
|
|
659
894
|
updateMinKeyInAncestors(leaf);
|
|
660
895
|
}
|
|
661
896
|
const countAfterSplice = leafEntryCount(leaf);
|
|
662
|
-
|
|
663
|
-
|
|
897
|
+
const rebalThreshold = leafRebalanceThreshold(state);
|
|
898
|
+
let safetyGuard = state.minLeafEntries + 4;
|
|
899
|
+
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
|
|
664
900
|
rebalanceAfterLeafRemoval(state, leaf);
|
|
665
|
-
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
666
|
-
|
|
901
|
+
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
902
|
+
break;
|
|
903
|
+
safetyGuard -= 1;
|
|
667
904
|
}
|
|
668
905
|
if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
|
|
669
906
|
updateMinKeyInAncestors(leaf);
|
|
@@ -673,12 +910,9 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
|
|
|
673
910
|
var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
|
|
674
911
|
var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
675
912
|
if (state.entryCount === 0) return 0;
|
|
676
|
-
|
|
677
|
-
if (boundCompared > 0) return 0;
|
|
913
|
+
if (isEmptyRange(state.compareKeys, startKey, endKey, options)) return 0;
|
|
678
914
|
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
679
915
|
const upperExclusive = options?.upperBound === "exclusive";
|
|
680
|
-
if (lowerExclusive && upperExclusive && boundCompared === 0) return 0;
|
|
681
|
-
const treeHeight = computeTreeHeight(state);
|
|
682
916
|
let deleted = 0;
|
|
683
917
|
let needsNavigate = true;
|
|
684
918
|
let leaf = null;
|
|
@@ -693,10 +927,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
693
927
|
}
|
|
694
928
|
if (idx >= leafEntryCount(leaf)) break;
|
|
695
929
|
const count = leafEntryCount(leaf);
|
|
696
|
-
const removeEnd = findRemoveEnd(state, leaf,
|
|
930
|
+
const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
|
|
697
931
|
const removeCount = removeEnd - idx;
|
|
698
932
|
if (removeCount === 0) break;
|
|
699
|
-
const countAfterSplice = spliceLeafAndRebalance(
|
|
933
|
+
const countAfterSplice = spliceLeafAndRebalance(
|
|
934
|
+
state,
|
|
935
|
+
leaf,
|
|
936
|
+
idx,
|
|
937
|
+
removeCount
|
|
938
|
+
);
|
|
700
939
|
deleted += removeCount;
|
|
701
940
|
if (removeEnd < count) break;
|
|
702
941
|
if (!isLeafStillValid(state, leaf)) {
|
|
@@ -715,6 +954,7 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
715
954
|
};
|
|
716
955
|
|
|
717
956
|
// src/btree/autoScale.ts
|
|
957
|
+
var minOccupancy = (max) => Math.ceil(max / 2);
|
|
718
958
|
var AUTO_SCALE_TIERS = [
|
|
719
959
|
{ threshold: 0, maxLeaf: 32, maxBranch: 32 },
|
|
720
960
|
{ threshold: 1e3, maxLeaf: 64, maxBranch: 64 },
|
|
@@ -741,33 +981,50 @@ var computeNextAutoScaleThreshold = (entryCount) => {
|
|
|
741
981
|
}
|
|
742
982
|
return Number.MAX_SAFE_INTEGER;
|
|
743
983
|
};
|
|
744
|
-
var
|
|
745
|
-
if (typeof config.compareKeys !== "function") {
|
|
746
|
-
throw new BTreeValidationError("compareKeys must be a function.");
|
|
747
|
-
}
|
|
748
|
-
const autoScale = config.autoScale === true;
|
|
984
|
+
var resolveInitialCapacity = (config, autoScale) => {
|
|
749
985
|
if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
|
|
750
|
-
throw new BTreeValidationError(
|
|
986
|
+
throw new BTreeValidationError(
|
|
987
|
+
"autoScale conflicts with explicit capacity."
|
|
988
|
+
);
|
|
751
989
|
}
|
|
752
|
-
let maxLeafEntries;
|
|
753
|
-
let maxBranchChildren;
|
|
754
990
|
if (autoScale) {
|
|
755
991
|
const tier = computeAutoScaleTier(0);
|
|
756
|
-
maxLeafEntries
|
|
757
|
-
maxBranchChildren = tier.maxBranch;
|
|
758
|
-
} else {
|
|
759
|
-
maxLeafEntries = normalizeNodeCapacity(config.maxLeafEntries, "maxLeafEntries", DEFAULT_MAX_LEAF_ENTRIES);
|
|
760
|
-
maxBranchChildren = normalizeNodeCapacity(config.maxBranchChildren, "maxBranchChildren", DEFAULT_MAX_BRANCH_CHILDREN);
|
|
992
|
+
return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
|
|
761
993
|
}
|
|
994
|
+
return {
|
|
995
|
+
maxLeafEntries: normalizeNodeCapacity(
|
|
996
|
+
config.maxLeafEntries,
|
|
997
|
+
"maxLeafEntries",
|
|
998
|
+
DEFAULT_MAX_LEAF_ENTRIES
|
|
999
|
+
),
|
|
1000
|
+
maxBranchChildren: normalizeNodeCapacity(
|
|
1001
|
+
config.maxBranchChildren,
|
|
1002
|
+
"maxBranchChildren",
|
|
1003
|
+
DEFAULT_MAX_BRANCH_CHILDREN
|
|
1004
|
+
)
|
|
1005
|
+
};
|
|
1006
|
+
};
|
|
1007
|
+
var createInitialState = (config) => {
|
|
1008
|
+
if (typeof config.compareKeys !== "function") {
|
|
1009
|
+
throw new BTreeValidationError("compareKeys must be a function.");
|
|
1010
|
+
}
|
|
1011
|
+
const autoScale = config.autoScale === true;
|
|
1012
|
+
const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
|
|
1013
|
+
config,
|
|
1014
|
+
autoScale
|
|
1015
|
+
);
|
|
762
1016
|
const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
|
|
1017
|
+
const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
|
|
1018
|
+
config.deleteRebalancePolicy
|
|
1019
|
+
);
|
|
763
1020
|
const emptyLeaf = createLeafNode([], null);
|
|
764
1021
|
return {
|
|
765
1022
|
compareKeys: config.compareKeys,
|
|
766
1023
|
maxLeafEntries,
|
|
767
1024
|
maxBranchChildren,
|
|
768
1025
|
duplicateKeys,
|
|
769
|
-
minLeafEntries:
|
|
770
|
-
minBranchChildren:
|
|
1026
|
+
minLeafEntries: minOccupancy(maxLeafEntries),
|
|
1027
|
+
minBranchChildren: minOccupancy(maxBranchChildren),
|
|
771
1028
|
root: emptyLeaf,
|
|
772
1029
|
leftmostLeaf: emptyLeaf,
|
|
773
1030
|
rightmostLeaf: emptyLeaf,
|
|
@@ -775,6 +1032,7 @@ var createInitialState = (config) => {
|
|
|
775
1032
|
nextSequence: 0,
|
|
776
1033
|
entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
|
|
777
1034
|
autoScale,
|
|
1035
|
+
deleteRebalancePolicy,
|
|
778
1036
|
_nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
|
|
779
1037
|
_cursor: { leaf: emptyLeaf, index: 0 }
|
|
780
1038
|
};
|
|
@@ -784,13 +1042,15 @@ var maybeAutoScale = (state) => {
|
|
|
784
1042
|
const { maxLeaf, maxBranch } = computeAutoScaleTier(state.entryCount);
|
|
785
1043
|
if (maxLeaf > state.maxLeafEntries) {
|
|
786
1044
|
state.maxLeafEntries = maxLeaf;
|
|
787
|
-
state.minLeafEntries =
|
|
1045
|
+
state.minLeafEntries = minOccupancy(maxLeaf);
|
|
788
1046
|
}
|
|
789
1047
|
if (maxBranch > state.maxBranchChildren) {
|
|
790
1048
|
state.maxBranchChildren = maxBranch;
|
|
791
|
-
state.minBranchChildren =
|
|
1049
|
+
state.minBranchChildren = minOccupancy(maxBranch);
|
|
792
1050
|
}
|
|
793
|
-
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1051
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1052
|
+
state.entryCount
|
|
1053
|
+
);
|
|
794
1054
|
};
|
|
795
1055
|
var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
|
|
796
1056
|
if (!state.autoScale) {
|
|
@@ -814,8 +1074,16 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
|
|
|
814
1074
|
}
|
|
815
1075
|
state.maxLeafEntries = normalizedLeaf;
|
|
816
1076
|
state.maxBranchChildren = normalizedBranch;
|
|
817
|
-
state.minLeafEntries =
|
|
818
|
-
state.minBranchChildren =
|
|
1077
|
+
state.minLeafEntries = minOccupancy(normalizedLeaf);
|
|
1078
|
+
state.minBranchChildren = minOccupancy(normalizedBranch);
|
|
1079
|
+
};
|
|
1080
|
+
var resetAutoScaleToTier0 = (state) => {
|
|
1081
|
+
const tier = computeAutoScaleTier(0);
|
|
1082
|
+
state.maxLeafEntries = tier.maxLeaf;
|
|
1083
|
+
state.maxBranchChildren = tier.maxBranch;
|
|
1084
|
+
state.minLeafEntries = minOccupancy(tier.maxLeaf);
|
|
1085
|
+
state.minBranchChildren = minOccupancy(tier.maxBranch);
|
|
1086
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
819
1087
|
};
|
|
820
1088
|
|
|
821
1089
|
// src/btree/bulkLoad.ts
|
|
@@ -837,7 +1105,11 @@ var computeChunkBoundaries = (total, max, min) => {
|
|
|
837
1105
|
return boundaries;
|
|
838
1106
|
};
|
|
839
1107
|
var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
840
|
-
const boundaries = computeChunkBoundaries(
|
|
1108
|
+
const boundaries = computeChunkBoundaries(
|
|
1109
|
+
entries.length,
|
|
1110
|
+
state.maxLeafEntries,
|
|
1111
|
+
state.minLeafEntries
|
|
1112
|
+
);
|
|
841
1113
|
const leaves = new Array(boundaries.length);
|
|
842
1114
|
let chunkStart = 0;
|
|
843
1115
|
for (let c = 0; c < boundaries.length; c += 1) {
|
|
@@ -845,7 +1117,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
|
845
1117
|
const chunk = new Array(chunkEnd - chunkStart);
|
|
846
1118
|
for (let i = chunkStart; i < chunkEnd; i += 1) {
|
|
847
1119
|
const seq = baseSequence + i;
|
|
848
|
-
chunk[i - chunkStart] =
|
|
1120
|
+
chunk[i - chunkStart] = createEntry(
|
|
1121
|
+
entries[i].key,
|
|
1122
|
+
seq,
|
|
1123
|
+
entries[i].value
|
|
1124
|
+
);
|
|
849
1125
|
ids[i] = seq;
|
|
850
1126
|
if (state.entryKeys !== null) {
|
|
851
1127
|
state.entryKeys.set(seq, entries[i].key);
|
|
@@ -879,11 +1155,18 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
879
1155
|
} else {
|
|
880
1156
|
let currentLevel = leaves;
|
|
881
1157
|
while (currentLevel.length > 1) {
|
|
882
|
-
const bounds = computeChunkBoundaries(
|
|
1158
|
+
const bounds = computeChunkBoundaries(
|
|
1159
|
+
currentLevel.length,
|
|
1160
|
+
state.maxBranchChildren,
|
|
1161
|
+
state.minBranchChildren
|
|
1162
|
+
);
|
|
883
1163
|
const nextLevel = new Array(bounds.length);
|
|
884
1164
|
let start = 0;
|
|
885
1165
|
for (let b = 0; b < bounds.length; b += 1) {
|
|
886
|
-
nextLevel[b] = createBranchNode(
|
|
1166
|
+
nextLevel[b] = createBranchNode(
|
|
1167
|
+
currentLevel.slice(start, bounds[b]),
|
|
1168
|
+
null
|
|
1169
|
+
);
|
|
887
1170
|
start = bounds[b];
|
|
888
1171
|
}
|
|
889
1172
|
currentLevel = nextLevel;
|
|
@@ -894,9 +1177,12 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
894
1177
|
return ids;
|
|
895
1178
|
};
|
|
896
1179
|
|
|
897
|
-
// src/btree/
|
|
1180
|
+
// src/btree/split.ts
|
|
898
1181
|
var insertChildAfter = (state, parent, existingChild, childToInsert) => {
|
|
899
|
-
const newChildMinKey = {
|
|
1182
|
+
const newChildMinKey = {
|
|
1183
|
+
key: void 0,
|
|
1184
|
+
sequence: 0
|
|
1185
|
+
};
|
|
900
1186
|
if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
|
|
901
1187
|
throw new BTreeInvariantError("inserted child has no min key");
|
|
902
1188
|
}
|
|
@@ -924,60 +1210,146 @@ var splitLeaf = (state, leaf) => {
|
|
|
924
1210
|
} else {
|
|
925
1211
|
state.rightmostLeaf = sibling;
|
|
926
1212
|
}
|
|
927
|
-
leaf.next = sibling;
|
|
928
|
-
if (leaf.parent === null) {
|
|
929
|
-
state.root = createBranchNode([leaf, sibling], null);
|
|
930
|
-
return;
|
|
1213
|
+
leaf.next = sibling;
|
|
1214
|
+
if (leaf.parent === null) {
|
|
1215
|
+
state.root = createBranchNode([leaf, sibling], null);
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
insertChildAfter(state, leaf.parent, leaf, sibling);
|
|
1219
|
+
};
|
|
1220
|
+
var splitBranch = (state, branch) => {
|
|
1221
|
+
branchCompact(branch);
|
|
1222
|
+
const splitAt = Math.ceil(branch.children.length / 2);
|
|
1223
|
+
const sibling = createBranchNode(
|
|
1224
|
+
branch.children.splice(splitAt),
|
|
1225
|
+
branch.parent
|
|
1226
|
+
);
|
|
1227
|
+
branch.keys.splice(splitAt);
|
|
1228
|
+
if (branch.parent === null) {
|
|
1229
|
+
state.root = createBranchNode([branch, sibling], null);
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
insertChildAfter(state, branch.parent, branch, sibling);
|
|
1233
|
+
};
|
|
1234
|
+
|
|
1235
|
+
// src/btree/entry-lookup.ts
|
|
1236
|
+
var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
1237
|
+
const targetLeaf = findLeafForKey(state, userKey, sequence);
|
|
1238
|
+
const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
|
|
1239
|
+
if (index >= leafEntryCount(targetLeaf)) return null;
|
|
1240
|
+
const entry = leafEntryAt(targetLeaf, index);
|
|
1241
|
+
if (entry.entryId !== sequence) return null;
|
|
1242
|
+
return { leaf: targetLeaf, index };
|
|
1243
|
+
};
|
|
1244
|
+
var peekEntryById = (state, entryId) => {
|
|
1245
|
+
if (state.entryKeys === null) {
|
|
1246
|
+
throw new BTreeInvariantError(
|
|
1247
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1248
|
+
);
|
|
1249
|
+
}
|
|
1250
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1251
|
+
if (userKey === void 0) return null;
|
|
1252
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1253
|
+
if (found === null) return null;
|
|
1254
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1255
|
+
return entry;
|
|
1256
|
+
};
|
|
1257
|
+
var updateEntryById = (state, entryId, newValue) => {
|
|
1258
|
+
if (state.entryKeys === null) {
|
|
1259
|
+
throw new BTreeInvariantError(
|
|
1260
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1264
|
+
if (userKey === void 0) return null;
|
|
1265
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1266
|
+
if (found === null) return null;
|
|
1267
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1268
|
+
const updated = createEntry(entry.key, entry.entryId, newValue);
|
|
1269
|
+
found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
|
|
1270
|
+
return updated;
|
|
1271
|
+
};
|
|
1272
|
+
var removeEntryById = (state, entryId) => {
|
|
1273
|
+
if (state.entryKeys === null) {
|
|
1274
|
+
throw new BTreeInvariantError(
|
|
1275
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1279
|
+
if (userKey === void 0) return null;
|
|
1280
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1281
|
+
if (found === null) return null;
|
|
1282
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1283
|
+
leafRemoveAt(found.leaf, found.index);
|
|
1284
|
+
state.entryCount -= 1;
|
|
1285
|
+
state.entryKeys.delete(entryId);
|
|
1286
|
+
if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
|
|
1287
|
+
updateMinKeyInAncestors(found.leaf);
|
|
1288
|
+
}
|
|
1289
|
+
if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
|
|
1290
|
+
rebalanceAfterLeafRemoval(state, found.leaf);
|
|
931
1291
|
}
|
|
932
|
-
|
|
1292
|
+
return entry;
|
|
933
1293
|
};
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
state.
|
|
941
|
-
|
|
1294
|
+
|
|
1295
|
+
// src/btree/mutations.ts
|
|
1296
|
+
var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
|
|
1297
|
+
if (state.duplicateKeys === "allow") return null;
|
|
1298
|
+
if (insertAt > 0) {
|
|
1299
|
+
const candidate = leafEntryAt(targetLeaf, insertAt - 1);
|
|
1300
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1301
|
+
return {
|
|
1302
|
+
leaf: targetLeaf,
|
|
1303
|
+
physIndex: targetLeaf.entryOffset + insertAt - 1,
|
|
1304
|
+
entry: candidate
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
} else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
|
|
1308
|
+
const prevLeaf = targetLeaf.prev;
|
|
1309
|
+
const prevCount = leafEntryCount(prevLeaf);
|
|
1310
|
+
const candidate = leafEntryAt(prevLeaf, prevCount - 1);
|
|
1311
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1312
|
+
return {
|
|
1313
|
+
leaf: prevLeaf,
|
|
1314
|
+
physIndex: prevLeaf.entryOffset + prevCount - 1,
|
|
1315
|
+
entry: candidate
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
942
1318
|
}
|
|
943
|
-
|
|
1319
|
+
return null;
|
|
944
1320
|
};
|
|
945
1321
|
var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
|
|
946
1322
|
const sequence = state.nextSequence;
|
|
947
1323
|
const insertAt = upperBoundInLeaf(state, targetLeaf, key, sequence);
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
if (
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
existingEntry = candidate;
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
if (existingEntry !== null) {
|
|
963
|
-
if (state.duplicateKeys === "reject") {
|
|
964
|
-
throw new BTreeValidationError("Duplicate key rejected.");
|
|
965
|
-
}
|
|
966
|
-
existingEntry.value = value;
|
|
967
|
-
return existingEntry.entryId;
|
|
968
|
-
}
|
|
1324
|
+
const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
|
|
1325
|
+
if (dup !== null) {
|
|
1326
|
+
if (state.duplicateKeys === "reject") {
|
|
1327
|
+
throw new BTreeValidationError("Duplicate key rejected.");
|
|
1328
|
+
}
|
|
1329
|
+
dup.leaf.entries[dup.physIndex] = createEntry(
|
|
1330
|
+
dup.entry.key,
|
|
1331
|
+
dup.entry.entryId,
|
|
1332
|
+
value
|
|
1333
|
+
);
|
|
1334
|
+
return dup.entry.entryId;
|
|
969
1335
|
}
|
|
970
1336
|
if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
|
|
971
1337
|
throw new BTreeValidationError("Sequence overflow.");
|
|
972
1338
|
}
|
|
973
1339
|
state.nextSequence += 1;
|
|
974
|
-
leafInsertAt(
|
|
1340
|
+
leafInsertAt(
|
|
1341
|
+
targetLeaf,
|
|
1342
|
+
insertAt,
|
|
1343
|
+
createEntry(key, sequence, value)
|
|
1344
|
+
);
|
|
975
1345
|
state.entryCount += 1;
|
|
976
1346
|
if (state.entryKeys !== null) {
|
|
977
1347
|
state.entryKeys.set(sequence, key);
|
|
978
1348
|
}
|
|
979
|
-
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
980
|
-
|
|
1349
|
+
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1350
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1351
|
+
if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
|
|
1352
|
+
splitLeaf(state, targetLeaf);
|
|
981
1353
|
maybeAutoScale(state);
|
|
982
1354
|
return sequence;
|
|
983
1355
|
};
|
|
@@ -988,7 +1360,8 @@ var putEntry = (state, key, value) => {
|
|
|
988
1360
|
var popFirstEntry = (state) => {
|
|
989
1361
|
if (state.entryCount === 0) return null;
|
|
990
1362
|
const firstEntry = leafShiftEntry(state.leftmostLeaf);
|
|
991
|
-
if (firstEntry === void 0)
|
|
1363
|
+
if (firstEntry === void 0)
|
|
1364
|
+
throw new BTreeInvariantError("leftmost leaf empty but count > 0");
|
|
992
1365
|
state.entryCount -= 1;
|
|
993
1366
|
if (state.entryKeys !== null) {
|
|
994
1367
|
state.entryKeys.delete(firstEntry.entryId);
|
|
@@ -1004,7 +1377,8 @@ var popFirstEntry = (state) => {
|
|
|
1004
1377
|
var popLastEntry = (state) => {
|
|
1005
1378
|
if (state.entryCount === 0) return null;
|
|
1006
1379
|
const lastEntry = leafPopEntry(state.rightmostLeaf);
|
|
1007
|
-
if (lastEntry === void 0)
|
|
1380
|
+
if (lastEntry === void 0)
|
|
1381
|
+
throw new BTreeInvariantError("rightmost leaf empty but count > 0");
|
|
1008
1382
|
state.entryCount -= 1;
|
|
1009
1383
|
if (state.entryKeys !== null) {
|
|
1010
1384
|
state.entryKeys.delete(lastEntry.entryId);
|
|
@@ -1025,62 +1399,26 @@ var removeFirstMatchingEntry = (state, key) => {
|
|
|
1025
1399
|
if (state.entryKeys !== null) {
|
|
1026
1400
|
state.entryKeys.delete(targetEntry.entryId);
|
|
1027
1401
|
}
|
|
1028
|
-
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1402
|
+
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1403
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1029
1404
|
if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
|
|
1030
1405
|
rebalanceAfterLeafRemoval(state, targetLeaf);
|
|
1031
1406
|
}
|
|
1032
1407
|
return targetEntry;
|
|
1033
1408
|
};
|
|
1034
|
-
var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
1035
|
-
const targetLeaf = findLeafForKey(state, userKey, sequence);
|
|
1036
|
-
const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
|
|
1037
|
-
if (index >= leafEntryCount(targetLeaf)) return null;
|
|
1038
|
-
const entry = leafEntryAt(targetLeaf, index);
|
|
1039
|
-
if (entry.entryId !== sequence) return null;
|
|
1040
|
-
return { leaf: targetLeaf, index };
|
|
1041
|
-
};
|
|
1042
|
-
var removeEntryById = (state, entryId) => {
|
|
1043
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1044
|
-
if (userKey === void 0) return null;
|
|
1045
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1046
|
-
if (found === null) return null;
|
|
1047
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1048
|
-
leafRemoveAt(found.leaf, found.index);
|
|
1049
|
-
state.entryCount -= 1;
|
|
1050
|
-
state.entryKeys.delete(entryId);
|
|
1051
|
-
if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
|
|
1052
|
-
updateMinKeyInAncestors(found.leaf);
|
|
1053
|
-
}
|
|
1054
|
-
if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
|
|
1055
|
-
rebalanceAfterLeafRemoval(state, found.leaf);
|
|
1056
|
-
}
|
|
1057
|
-
return entry;
|
|
1058
|
-
};
|
|
1059
|
-
var peekEntryById = (state, entryId) => {
|
|
1060
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1061
|
-
if (userKey === void 0) return null;
|
|
1062
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1063
|
-
if (found === null) return null;
|
|
1064
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1065
|
-
return entry;
|
|
1066
|
-
};
|
|
1067
|
-
var updateEntryById = (state, entryId, newValue) => {
|
|
1068
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1069
|
-
if (userKey === void 0) return null;
|
|
1070
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1071
|
-
if (found === null) return null;
|
|
1072
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1073
|
-
entry.value = newValue;
|
|
1074
|
-
return entry;
|
|
1075
|
-
};
|
|
1076
1409
|
var putManyEntries = (state, entries) => {
|
|
1077
1410
|
if (entries.length === 0) return [];
|
|
1078
1411
|
const strictlyAscending = state.duplicateKeys !== "allow";
|
|
1079
1412
|
for (let i = 1; i < entries.length; i += 1) {
|
|
1080
1413
|
const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
|
|
1081
|
-
if (
|
|
1414
|
+
if (cmp > 0) {
|
|
1415
|
+
throw new BTreeValidationError(
|
|
1416
|
+
"putMany: entries not in ascending order."
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1419
|
+
if (strictlyAscending && cmp === 0) {
|
|
1082
1420
|
throw new BTreeValidationError(
|
|
1083
|
-
|
|
1421
|
+
state.duplicateKeys === "reject" ? "putMany: duplicate key rejected." : "putMany: equal keys not allowed in strict mode."
|
|
1084
1422
|
);
|
|
1085
1423
|
}
|
|
1086
1424
|
}
|
|
@@ -1089,7 +1427,12 @@ var putManyEntries = (state, entries) => {
|
|
|
1089
1427
|
let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
|
|
1090
1428
|
for (let i = 0; i < entries.length; i += 1) {
|
|
1091
1429
|
const entry = entries[i];
|
|
1092
|
-
const targetLeaf = findLeafFromHint(
|
|
1430
|
+
const targetLeaf = findLeafFromHint(
|
|
1431
|
+
state,
|
|
1432
|
+
hintLeaf,
|
|
1433
|
+
entry.key,
|
|
1434
|
+
state.nextSequence
|
|
1435
|
+
);
|
|
1093
1436
|
ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
|
|
1094
1437
|
hintLeaf = targetLeaf;
|
|
1095
1438
|
}
|
|
@@ -1098,116 +1441,14 @@ var putManyEntries = (state, entries) => {
|
|
|
1098
1441
|
return bulkLoadEntries(state, entries);
|
|
1099
1442
|
};
|
|
1100
1443
|
|
|
1101
|
-
// src/btree/rangeQuery.ts
|
|
1102
|
-
var initRangeCursor = (state, startKey, endKey, options) => {
|
|
1103
|
-
if (state.entryCount === 0) return null;
|
|
1104
|
-
const compare = state.compareKeys;
|
|
1105
|
-
const boundCompared = compare(startKey, endKey);
|
|
1106
|
-
if (boundCompared > 0) return null;
|
|
1107
|
-
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
1108
|
-
const upperExclusive = options?.upperBound === "exclusive";
|
|
1109
|
-
if (lowerExclusive && upperExclusive && boundCompared === 0) return null;
|
|
1110
|
-
const startSeq = lowerExclusive ? Number.MAX_SAFE_INTEGER : 0;
|
|
1111
|
-
const leaf = findLeafForKey(state, startKey, startSeq);
|
|
1112
|
-
const index = lowerExclusive ? upperBoundInLeaf(state, leaf, startKey, Number.MAX_SAFE_INTEGER) : lowerBoundInLeaf(state, leaf, startKey, 0);
|
|
1113
|
-
return { leaf, index, compare, upperExclusive };
|
|
1114
|
-
};
|
|
1115
|
-
var countRangeEntries = (state, startKey, endKey, options) => {
|
|
1116
|
-
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
1117
|
-
if (cursor === null) return 0;
|
|
1118
|
-
let cursorLeaf = cursor.leaf;
|
|
1119
|
-
let cursorIndex = cursor.index;
|
|
1120
|
-
const { compare, upperExclusive } = cursor;
|
|
1121
|
-
let count = 0;
|
|
1122
|
-
while (cursorLeaf !== null) {
|
|
1123
|
-
const leafCount = leafEntryCount(cursorLeaf);
|
|
1124
|
-
if (cursorIndex >= leafCount) {
|
|
1125
|
-
cursorLeaf = cursorLeaf.next;
|
|
1126
|
-
cursorIndex = 0;
|
|
1127
|
-
continue;
|
|
1128
|
-
}
|
|
1129
|
-
const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
|
|
1130
|
-
const cmpLast = compare(lastEntry.key, endKey);
|
|
1131
|
-
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
1132
|
-
count += leafCount - cursorIndex;
|
|
1133
|
-
cursorLeaf = cursorLeaf.next;
|
|
1134
|
-
cursorIndex = 0;
|
|
1135
|
-
continue;
|
|
1136
|
-
}
|
|
1137
|
-
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
1138
|
-
const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
|
|
1139
|
-
const limit = endBound < leafCount ? endBound : leafCount;
|
|
1140
|
-
count += limit - cursorIndex;
|
|
1141
|
-
return count;
|
|
1142
|
-
}
|
|
1143
|
-
return count;
|
|
1144
|
-
};
|
|
1145
|
-
var RANGE_PREALLOC_THRESHOLD = 200;
|
|
1146
|
-
var allocateRangeOutput = (state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options) => {
|
|
1147
|
-
const firstLeafCount = leafEntryCount(cursorLeaf);
|
|
1148
|
-
const firstLeafRemainder = firstLeafCount - cursorIndex;
|
|
1149
|
-
if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursorLeaf.next !== null) {
|
|
1150
|
-
const lastEntry = leafEntryAt(cursorLeaf, firstLeafCount - 1);
|
|
1151
|
-
const cmpLast = compare(lastEntry.key, endKey);
|
|
1152
|
-
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
1153
|
-
const total = countRangeEntries(state, startKey, endKey, options);
|
|
1154
|
-
return new Array(total);
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
return [];
|
|
1158
|
-
};
|
|
1159
|
-
var appendLeafSlice = (leaf, from, to, output, useIndexed, writeIdx) => {
|
|
1160
|
-
if (useIndexed) {
|
|
1161
|
-
for (let i = from; i < to; i += 1) {
|
|
1162
|
-
output[writeIdx++] = leafEntryAt(leaf, i);
|
|
1163
|
-
}
|
|
1164
|
-
} else {
|
|
1165
|
-
for (let i = from; i < to; i += 1) {
|
|
1166
|
-
output.push(leafEntryAt(leaf, i));
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
return writeIdx;
|
|
1170
|
-
};
|
|
1171
|
-
var rangeQueryEntries = (state, startKey, endKey, options) => {
|
|
1172
|
-
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
1173
|
-
if (cursor === null) return [];
|
|
1174
|
-
let cursorLeaf = cursor.leaf;
|
|
1175
|
-
let cursorIndex = cursor.index;
|
|
1176
|
-
const { compare, upperExclusive } = cursor;
|
|
1177
|
-
const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
|
|
1178
|
-
let writeIdx = 0;
|
|
1179
|
-
const useIndexed = output.length > 0;
|
|
1180
|
-
while (cursorLeaf !== null) {
|
|
1181
|
-
const leafCount = leafEntryCount(cursorLeaf);
|
|
1182
|
-
if (cursorIndex >= leafCount) {
|
|
1183
|
-
cursorLeaf = cursorLeaf.next;
|
|
1184
|
-
cursorIndex = 0;
|
|
1185
|
-
continue;
|
|
1186
|
-
}
|
|
1187
|
-
const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
|
|
1188
|
-
const cmpLast = compare(lastEntry.key, endKey);
|
|
1189
|
-
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
1190
|
-
writeIdx = appendLeafSlice(cursorLeaf, cursorIndex, leafCount, output, useIndexed, writeIdx);
|
|
1191
|
-
cursorLeaf = cursorLeaf.next;
|
|
1192
|
-
cursorIndex = 0;
|
|
1193
|
-
continue;
|
|
1194
|
-
}
|
|
1195
|
-
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
1196
|
-
const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
|
|
1197
|
-
const limit = endBound < leafCount ? endBound : leafCount;
|
|
1198
|
-
appendLeafSlice(cursorLeaf, cursorIndex, limit, output, useIndexed, writeIdx);
|
|
1199
|
-
return output;
|
|
1200
|
-
}
|
|
1201
|
-
return output;
|
|
1202
|
-
};
|
|
1203
|
-
|
|
1204
1444
|
// src/btree/serialization.ts
|
|
1205
1445
|
var buildConfigFromState = (state) => {
|
|
1206
1446
|
const config = {
|
|
1207
1447
|
compareKeys: state.compareKeys,
|
|
1208
1448
|
duplicateKeys: state.duplicateKeys,
|
|
1209
1449
|
enableEntryIdLookup: state.entryKeys !== null,
|
|
1210
|
-
autoScale: state.autoScale
|
|
1450
|
+
autoScale: state.autoScale,
|
|
1451
|
+
deleteRebalancePolicy: state.deleteRebalancePolicy
|
|
1211
1452
|
};
|
|
1212
1453
|
if (!state.autoScale) {
|
|
1213
1454
|
config.maxLeafEntries = state.maxLeafEntries;
|
|
@@ -1227,17 +1468,17 @@ var serializeToJSON = (state) => {
|
|
|
1227
1468
|
}
|
|
1228
1469
|
leaf = leaf.next;
|
|
1229
1470
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
enableEntryIdLookup: state.entryKeys !== null,
|
|
1237
|
-
autoScale: state.autoScale
|
|
1238
|
-
},
|
|
1239
|
-
entries
|
|
1471
|
+
const config = {
|
|
1472
|
+
maxLeafEntries: state.maxLeafEntries,
|
|
1473
|
+
maxBranchChildren: state.maxBranchChildren,
|
|
1474
|
+
duplicateKeys: state.duplicateKeys,
|
|
1475
|
+
enableEntryIdLookup: state.entryKeys !== null,
|
|
1476
|
+
autoScale: state.autoScale
|
|
1240
1477
|
};
|
|
1478
|
+
if (state.deleteRebalancePolicy !== "standard") {
|
|
1479
|
+
config.deleteRebalancePolicy = state.deleteRebalancePolicy;
|
|
1480
|
+
}
|
|
1481
|
+
return { version: 1, config, entries };
|
|
1241
1482
|
};
|
|
1242
1483
|
var MAX_SERIALIZED_ENTRIES = 1e6;
|
|
1243
1484
|
var validateStructure = (json) => {
|
|
@@ -1300,13 +1541,28 @@ var validateBTreeJSON = (json) => {
|
|
|
1300
1541
|
validateStructure(json);
|
|
1301
1542
|
validateConfig(json.config);
|
|
1302
1543
|
};
|
|
1544
|
+
var validateBTreeJSONSortOrder = (json, compareKeys) => {
|
|
1545
|
+
const strict = json.config.duplicateKeys !== "allow";
|
|
1546
|
+
for (let i = 1; i < json.entries.length; i += 1) {
|
|
1547
|
+
const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
|
|
1548
|
+
if (cmp > 0) {
|
|
1549
|
+
throw new BTreeValidationError("fromJSON: entries not sorted.");
|
|
1550
|
+
}
|
|
1551
|
+
if (strict && cmp === 0) {
|
|
1552
|
+
throw new BTreeValidationError(
|
|
1553
|
+
'fromJSON: duplicate keys require duplicateKeys "allow".'
|
|
1554
|
+
);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
};
|
|
1303
1558
|
var buildConfigFromJSON = (json, compareKeys) => {
|
|
1304
1559
|
const cfg = json.config;
|
|
1305
1560
|
const config = {
|
|
1306
1561
|
compareKeys,
|
|
1307
1562
|
duplicateKeys: cfg.duplicateKeys,
|
|
1308
1563
|
enableEntryIdLookup: cfg.enableEntryIdLookup,
|
|
1309
|
-
autoScale: cfg.autoScale
|
|
1564
|
+
autoScale: cfg.autoScale,
|
|
1565
|
+
deleteRebalancePolicy: cfg.deleteRebalancePolicy
|
|
1310
1566
|
};
|
|
1311
1567
|
if (!cfg.autoScale) {
|
|
1312
1568
|
config.maxLeafEntries = cfg.maxLeafEntries;
|
|
@@ -1315,6 +1571,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
|
|
|
1315
1571
|
return config;
|
|
1316
1572
|
};
|
|
1317
1573
|
|
|
1574
|
+
// src/btree/traversal.ts
|
|
1575
|
+
var snapshotEntries = (state) => {
|
|
1576
|
+
const result = new Array(state.entryCount);
|
|
1577
|
+
let leaf = state.leftmostLeaf;
|
|
1578
|
+
let writeIdx = 0;
|
|
1579
|
+
while (leaf !== null) {
|
|
1580
|
+
const count = leafEntryCount(leaf);
|
|
1581
|
+
for (let i = 0; i < count; i += 1) {
|
|
1582
|
+
result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
|
|
1583
|
+
}
|
|
1584
|
+
leaf = leaf.next;
|
|
1585
|
+
}
|
|
1586
|
+
return result;
|
|
1587
|
+
};
|
|
1588
|
+
var collectInternalEntries = (state) => {
|
|
1589
|
+
const result = new Array(state.entryCount);
|
|
1590
|
+
let leaf = state.leftmostLeaf;
|
|
1591
|
+
let writeIdx = 0;
|
|
1592
|
+
while (leaf !== null) {
|
|
1593
|
+
const count = leafEntryCount(leaf);
|
|
1594
|
+
for (let i = 0; i < count; i += 1) {
|
|
1595
|
+
result[writeIdx++] = leafEntryAt(leaf, i);
|
|
1596
|
+
}
|
|
1597
|
+
leaf = leaf.next;
|
|
1598
|
+
}
|
|
1599
|
+
return result;
|
|
1600
|
+
};
|
|
1601
|
+
var forEachEntry = (state, callback, thisArg) => {
|
|
1602
|
+
let leaf = state.leftmostLeaf;
|
|
1603
|
+
while (leaf !== null) {
|
|
1604
|
+
const count = leafEntryCount(leaf);
|
|
1605
|
+
for (let i = 0; i < count; i += 1) {
|
|
1606
|
+
callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
|
|
1607
|
+
}
|
|
1608
|
+
leaf = leaf.next;
|
|
1609
|
+
}
|
|
1610
|
+
};
|
|
1611
|
+
|
|
1318
1612
|
// src/btree/integrity-helpers.ts
|
|
1319
1613
|
var nodeMinKey = (node) => {
|
|
1320
1614
|
if (isLeafNode(node)) {
|
|
@@ -1323,14 +1617,17 @@ var nodeMinKey = (node) => {
|
|
|
1323
1617
|
return { key: e.key, sequence: e.entryId };
|
|
1324
1618
|
}
|
|
1325
1619
|
if (node.childOffset >= node.keys.length) return null;
|
|
1326
|
-
return {
|
|
1620
|
+
return {
|
|
1621
|
+
key: node.keys[node.childOffset].key,
|
|
1622
|
+
sequence: node.keys[node.childOffset].sequence
|
|
1623
|
+
};
|
|
1327
1624
|
};
|
|
1328
1625
|
var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
|
|
1329
1626
|
const cmp = comparator(leftKey, rightKey);
|
|
1330
1627
|
if (cmp !== 0) {
|
|
1331
1628
|
return cmp;
|
|
1332
1629
|
}
|
|
1333
|
-
return leftSeq - rightSeq;
|
|
1630
|
+
return leftSeq < rightSeq ? -1 : leftSeq > rightSeq ? 1 : 0;
|
|
1334
1631
|
};
|
|
1335
1632
|
var getNodeMaxKey = (node) => {
|
|
1336
1633
|
if (isLeafNode(node)) {
|
|
@@ -1345,9 +1642,7 @@ var getNodeMaxKey = (node) => {
|
|
|
1345
1642
|
};
|
|
1346
1643
|
var validateComparatorResult = (result) => {
|
|
1347
1644
|
if (!Number.isFinite(result)) {
|
|
1348
|
-
throw new BTreeValidationError(
|
|
1349
|
-
"compareKeys must return a finite number."
|
|
1350
|
-
);
|
|
1645
|
+
throw new BTreeValidationError("compareKeys must return a finite number.");
|
|
1351
1646
|
}
|
|
1352
1647
|
return result;
|
|
1353
1648
|
};
|
|
@@ -1407,6 +1702,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
|
|
|
1407
1702
|
throw error;
|
|
1408
1703
|
}
|
|
1409
1704
|
};
|
|
1705
|
+
var validateAdjacentLeafOrdering = (state, previous, cursor) => {
|
|
1706
|
+
const prevMax = getNodeMaxKey(previous);
|
|
1707
|
+
const currentMin = nodeMinKey(cursor);
|
|
1708
|
+
if (prevMax === null || currentMin === null) {
|
|
1709
|
+
throw new BTreeInvariantError(
|
|
1710
|
+
"Non-empty tree leaf chain contains empty leaf node."
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1713
|
+
if (compareNodeKeys(
|
|
1714
|
+
state.compareKeys,
|
|
1715
|
+
prevMax.key,
|
|
1716
|
+
prevMax.sequence,
|
|
1717
|
+
currentMin.key,
|
|
1718
|
+
currentMin.sequence
|
|
1719
|
+
) > 0) {
|
|
1720
|
+
throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
|
|
1721
|
+
}
|
|
1722
|
+
const prevCount = leafEntryCount(previous);
|
|
1723
|
+
const curCount = leafEntryCount(cursor);
|
|
1724
|
+
if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
|
|
1725
|
+
leafEntryAt(previous, prevCount - 1).key,
|
|
1726
|
+
leafEntryAt(cursor, 0).key
|
|
1727
|
+
) === 0) {
|
|
1728
|
+
throw new BTreeInvariantError(
|
|
1729
|
+
"Duplicate user key detected across adjacent leaves with uniqueness policy."
|
|
1730
|
+
);
|
|
1731
|
+
}
|
|
1732
|
+
};
|
|
1410
1733
|
var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
1411
1734
|
if (!isLeafNode(cursor)) {
|
|
1412
1735
|
throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
|
|
@@ -1418,22 +1741,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
|
1418
1741
|
throw new BTreeInvariantError("Leaf prev pointer mismatch.");
|
|
1419
1742
|
}
|
|
1420
1743
|
if (previous !== null && isLeafNode(previous)) {
|
|
1421
|
-
|
|
1422
|
-
const currentMin = nodeMinKey(cursor);
|
|
1423
|
-
if (prevMax === null || currentMin === null) {
|
|
1424
|
-
throw new BTreeInvariantError("Non-empty tree leaf chain contains empty leaf node.");
|
|
1425
|
-
}
|
|
1426
|
-
if (compareNodeKeys(state.compareKeys, prevMax.key, prevMax.sequence, currentMin.key, currentMin.sequence) > 0) {
|
|
1427
|
-
throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
|
|
1428
|
-
}
|
|
1429
|
-
const prevCount = leafEntryCount(previous);
|
|
1430
|
-
const curCount = leafEntryCount(cursor);
|
|
1431
|
-
if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
|
|
1432
|
-
leafEntryAt(previous, prevCount - 1).key,
|
|
1433
|
-
leafEntryAt(cursor, 0).key
|
|
1434
|
-
) === 0) {
|
|
1435
|
-
throw new BTreeInvariantError("Duplicate user key detected across adjacent leaves with uniqueness policy.");
|
|
1436
|
-
}
|
|
1744
|
+
validateAdjacentLeafOrdering(state, previous, cursor);
|
|
1437
1745
|
}
|
|
1438
1746
|
};
|
|
1439
1747
|
var validateLeafLinks = (state, expectedLeafCount) => {
|
|
@@ -1442,7 +1750,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1442
1750
|
throw new BTreeInvariantError("Empty tree root must be a leaf node.");
|
|
1443
1751
|
}
|
|
1444
1752
|
if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
|
|
1445
|
-
throw new BTreeInvariantError(
|
|
1753
|
+
throw new BTreeInvariantError(
|
|
1754
|
+
"Empty tree leaf pointers must reference root leaf."
|
|
1755
|
+
);
|
|
1446
1756
|
}
|
|
1447
1757
|
return;
|
|
1448
1758
|
}
|
|
@@ -1467,7 +1777,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1467
1777
|
throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
|
|
1468
1778
|
}
|
|
1469
1779
|
if (leafCount !== expectedLeafCount) {
|
|
1470
|
-
throw new BTreeInvariantError(
|
|
1780
|
+
throw new BTreeInvariantError(
|
|
1781
|
+
"Leaf chain count mismatch with tree traversal count."
|
|
1782
|
+
);
|
|
1471
1783
|
}
|
|
1472
1784
|
};
|
|
1473
1785
|
|
|
@@ -1494,7 +1806,9 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1494
1806
|
leafEntryAt(node, index - 1).key,
|
|
1495
1807
|
leafEntryAt(node, index).key
|
|
1496
1808
|
) === 0) {
|
|
1497
|
-
throw new BTreeInvariantError(
|
|
1809
|
+
throw new BTreeInvariantError(
|
|
1810
|
+
"Duplicate user key detected in tree with uniqueness policy."
|
|
1811
|
+
);
|
|
1498
1812
|
}
|
|
1499
1813
|
}
|
|
1500
1814
|
}
|
|
@@ -1502,12 +1816,7 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1502
1816
|
const first = leafEntryAt(node, index - 2);
|
|
1503
1817
|
const second = leafEntryAt(node, index - 1);
|
|
1504
1818
|
const third = leafEntryAt(node, index);
|
|
1505
|
-
assertTransitivityAsInvariant(
|
|
1506
|
-
state,
|
|
1507
|
-
first.key,
|
|
1508
|
-
second.key,
|
|
1509
|
-
third.key
|
|
1510
|
-
);
|
|
1819
|
+
assertTransitivityAsInvariant(state, first.key, second.key, third.key);
|
|
1511
1820
|
}
|
|
1512
1821
|
if (count > state.maxLeafEntries) {
|
|
1513
1822
|
throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
|
|
@@ -1516,9 +1825,14 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1516
1825
|
var validateLeafNode = (state, node, depth) => {
|
|
1517
1826
|
validateLeafNodeOrdering(state, node);
|
|
1518
1827
|
const count = leafEntryCount(node);
|
|
1519
|
-
|
|
1828
|
+
let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
|
|
1829
|
+
if (state.deleteRebalancePolicy === "lazy") {
|
|
1830
|
+
baseMinLeaf = applyLazyThreshold(baseMinLeaf);
|
|
1831
|
+
}
|
|
1520
1832
|
if (node !== state.root && count < baseMinLeaf) {
|
|
1521
|
-
throw new BTreeInvariantError(
|
|
1833
|
+
throw new BTreeInvariantError(
|
|
1834
|
+
"Non-root leaf node violates minimum occupancy."
|
|
1835
|
+
);
|
|
1522
1836
|
}
|
|
1523
1837
|
const first = count === 0 ? null : leafEntryAt(node, 0);
|
|
1524
1838
|
const last = count === 0 ? null : leafEntryAt(node, count - 1);
|
|
@@ -1540,76 +1854,110 @@ var validateBranchStructure = (state, node) => {
|
|
|
1540
1854
|
}
|
|
1541
1855
|
const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
|
|
1542
1856
|
if (node !== state.root && liveCount < baseMinBranch) {
|
|
1543
|
-
throw new BTreeInvariantError(
|
|
1857
|
+
throw new BTreeInvariantError(
|
|
1858
|
+
"Non-root branch node violates minimum occupancy."
|
|
1859
|
+
);
|
|
1544
1860
|
}
|
|
1545
1861
|
if (liveCount > state.maxBranchChildren) {
|
|
1546
1862
|
throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
|
|
1547
1863
|
}
|
|
1548
1864
|
if (node.keys.length !== node.children.length) {
|
|
1549
|
-
throw new BTreeInvariantError(
|
|
1865
|
+
throw new BTreeInvariantError(
|
|
1866
|
+
"Branch keys array length does not match children array length."
|
|
1867
|
+
);
|
|
1550
1868
|
}
|
|
1551
1869
|
};
|
|
1552
1870
|
var validateBranchChild = (state, node, childIndex, depth) => {
|
|
1553
1871
|
const child = node.children[childIndex];
|
|
1554
1872
|
if (child.parent !== node) {
|
|
1555
|
-
throw new BTreeInvariantError(
|
|
1873
|
+
throw new BTreeInvariantError(
|
|
1874
|
+
"Child-parent pointer mismatch in branch node."
|
|
1875
|
+
);
|
|
1556
1876
|
}
|
|
1557
1877
|
if (child.indexInParent !== childIndex) {
|
|
1558
|
-
throw new BTreeInvariantError(
|
|
1878
|
+
throw new BTreeInvariantError(
|
|
1879
|
+
"Child indexInParent does not match actual position in parent."
|
|
1880
|
+
);
|
|
1559
1881
|
}
|
|
1560
1882
|
const childValidation = validateNode(state, child, depth + 1);
|
|
1561
1883
|
if (childValidation.minKey === null || childValidation.maxKey === null) {
|
|
1562
|
-
throw new BTreeInvariantError(
|
|
1884
|
+
throw new BTreeInvariantError(
|
|
1885
|
+
"Branch child must not be empty in non-root branch tree."
|
|
1886
|
+
);
|
|
1563
1887
|
}
|
|
1564
1888
|
const cachedMinKey = node.keys[childIndex];
|
|
1565
1889
|
const actualMinKey = nodeMinKey(child);
|
|
1566
|
-
if (actualMinKey === null || compareNodeKeys(
|
|
1567
|
-
|
|
1890
|
+
if (actualMinKey === null || compareNodeKeys(
|
|
1891
|
+
state.compareKeys,
|
|
1892
|
+
cachedMinKey.key,
|
|
1893
|
+
cachedMinKey.sequence,
|
|
1894
|
+
actualMinKey.key,
|
|
1895
|
+
actualMinKey.sequence
|
|
1896
|
+
) !== 0) {
|
|
1897
|
+
throw new BTreeInvariantError(
|
|
1898
|
+
"Branch cached key does not match actual child minimum key."
|
|
1899
|
+
);
|
|
1568
1900
|
}
|
|
1569
1901
|
return childValidation;
|
|
1570
1902
|
};
|
|
1903
|
+
var mergeChildValidation = (state, accumulated, childValidation) => {
|
|
1904
|
+
if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
|
|
1905
|
+
throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
|
|
1906
|
+
}
|
|
1907
|
+
if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
|
|
1908
|
+
accumulated.leafDepth = childValidation.leafDepth;
|
|
1909
|
+
}
|
|
1910
|
+
if (accumulated.previousChildMax !== null && compareNodeKeys(
|
|
1911
|
+
state.compareKeys,
|
|
1912
|
+
accumulated.previousChildMax.key,
|
|
1913
|
+
accumulated.previousChildMax.sequence,
|
|
1914
|
+
childValidation.minKey.key,
|
|
1915
|
+
childValidation.minKey.sequence
|
|
1916
|
+
) >= 0) {
|
|
1917
|
+
throw new BTreeInvariantError(
|
|
1918
|
+
"Branch child key ranges are not strictly ordered."
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1921
|
+
if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
|
|
1922
|
+
accumulated.maxKey = childValidation.maxKey;
|
|
1923
|
+
accumulated.previousChildMax = childValidation.maxKey;
|
|
1924
|
+
accumulated.leafCount += childValidation.leafCount;
|
|
1925
|
+
accumulated.branchCount += childValidation.branchCount;
|
|
1926
|
+
accumulated.entryCount += childValidation.entryCount;
|
|
1927
|
+
};
|
|
1571
1928
|
var validateNode = (state, node, depth) => {
|
|
1572
1929
|
if (isLeafNode(node)) {
|
|
1573
1930
|
return validateLeafNode(state, node, depth);
|
|
1574
1931
|
}
|
|
1575
1932
|
validateBranchStructure(state, node);
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1933
|
+
const acc = {
|
|
1934
|
+
leafDepth: null,
|
|
1935
|
+
leafCount: 0,
|
|
1936
|
+
branchCount: 1,
|
|
1937
|
+
entryCount: 0,
|
|
1938
|
+
minKey: null,
|
|
1939
|
+
maxKey: null,
|
|
1940
|
+
previousChildMax: null
|
|
1941
|
+
};
|
|
1583
1942
|
for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
|
|
1584
1943
|
const childValidation = validateBranchChild(state, node, childIndex, depth);
|
|
1585
|
-
|
|
1586
|
-
throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
|
|
1587
|
-
}
|
|
1588
|
-
if (leafDepth === null && childValidation.leafDepth !== null) {
|
|
1589
|
-
leafDepth = childValidation.leafDepth;
|
|
1590
|
-
}
|
|
1591
|
-
if (previousChildMax !== null && compareNodeKeys(
|
|
1592
|
-
state.compareKeys,
|
|
1593
|
-
previousChildMax.key,
|
|
1594
|
-
previousChildMax.sequence,
|
|
1595
|
-
childValidation.minKey.key,
|
|
1596
|
-
childValidation.minKey.sequence
|
|
1597
|
-
) >= 0) {
|
|
1598
|
-
throw new BTreeInvariantError("Branch child key ranges are not strictly ordered.");
|
|
1599
|
-
}
|
|
1600
|
-
if (minKey === null) minKey = childValidation.minKey;
|
|
1601
|
-
maxKey = childValidation.maxKey;
|
|
1602
|
-
previousChildMax = childValidation.maxKey;
|
|
1603
|
-
leafCount += childValidation.leafCount;
|
|
1604
|
-
branchCount += childValidation.branchCount;
|
|
1605
|
-
entryCount += childValidation.entryCount;
|
|
1944
|
+
mergeChildValidation(state, acc, childValidation);
|
|
1606
1945
|
}
|
|
1607
|
-
return {
|
|
1946
|
+
return {
|
|
1947
|
+
minKey: acc.minKey,
|
|
1948
|
+
maxKey: acc.maxKey,
|
|
1949
|
+
leafDepth: acc.leafDepth,
|
|
1950
|
+
leafCount: acc.leafCount,
|
|
1951
|
+
branchCount: acc.branchCount,
|
|
1952
|
+
entryCount: acc.entryCount
|
|
1953
|
+
};
|
|
1608
1954
|
};
|
|
1609
1955
|
var assertInvariants = (state) => {
|
|
1610
1956
|
const validation = validateNode(state, state.root, 0);
|
|
1611
1957
|
if (validation.entryCount !== state.entryCount) {
|
|
1612
|
-
throw new BTreeInvariantError(
|
|
1958
|
+
throw new BTreeInvariantError(
|
|
1959
|
+
"Index entry count mismatch between tree traversal and tracked state."
|
|
1960
|
+
);
|
|
1613
1961
|
}
|
|
1614
1962
|
validateLeafLinks(state, validation.leafCount);
|
|
1615
1963
|
};
|
|
@@ -1663,44 +2011,56 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1663
2011
|
return putManyEntries(this.state, entries);
|
|
1664
2012
|
}
|
|
1665
2013
|
remove(key) {
|
|
1666
|
-
|
|
2014
|
+
const entry = removeFirstMatchingEntry(this.state, key);
|
|
2015
|
+
if (entry === null) return null;
|
|
2016
|
+
return freezeEntry(entry);
|
|
1667
2017
|
}
|
|
1668
2018
|
removeById(entryId) {
|
|
1669
2019
|
if (this.state.entryKeys === null) {
|
|
1670
2020
|
throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
|
|
1671
2021
|
}
|
|
1672
|
-
|
|
2022
|
+
const entry = removeEntryById(this.state, entryId);
|
|
2023
|
+
if (entry === null) return null;
|
|
2024
|
+
return freezeEntry(entry);
|
|
1673
2025
|
}
|
|
1674
2026
|
peekById(entryId) {
|
|
1675
2027
|
if (this.state.entryKeys === null) {
|
|
1676
2028
|
throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
|
|
1677
2029
|
}
|
|
1678
|
-
|
|
2030
|
+
const entry = peekEntryById(this.state, entryId);
|
|
2031
|
+
if (entry === null) return null;
|
|
2032
|
+
return freezeEntry(entry);
|
|
1679
2033
|
}
|
|
1680
2034
|
updateById(entryId, value) {
|
|
1681
2035
|
if (this.state.entryKeys === null) {
|
|
1682
2036
|
throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
|
|
1683
2037
|
}
|
|
1684
|
-
|
|
2038
|
+
const entry = updateEntryById(this.state, entryId, value);
|
|
2039
|
+
if (entry === null) return null;
|
|
2040
|
+
return freezeEntry(entry);
|
|
1685
2041
|
}
|
|
1686
2042
|
popFirst() {
|
|
1687
|
-
|
|
2043
|
+
const entry = popFirstEntry(this.state);
|
|
2044
|
+
if (entry === null) return null;
|
|
2045
|
+
return freezeEntry(entry);
|
|
1688
2046
|
}
|
|
1689
2047
|
peekFirst() {
|
|
1690
2048
|
if (this.state.entryCount === 0) {
|
|
1691
2049
|
return null;
|
|
1692
2050
|
}
|
|
1693
|
-
return leafEntryAt(this.state.leftmostLeaf, 0);
|
|
2051
|
+
return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
|
|
1694
2052
|
}
|
|
1695
2053
|
peekLast() {
|
|
1696
2054
|
if (this.state.entryCount === 0) {
|
|
1697
2055
|
return null;
|
|
1698
2056
|
}
|
|
1699
2057
|
const leaf = this.state.rightmostLeaf;
|
|
1700
|
-
return leafEntryAt(leaf, leafEntryCount(leaf) - 1);
|
|
2058
|
+
return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
|
|
1701
2059
|
}
|
|
1702
2060
|
popLast() {
|
|
1703
|
-
|
|
2061
|
+
const entry = popLastEntry(this.state);
|
|
2062
|
+
if (entry === null) return null;
|
|
2063
|
+
return freezeEntry(entry);
|
|
1704
2064
|
}
|
|
1705
2065
|
clear() {
|
|
1706
2066
|
const emptyLeaf = createLeafNode([], null);
|
|
@@ -1710,16 +2070,9 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1710
2070
|
this.state.entryCount = 0;
|
|
1711
2071
|
this.state._cursor.leaf = emptyLeaf;
|
|
1712
2072
|
this.state._cursor.index = 0;
|
|
1713
|
-
|
|
1714
|
-
this.state.entryKeys.clear();
|
|
1715
|
-
}
|
|
2073
|
+
this.state.entryKeys?.clear();
|
|
1716
2074
|
if (this.state.autoScale) {
|
|
1717
|
-
|
|
1718
|
-
this.state.maxLeafEntries = tier.maxLeaf;
|
|
1719
|
-
this.state.maxBranchChildren = tier.maxBranch;
|
|
1720
|
-
this.state.minLeafEntries = Math.ceil(tier.maxLeaf / 2);
|
|
1721
|
-
this.state.minBranchChildren = Math.ceil(tier.maxBranch / 2);
|
|
1722
|
-
this.state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
2075
|
+
resetAutoScaleToTier0(this.state);
|
|
1723
2076
|
}
|
|
1724
2077
|
}
|
|
1725
2078
|
get(key) {
|
|
@@ -1733,12 +2086,12 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1733
2086
|
findFirst(key) {
|
|
1734
2087
|
const found = findFirstMatchingUserKey(this.state, key);
|
|
1735
2088
|
if (found === null) return null;
|
|
1736
|
-
return leafEntryAt(found.leaf, found.index);
|
|
2089
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1737
2090
|
}
|
|
1738
2091
|
findLast(key) {
|
|
1739
2092
|
const found = findLastMatchingUserKey(this.state, key);
|
|
1740
2093
|
if (found === null) return null;
|
|
1741
|
-
return leafEntryAt(found.leaf, found.index);
|
|
2094
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1742
2095
|
}
|
|
1743
2096
|
nextHigherKey(key) {
|
|
1744
2097
|
return findNextHigherKey(this.state, key);
|
|
@@ -1749,7 +2102,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1749
2102
|
getPairOrNextLower(key) {
|
|
1750
2103
|
const found = findPairOrNextLower(this.state, key);
|
|
1751
2104
|
if (found === null) return null;
|
|
1752
|
-
return leafEntryAt(found.leaf, found.index);
|
|
2105
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1753
2106
|
}
|
|
1754
2107
|
count(startKey, endKey, options) {
|
|
1755
2108
|
return countRangeEntries(this.state, startKey, endKey, options);
|
|
@@ -1758,14 +2111,14 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1758
2111
|
return deleteRangeEntries(this.state, startKey, endKey, options);
|
|
1759
2112
|
}
|
|
1760
2113
|
range(startKey, endKey, options) {
|
|
1761
|
-
return
|
|
2114
|
+
return rangeQueryPublicEntries(this.state, startKey, endKey, options);
|
|
1762
2115
|
}
|
|
1763
2116
|
*entries() {
|
|
1764
2117
|
let leaf = this.state.leftmostLeaf;
|
|
1765
2118
|
while (leaf !== null) {
|
|
1766
2119
|
const count = leafEntryCount(leaf);
|
|
1767
2120
|
for (let i = 0; i < count; i += 1) {
|
|
1768
|
-
yield leafEntryAt(leaf, i);
|
|
2121
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1769
2122
|
}
|
|
1770
2123
|
leaf = leaf.next;
|
|
1771
2124
|
}
|
|
@@ -1775,7 +2128,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1775
2128
|
while (leaf !== null) {
|
|
1776
2129
|
const count = leafEntryCount(leaf);
|
|
1777
2130
|
for (let i = count - 1; i >= 0; i -= 1) {
|
|
1778
|
-
yield leafEntryAt(leaf, i);
|
|
2131
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1779
2132
|
}
|
|
1780
2133
|
leaf = leaf.prev;
|
|
1781
2134
|
}
|
|
@@ -1793,48 +2146,26 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1793
2146
|
[Symbol.iterator]() {
|
|
1794
2147
|
return this.entries();
|
|
1795
2148
|
}
|
|
2149
|
+
forEachRange(startKey, endKey, callback, options) {
|
|
2150
|
+
forEachRangeEntries(this.state, startKey, endKey, callback, options);
|
|
2151
|
+
}
|
|
1796
2152
|
forEach(callback, thisArg) {
|
|
1797
|
-
|
|
1798
|
-
while (leaf !== null) {
|
|
1799
|
-
const count = leafEntryCount(leaf);
|
|
1800
|
-
for (let i = 0; i < count; i += 1) {
|
|
1801
|
-
callback.call(thisArg, leafEntryAt(leaf, i));
|
|
1802
|
-
}
|
|
1803
|
-
leaf = leaf.next;
|
|
1804
|
-
}
|
|
2153
|
+
forEachEntry(this.state, callback, thisArg);
|
|
1805
2154
|
}
|
|
1806
2155
|
snapshot() {
|
|
1807
|
-
|
|
1808
|
-
let leaf = this.state.leftmostLeaf;
|
|
1809
|
-
let writeIdx = 0;
|
|
1810
|
-
while (leaf !== null) {
|
|
1811
|
-
const count = leafEntryCount(leaf);
|
|
1812
|
-
for (let i = 0; i < count; i += 1) {
|
|
1813
|
-
result[writeIdx++] = leafEntryAt(leaf, i);
|
|
1814
|
-
}
|
|
1815
|
-
leaf = leaf.next;
|
|
1816
|
-
}
|
|
1817
|
-
return result;
|
|
2156
|
+
return snapshotEntries(this.state);
|
|
1818
2157
|
}
|
|
1819
2158
|
clone() {
|
|
1820
|
-
const cloned = new _InMemoryBTree(
|
|
2159
|
+
const cloned = new _InMemoryBTree(
|
|
2160
|
+
buildConfigFromState(this.state)
|
|
2161
|
+
);
|
|
1821
2162
|
applyAutoScaleCapacitySnapshot(
|
|
1822
2163
|
cloned.state,
|
|
1823
2164
|
this.state.maxLeafEntries,
|
|
1824
2165
|
this.state.maxBranchChildren
|
|
1825
2166
|
);
|
|
1826
2167
|
if (this.state.entryCount > 0) {
|
|
1827
|
-
|
|
1828
|
-
let leaf = this.state.leftmostLeaf;
|
|
1829
|
-
let writeIdx = 0;
|
|
1830
|
-
while (leaf !== null) {
|
|
1831
|
-
const count = leafEntryCount(leaf);
|
|
1832
|
-
for (let i = 0; i < count; i += 1) {
|
|
1833
|
-
pairs[writeIdx++] = leafEntryAt(leaf, i);
|
|
1834
|
-
}
|
|
1835
|
-
leaf = leaf.next;
|
|
1836
|
-
}
|
|
1837
|
-
cloned.putMany(pairs);
|
|
2168
|
+
cloned.putMany(collectInternalEntries(this.state));
|
|
1838
2169
|
}
|
|
1839
2170
|
return cloned;
|
|
1840
2171
|
}
|
|
@@ -1843,26 +2174,19 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1843
2174
|
}
|
|
1844
2175
|
static fromJSON(json, compareKeys) {
|
|
1845
2176
|
validateBTreeJSON(json);
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
throw new BTreeValidationError("fromJSON: entries not sorted.");
|
|
1851
|
-
}
|
|
1852
|
-
if (strict && cmp === 0) {
|
|
1853
|
-
throw new BTreeValidationError(
|
|
1854
|
-
'fromJSON: duplicate keys require duplicateKeys "allow".'
|
|
1855
|
-
);
|
|
1856
|
-
}
|
|
1857
|
-
}
|
|
1858
|
-
const tree = new _InMemoryBTree(buildConfigFromJSON(json, compareKeys));
|
|
2177
|
+
validateBTreeJSONSortOrder(json, compareKeys);
|
|
2178
|
+
const tree = new _InMemoryBTree(
|
|
2179
|
+
buildConfigFromJSON(json, compareKeys)
|
|
2180
|
+
);
|
|
1859
2181
|
applyAutoScaleCapacitySnapshot(
|
|
1860
2182
|
tree.state,
|
|
1861
2183
|
json.config.maxLeafEntries,
|
|
1862
2184
|
json.config.maxBranchChildren
|
|
1863
2185
|
);
|
|
1864
2186
|
if (json.entries.length > 0) {
|
|
1865
|
-
const pairs = new Array(
|
|
2187
|
+
const pairs = new Array(
|
|
2188
|
+
json.entries.length
|
|
2189
|
+
);
|
|
1866
2190
|
for (let i = 0; i < json.entries.length; i += 1) {
|
|
1867
2191
|
pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
|
|
1868
2192
|
}
|