@frostpillar/frostpillar-btree 0.2.5 → 0.2.6
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 +33 -2
- package/README.md +33 -2
- package/dist/InMemoryBTree.d.ts +33 -0
- package/dist/btree/autoScale.d.ts +1 -0
- package/dist/btree/node-ops.d.ts +15 -0
- package/dist/btree/rangeQuery.d.ts +1 -0
- package/dist/btree/types.d.ts +12 -26
- package/dist/{chunk-CZFRT2NN.js → chunk-OWHENPGJ.js} +255 -198
- package/dist/concurrency/ConcurrentInMemoryBTree.d.ts +20 -16
- package/dist/concurrency/helpers.d.ts +8 -2
- package/dist/concurrency/types.d.ts +14 -1
- package/dist/concurrency/writeOps.d.ts +48 -0
- package/dist/core.cjs +255 -198
- 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 +531 -299
- package/dist/index.js +277 -102
- package/package.json +1 -1
package/dist/core.cjs
CHANGED
|
@@ -42,54 +42,10 @@ var BTreeInvariantError = class extends Error {
|
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
// src/btree/
|
|
46
|
-
var DEFAULT_MAX_LEAF_ENTRIES = 64;
|
|
47
|
-
var DEFAULT_MAX_BRANCH_CHILDREN = 64;
|
|
48
|
-
var MIN_NODE_CAPACITY = 3;
|
|
49
|
-
var MAX_NODE_CAPACITY = 16384;
|
|
50
|
-
var NODE_LEAF = 0;
|
|
51
|
-
var NODE_BRANCH = 1;
|
|
52
|
-
var normalizeDuplicateKeyPolicy = (value) => {
|
|
53
|
-
if (value === void 0) {
|
|
54
|
-
return "replace";
|
|
55
|
-
}
|
|
56
|
-
if (value !== "allow" && value !== "reject" && value !== "replace") {
|
|
57
|
-
throw new BTreeValidationError(
|
|
58
|
-
`Invalid duplicateKeys option.`
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
return value;
|
|
62
|
-
};
|
|
63
|
-
var isLeafNode = (node) => {
|
|
64
|
-
return node.kind === NODE_LEAF;
|
|
65
|
-
};
|
|
66
|
-
var writeMinKeyTo = (node, target) => {
|
|
67
|
-
if (node.kind === NODE_LEAF) {
|
|
68
|
-
if (node.entryOffset >= node.entries.length) return false;
|
|
69
|
-
const e = node.entries[node.entryOffset];
|
|
70
|
-
target.key = e.key;
|
|
71
|
-
target.sequence = e.entryId;
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
if (node.childOffset >= node.keys.length) return false;
|
|
75
|
-
target.key = node.keys[node.childOffset].key;
|
|
76
|
-
target.sequence = node.keys[node.childOffset].sequence;
|
|
77
|
-
return true;
|
|
78
|
-
};
|
|
79
|
-
var normalizeNodeCapacity = (value, field, defaultValue) => {
|
|
80
|
-
if (value === void 0) {
|
|
81
|
-
return defaultValue;
|
|
82
|
-
}
|
|
83
|
-
if (!Number.isInteger(value) || value < MIN_NODE_CAPACITY || value > MAX_NODE_CAPACITY) {
|
|
84
|
-
throw new BTreeValidationError(
|
|
85
|
-
`${field}: integer ${MIN_NODE_CAPACITY}\u2013${MAX_NODE_CAPACITY} required.`
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
return value;
|
|
89
|
-
};
|
|
45
|
+
// src/btree/node-ops.ts
|
|
90
46
|
var createLeafNode = (entries, parent) => {
|
|
91
47
|
return {
|
|
92
|
-
kind:
|
|
48
|
+
kind: 0,
|
|
93
49
|
entries,
|
|
94
50
|
entryOffset: 0,
|
|
95
51
|
parent,
|
|
@@ -101,7 +57,7 @@ var createLeafNode = (entries, parent) => {
|
|
|
101
57
|
var createBranchNode = (children, parent) => {
|
|
102
58
|
const keys = [];
|
|
103
59
|
const branch = {
|
|
104
|
-
kind:
|
|
60
|
+
kind: 1,
|
|
105
61
|
children,
|
|
106
62
|
keys,
|
|
107
63
|
childOffset: 0,
|
|
@@ -240,6 +196,56 @@ var branchRemoveAt = (branch, physIndex) => {
|
|
|
240
196
|
}
|
|
241
197
|
};
|
|
242
198
|
|
|
199
|
+
// src/btree/types.ts
|
|
200
|
+
var DEFAULT_MAX_LEAF_ENTRIES = 64;
|
|
201
|
+
var DEFAULT_MAX_BRANCH_CHILDREN = 64;
|
|
202
|
+
var MIN_NODE_CAPACITY = 3;
|
|
203
|
+
var MAX_NODE_CAPACITY = 16384;
|
|
204
|
+
var NODE_LEAF = 0;
|
|
205
|
+
var normalizeDuplicateKeyPolicy = (value) => {
|
|
206
|
+
if (value === void 0) {
|
|
207
|
+
return "replace";
|
|
208
|
+
}
|
|
209
|
+
if (value !== "allow" && value !== "reject" && value !== "replace") {
|
|
210
|
+
throw new BTreeValidationError(
|
|
211
|
+
`Invalid duplicateKeys option.`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
return value;
|
|
215
|
+
};
|
|
216
|
+
var toPublicEntry = (entry) => ({
|
|
217
|
+
entryId: entry.entryId,
|
|
218
|
+
key: entry.key,
|
|
219
|
+
value: entry.value
|
|
220
|
+
});
|
|
221
|
+
var isLeafNode = (node) => {
|
|
222
|
+
return node.kind === NODE_LEAF;
|
|
223
|
+
};
|
|
224
|
+
var writeMinKeyTo = (node, target) => {
|
|
225
|
+
if (node.kind === NODE_LEAF) {
|
|
226
|
+
if (node.entryOffset >= node.entries.length) return false;
|
|
227
|
+
const e = node.entries[node.entryOffset];
|
|
228
|
+
target.key = e.key;
|
|
229
|
+
target.sequence = e.entryId;
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
if (node.childOffset >= node.keys.length) return false;
|
|
233
|
+
target.key = node.keys[node.childOffset].key;
|
|
234
|
+
target.sequence = node.keys[node.childOffset].sequence;
|
|
235
|
+
return true;
|
|
236
|
+
};
|
|
237
|
+
var normalizeNodeCapacity = (value, field, defaultValue) => {
|
|
238
|
+
if (value === void 0) {
|
|
239
|
+
return defaultValue;
|
|
240
|
+
}
|
|
241
|
+
if (!Number.isInteger(value) || value < MIN_NODE_CAPACITY || value > MAX_NODE_CAPACITY) {
|
|
242
|
+
throw new BTreeValidationError(
|
|
243
|
+
`${field}: integer ${MIN_NODE_CAPACITY}\u2013${MAX_NODE_CAPACITY} required.`
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
return value;
|
|
247
|
+
};
|
|
248
|
+
|
|
243
249
|
// src/btree/navigation.ts
|
|
244
250
|
var selectBranchChild = (compare, branch, userKey, sequence) => {
|
|
245
251
|
const off = branch.childOffset;
|
|
@@ -253,7 +259,7 @@ var selectBranchChild = (compare, branch, userKey, sequence) => {
|
|
|
253
259
|
const mid = lower + upper >>> 1;
|
|
254
260
|
const k = branch.keys[mid];
|
|
255
261
|
const cmp = compare(k.key, userKey);
|
|
256
|
-
if ((cmp !== 0 ? cmp : k.sequence - sequence) <= 0) {
|
|
262
|
+
if ((cmp !== 0 ? cmp : k.sequence < sequence ? -1 : k.sequence > sequence ? 1 : 0) <= 0) {
|
|
257
263
|
selectedIndex = mid;
|
|
258
264
|
lower = mid + 1;
|
|
259
265
|
} else {
|
|
@@ -278,7 +284,7 @@ var lowerBoundInLeaf = (state, leaf, userKey, sequence) => {
|
|
|
278
284
|
const mid = lower + upper >>> 1;
|
|
279
285
|
const e = leaf.entries[mid];
|
|
280
286
|
const cmp = compare(e.key, userKey);
|
|
281
|
-
if ((cmp !== 0 ? cmp : e.entryId - sequence) < 0) {
|
|
287
|
+
if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) < 0) {
|
|
282
288
|
lower = mid + 1;
|
|
283
289
|
} else {
|
|
284
290
|
upper = mid;
|
|
@@ -294,7 +300,7 @@ var upperBoundInLeaf = (state, leaf, userKey, sequence) => {
|
|
|
294
300
|
const mid = lower + upper >>> 1;
|
|
295
301
|
const e = leaf.entries[mid];
|
|
296
302
|
const cmp = compare(e.key, userKey);
|
|
297
|
-
if ((cmp !== 0 ? cmp : e.entryId - sequence) <= 0) {
|
|
303
|
+
if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) <= 0) {
|
|
298
304
|
lower = mid + 1;
|
|
299
305
|
} else {
|
|
300
306
|
upper = mid;
|
|
@@ -431,6 +437,114 @@ var findPairOrNextLower = (state, key) => {
|
|
|
431
437
|
return null;
|
|
432
438
|
};
|
|
433
439
|
|
|
440
|
+
// src/btree/rangeQuery.ts
|
|
441
|
+
function isEmptyRange(compare, startKey, endKey, options) {
|
|
442
|
+
const cmp = compare(startKey, endKey);
|
|
443
|
+
if (cmp > 0) return true;
|
|
444
|
+
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
445
|
+
const upperExclusive = options?.upperBound === "exclusive";
|
|
446
|
+
return lowerExclusive && upperExclusive && cmp === 0;
|
|
447
|
+
}
|
|
448
|
+
var initRangeCursor = (state, startKey, endKey, options) => {
|
|
449
|
+
if (state.entryCount === 0) return null;
|
|
450
|
+
const compare = state.compareKeys;
|
|
451
|
+
if (isEmptyRange(compare, startKey, endKey, options)) return null;
|
|
452
|
+
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
453
|
+
const upperExclusive = options?.upperBound === "exclusive";
|
|
454
|
+
const startSeq = lowerExclusive ? Number.MAX_SAFE_INTEGER : 0;
|
|
455
|
+
const leaf = findLeafForKey(state, startKey, startSeq);
|
|
456
|
+
const index = lowerExclusive ? upperBoundInLeaf(state, leaf, startKey, Number.MAX_SAFE_INTEGER) : lowerBoundInLeaf(state, leaf, startKey, 0);
|
|
457
|
+
return { leaf, index, compare, upperExclusive };
|
|
458
|
+
};
|
|
459
|
+
var countRangeEntries = (state, startKey, endKey, options) => {
|
|
460
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
461
|
+
if (cursor === null) return 0;
|
|
462
|
+
let cursorLeaf = cursor.leaf;
|
|
463
|
+
let cursorIndex = cursor.index;
|
|
464
|
+
const { compare, upperExclusive } = cursor;
|
|
465
|
+
let count = 0;
|
|
466
|
+
while (cursorLeaf !== null) {
|
|
467
|
+
const leafCount = leafEntryCount(cursorLeaf);
|
|
468
|
+
if (cursorIndex >= leafCount) {
|
|
469
|
+
cursorLeaf = cursorLeaf.next;
|
|
470
|
+
cursorIndex = 0;
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
|
|
474
|
+
const cmpLast = compare(lastEntry.key, endKey);
|
|
475
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
476
|
+
count += leafCount - cursorIndex;
|
|
477
|
+
cursorLeaf = cursorLeaf.next;
|
|
478
|
+
cursorIndex = 0;
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
482
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
|
|
483
|
+
const limit = endBound < leafCount ? endBound : leafCount;
|
|
484
|
+
count += limit - cursorIndex;
|
|
485
|
+
return count;
|
|
486
|
+
}
|
|
487
|
+
return count;
|
|
488
|
+
};
|
|
489
|
+
var RANGE_PREALLOC_THRESHOLD = 200;
|
|
490
|
+
var allocateRangeOutput = (state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options) => {
|
|
491
|
+
const firstLeafCount = leafEntryCount(cursorLeaf);
|
|
492
|
+
const firstLeafRemainder = firstLeafCount - cursorIndex;
|
|
493
|
+
if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursorLeaf.next !== null) {
|
|
494
|
+
const lastEntry = leafEntryAt(cursorLeaf, firstLeafCount - 1);
|
|
495
|
+
const cmpLast = compare(lastEntry.key, endKey);
|
|
496
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
497
|
+
const total = countRangeEntries(state, startKey, endKey, options);
|
|
498
|
+
return new Array(total);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
return [];
|
|
502
|
+
};
|
|
503
|
+
var appendLeafSlice = (leaf, from, to, output, useIndexed, writeIdx) => {
|
|
504
|
+
if (useIndexed) {
|
|
505
|
+
for (let i = from; i < to; i += 1) {
|
|
506
|
+
output[writeIdx++] = leafEntryAt(leaf, i);
|
|
507
|
+
}
|
|
508
|
+
} else {
|
|
509
|
+
for (let i = from; i < to; i += 1) {
|
|
510
|
+
output.push(leafEntryAt(leaf, i));
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return writeIdx;
|
|
514
|
+
};
|
|
515
|
+
var rangeQueryEntries = (state, startKey, endKey, options) => {
|
|
516
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
517
|
+
if (cursor === null) return [];
|
|
518
|
+
let cursorLeaf = cursor.leaf;
|
|
519
|
+
let cursorIndex = cursor.index;
|
|
520
|
+
const { compare, upperExclusive } = cursor;
|
|
521
|
+
const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
|
|
522
|
+
let writeIdx = 0;
|
|
523
|
+
const useIndexed = output.length > 0;
|
|
524
|
+
while (cursorLeaf !== null) {
|
|
525
|
+
const leafCount = leafEntryCount(cursorLeaf);
|
|
526
|
+
if (cursorIndex >= leafCount) {
|
|
527
|
+
cursorLeaf = cursorLeaf.next;
|
|
528
|
+
cursorIndex = 0;
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
|
|
532
|
+
const cmpLast = compare(lastEntry.key, endKey);
|
|
533
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
534
|
+
writeIdx = appendLeafSlice(cursorLeaf, cursorIndex, leafCount, output, useIndexed, writeIdx);
|
|
535
|
+
cursorLeaf = cursorLeaf.next;
|
|
536
|
+
cursorIndex = 0;
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
540
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
|
|
541
|
+
const limit = endBound < leafCount ? endBound : leafCount;
|
|
542
|
+
appendLeafSlice(cursorLeaf, cursorIndex, limit, output, useIndexed, writeIdx);
|
|
543
|
+
return output;
|
|
544
|
+
}
|
|
545
|
+
return output;
|
|
546
|
+
};
|
|
547
|
+
|
|
434
548
|
// src/btree/rebalance.ts
|
|
435
549
|
var updateMinKeyInAncestors = (node) => {
|
|
436
550
|
let current = node;
|
|
@@ -656,16 +770,7 @@ var findRemoveEnd = (state, leaf, idx, endKey, upperExclusive) => {
|
|
|
656
770
|
}
|
|
657
771
|
return removeEnd;
|
|
658
772
|
};
|
|
659
|
-
var
|
|
660
|
-
let h = 0;
|
|
661
|
-
let n = state.root;
|
|
662
|
-
while (!isLeafNode(n)) {
|
|
663
|
-
n = n.children[n.childOffset];
|
|
664
|
-
h += 1;
|
|
665
|
-
}
|
|
666
|
-
return h;
|
|
667
|
-
};
|
|
668
|
-
var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth) => {
|
|
773
|
+
var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
669
774
|
if (state.entryKeys !== null) {
|
|
670
775
|
for (let i = idx; i < idx + removeCount; i += 1) {
|
|
671
776
|
state.entryKeys.delete(leafEntryAt(leaf, i).entryId);
|
|
@@ -680,11 +785,11 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
|
|
|
680
785
|
updateMinKeyInAncestors(leaf);
|
|
681
786
|
}
|
|
682
787
|
const countAfterSplice = leafEntryCount(leaf);
|
|
683
|
-
let
|
|
684
|
-
while (
|
|
788
|
+
let safetyGuard = state.minLeafEntries + 4;
|
|
789
|
+
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < state.minLeafEntries) {
|
|
685
790
|
rebalanceAfterLeafRemoval(state, leaf);
|
|
686
791
|
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf) break;
|
|
687
|
-
|
|
792
|
+
safetyGuard -= 1;
|
|
688
793
|
}
|
|
689
794
|
if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
|
|
690
795
|
updateMinKeyInAncestors(leaf);
|
|
@@ -694,12 +799,9 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
|
|
|
694
799
|
var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
|
|
695
800
|
var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
696
801
|
if (state.entryCount === 0) return 0;
|
|
697
|
-
|
|
698
|
-
if (boundCompared > 0) return 0;
|
|
802
|
+
if (isEmptyRange(state.compareKeys, startKey, endKey, options)) return 0;
|
|
699
803
|
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
700
804
|
const upperExclusive = options?.upperBound === "exclusive";
|
|
701
|
-
if (lowerExclusive && upperExclusive && boundCompared === 0) return 0;
|
|
702
|
-
const treeHeight = computeTreeHeight(state);
|
|
703
805
|
let deleted = 0;
|
|
704
806
|
let needsNavigate = true;
|
|
705
807
|
let leaf = null;
|
|
@@ -717,7 +819,7 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
717
819
|
const removeEnd = findRemoveEnd(state, leaf, idx, endKey, upperExclusive);
|
|
718
820
|
const removeCount = removeEnd - idx;
|
|
719
821
|
if (removeCount === 0) break;
|
|
720
|
-
const countAfterSplice = spliceLeafAndRebalance(state, leaf, idx, removeCount
|
|
822
|
+
const countAfterSplice = spliceLeafAndRebalance(state, leaf, idx, removeCount);
|
|
721
823
|
deleted += removeCount;
|
|
722
824
|
if (removeEnd < count) break;
|
|
723
825
|
if (!isLeafStillValid(state, leaf)) {
|
|
@@ -736,6 +838,7 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
736
838
|
};
|
|
737
839
|
|
|
738
840
|
// src/btree/autoScale.ts
|
|
841
|
+
var minOccupancy = (max) => Math.ceil(max / 2);
|
|
739
842
|
var AUTO_SCALE_TIERS = [
|
|
740
843
|
{ threshold: 0, maxLeaf: 32, maxBranch: 32 },
|
|
741
844
|
{ threshold: 1e3, maxLeaf: 64, maxBranch: 64 },
|
|
@@ -787,8 +890,8 @@ var createInitialState = (config) => {
|
|
|
787
890
|
maxLeafEntries,
|
|
788
891
|
maxBranchChildren,
|
|
789
892
|
duplicateKeys,
|
|
790
|
-
minLeafEntries:
|
|
791
|
-
minBranchChildren:
|
|
893
|
+
minLeafEntries: minOccupancy(maxLeafEntries),
|
|
894
|
+
minBranchChildren: minOccupancy(maxBranchChildren),
|
|
792
895
|
root: emptyLeaf,
|
|
793
896
|
leftmostLeaf: emptyLeaf,
|
|
794
897
|
rightmostLeaf: emptyLeaf,
|
|
@@ -805,11 +908,11 @@ var maybeAutoScale = (state) => {
|
|
|
805
908
|
const { maxLeaf, maxBranch } = computeAutoScaleTier(state.entryCount);
|
|
806
909
|
if (maxLeaf > state.maxLeafEntries) {
|
|
807
910
|
state.maxLeafEntries = maxLeaf;
|
|
808
|
-
state.minLeafEntries =
|
|
911
|
+
state.minLeafEntries = minOccupancy(maxLeaf);
|
|
809
912
|
}
|
|
810
913
|
if (maxBranch > state.maxBranchChildren) {
|
|
811
914
|
state.maxBranchChildren = maxBranch;
|
|
812
|
-
state.minBranchChildren =
|
|
915
|
+
state.minBranchChildren = minOccupancy(maxBranch);
|
|
813
916
|
}
|
|
814
917
|
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(state.entryCount);
|
|
815
918
|
};
|
|
@@ -835,8 +938,8 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
|
|
|
835
938
|
}
|
|
836
939
|
state.maxLeafEntries = normalizedLeaf;
|
|
837
940
|
state.maxBranchChildren = normalizedBranch;
|
|
838
|
-
state.minLeafEntries =
|
|
839
|
-
state.minBranchChildren =
|
|
941
|
+
state.minLeafEntries = minOccupancy(normalizedLeaf);
|
|
942
|
+
state.minBranchChildren = minOccupancy(normalizedBranch);
|
|
840
943
|
};
|
|
841
944
|
|
|
842
945
|
// src/btree/bulkLoad.ts
|
|
@@ -1061,6 +1164,9 @@ var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
|
1061
1164
|
return { leaf: targetLeaf, index };
|
|
1062
1165
|
};
|
|
1063
1166
|
var removeEntryById = (state, entryId) => {
|
|
1167
|
+
if (state.entryKeys === null) {
|
|
1168
|
+
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1169
|
+
}
|
|
1064
1170
|
const userKey = state.entryKeys.get(entryId);
|
|
1065
1171
|
if (userKey === void 0) return null;
|
|
1066
1172
|
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
@@ -1078,6 +1184,9 @@ var removeEntryById = (state, entryId) => {
|
|
|
1078
1184
|
return entry;
|
|
1079
1185
|
};
|
|
1080
1186
|
var peekEntryById = (state, entryId) => {
|
|
1187
|
+
if (state.entryKeys === null) {
|
|
1188
|
+
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1189
|
+
}
|
|
1081
1190
|
const userKey = state.entryKeys.get(entryId);
|
|
1082
1191
|
if (userKey === void 0) return null;
|
|
1083
1192
|
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
@@ -1086,6 +1195,9 @@ var peekEntryById = (state, entryId) => {
|
|
|
1086
1195
|
return entry;
|
|
1087
1196
|
};
|
|
1088
1197
|
var updateEntryById = (state, entryId, newValue) => {
|
|
1198
|
+
if (state.entryKeys === null) {
|
|
1199
|
+
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1200
|
+
}
|
|
1089
1201
|
const userKey = state.entryKeys.get(entryId);
|
|
1090
1202
|
if (userKey === void 0) return null;
|
|
1091
1203
|
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
@@ -1099,9 +1211,12 @@ var putManyEntries = (state, entries) => {
|
|
|
1099
1211
|
const strictlyAscending = state.duplicateKeys !== "allow";
|
|
1100
1212
|
for (let i = 1; i < entries.length; i += 1) {
|
|
1101
1213
|
const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
|
|
1102
|
-
if (
|
|
1214
|
+
if (cmp > 0) {
|
|
1215
|
+
throw new BTreeValidationError("putMany: entries not in ascending order.");
|
|
1216
|
+
}
|
|
1217
|
+
if (strictlyAscending && cmp === 0) {
|
|
1103
1218
|
throw new BTreeValidationError(
|
|
1104
|
-
|
|
1219
|
+
state.duplicateKeys === "reject" ? "putMany: duplicate key rejected." : "putMany: equal keys not allowed in strict mode."
|
|
1105
1220
|
);
|
|
1106
1221
|
}
|
|
1107
1222
|
}
|
|
@@ -1119,109 +1234,6 @@ var putManyEntries = (state, entries) => {
|
|
|
1119
1234
|
return bulkLoadEntries(state, entries);
|
|
1120
1235
|
};
|
|
1121
1236
|
|
|
1122
|
-
// src/btree/rangeQuery.ts
|
|
1123
|
-
var initRangeCursor = (state, startKey, endKey, options) => {
|
|
1124
|
-
if (state.entryCount === 0) return null;
|
|
1125
|
-
const compare = state.compareKeys;
|
|
1126
|
-
const boundCompared = compare(startKey, endKey);
|
|
1127
|
-
if (boundCompared > 0) return null;
|
|
1128
|
-
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
1129
|
-
const upperExclusive = options?.upperBound === "exclusive";
|
|
1130
|
-
if (lowerExclusive && upperExclusive && boundCompared === 0) return null;
|
|
1131
|
-
const startSeq = lowerExclusive ? Number.MAX_SAFE_INTEGER : 0;
|
|
1132
|
-
const leaf = findLeafForKey(state, startKey, startSeq);
|
|
1133
|
-
const index = lowerExclusive ? upperBoundInLeaf(state, leaf, startKey, Number.MAX_SAFE_INTEGER) : lowerBoundInLeaf(state, leaf, startKey, 0);
|
|
1134
|
-
return { leaf, index, compare, upperExclusive };
|
|
1135
|
-
};
|
|
1136
|
-
var countRangeEntries = (state, startKey, endKey, options) => {
|
|
1137
|
-
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
1138
|
-
if (cursor === null) return 0;
|
|
1139
|
-
let cursorLeaf = cursor.leaf;
|
|
1140
|
-
let cursorIndex = cursor.index;
|
|
1141
|
-
const { compare, upperExclusive } = cursor;
|
|
1142
|
-
let count = 0;
|
|
1143
|
-
while (cursorLeaf !== null) {
|
|
1144
|
-
const leafCount = leafEntryCount(cursorLeaf);
|
|
1145
|
-
if (cursorIndex >= leafCount) {
|
|
1146
|
-
cursorLeaf = cursorLeaf.next;
|
|
1147
|
-
cursorIndex = 0;
|
|
1148
|
-
continue;
|
|
1149
|
-
}
|
|
1150
|
-
const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
|
|
1151
|
-
const cmpLast = compare(lastEntry.key, endKey);
|
|
1152
|
-
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
1153
|
-
count += leafCount - cursorIndex;
|
|
1154
|
-
cursorLeaf = cursorLeaf.next;
|
|
1155
|
-
cursorIndex = 0;
|
|
1156
|
-
continue;
|
|
1157
|
-
}
|
|
1158
|
-
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
1159
|
-
const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
|
|
1160
|
-
const limit = endBound < leafCount ? endBound : leafCount;
|
|
1161
|
-
count += limit - cursorIndex;
|
|
1162
|
-
return count;
|
|
1163
|
-
}
|
|
1164
|
-
return count;
|
|
1165
|
-
};
|
|
1166
|
-
var RANGE_PREALLOC_THRESHOLD = 200;
|
|
1167
|
-
var allocateRangeOutput = (state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options) => {
|
|
1168
|
-
const firstLeafCount = leafEntryCount(cursorLeaf);
|
|
1169
|
-
const firstLeafRemainder = firstLeafCount - cursorIndex;
|
|
1170
|
-
if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursorLeaf.next !== null) {
|
|
1171
|
-
const lastEntry = leafEntryAt(cursorLeaf, firstLeafCount - 1);
|
|
1172
|
-
const cmpLast = compare(lastEntry.key, endKey);
|
|
1173
|
-
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
1174
|
-
const total = countRangeEntries(state, startKey, endKey, options);
|
|
1175
|
-
return new Array(total);
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
return [];
|
|
1179
|
-
};
|
|
1180
|
-
var appendLeafSlice = (leaf, from, to, output, useIndexed, writeIdx) => {
|
|
1181
|
-
if (useIndexed) {
|
|
1182
|
-
for (let i = from; i < to; i += 1) {
|
|
1183
|
-
output[writeIdx++] = leafEntryAt(leaf, i);
|
|
1184
|
-
}
|
|
1185
|
-
} else {
|
|
1186
|
-
for (let i = from; i < to; i += 1) {
|
|
1187
|
-
output.push(leafEntryAt(leaf, i));
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
return writeIdx;
|
|
1191
|
-
};
|
|
1192
|
-
var rangeQueryEntries = (state, startKey, endKey, options) => {
|
|
1193
|
-
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
1194
|
-
if (cursor === null) return [];
|
|
1195
|
-
let cursorLeaf = cursor.leaf;
|
|
1196
|
-
let cursorIndex = cursor.index;
|
|
1197
|
-
const { compare, upperExclusive } = cursor;
|
|
1198
|
-
const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
|
|
1199
|
-
let writeIdx = 0;
|
|
1200
|
-
const useIndexed = output.length > 0;
|
|
1201
|
-
while (cursorLeaf !== null) {
|
|
1202
|
-
const leafCount = leafEntryCount(cursorLeaf);
|
|
1203
|
-
if (cursorIndex >= leafCount) {
|
|
1204
|
-
cursorLeaf = cursorLeaf.next;
|
|
1205
|
-
cursorIndex = 0;
|
|
1206
|
-
continue;
|
|
1207
|
-
}
|
|
1208
|
-
const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
|
|
1209
|
-
const cmpLast = compare(lastEntry.key, endKey);
|
|
1210
|
-
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
1211
|
-
writeIdx = appendLeafSlice(cursorLeaf, cursorIndex, leafCount, output, useIndexed, writeIdx);
|
|
1212
|
-
cursorLeaf = cursorLeaf.next;
|
|
1213
|
-
cursorIndex = 0;
|
|
1214
|
-
continue;
|
|
1215
|
-
}
|
|
1216
|
-
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
1217
|
-
const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
|
|
1218
|
-
const limit = endBound < leafCount ? endBound : leafCount;
|
|
1219
|
-
appendLeafSlice(cursorLeaf, cursorIndex, limit, output, useIndexed, writeIdx);
|
|
1220
|
-
return output;
|
|
1221
|
-
}
|
|
1222
|
-
return output;
|
|
1223
|
-
};
|
|
1224
|
-
|
|
1225
1237
|
// src/btree/serialization.ts
|
|
1226
1238
|
var buildConfigFromState = (state) => {
|
|
1227
1239
|
const config = {
|
|
@@ -1351,7 +1363,7 @@ var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
|
|
|
1351
1363
|
if (cmp !== 0) {
|
|
1352
1364
|
return cmp;
|
|
1353
1365
|
}
|
|
1354
|
-
return leftSeq - rightSeq;
|
|
1366
|
+
return leftSeq < rightSeq ? -1 : leftSeq > rightSeq ? 1 : 0;
|
|
1355
1367
|
};
|
|
1356
1368
|
var getNodeMaxKey = (node) => {
|
|
1357
1369
|
if (isLeafNode(node)) {
|
|
@@ -1684,44 +1696,56 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1684
1696
|
return putManyEntries(this.state, entries);
|
|
1685
1697
|
}
|
|
1686
1698
|
remove(key) {
|
|
1687
|
-
|
|
1699
|
+
const entry = removeFirstMatchingEntry(this.state, key);
|
|
1700
|
+
if (entry === null) return null;
|
|
1701
|
+
return toPublicEntry(entry);
|
|
1688
1702
|
}
|
|
1689
1703
|
removeById(entryId) {
|
|
1690
1704
|
if (this.state.entryKeys === null) {
|
|
1691
1705
|
throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
|
|
1692
1706
|
}
|
|
1693
|
-
|
|
1707
|
+
const entry = removeEntryById(this.state, entryId);
|
|
1708
|
+
if (entry === null) return null;
|
|
1709
|
+
return toPublicEntry(entry);
|
|
1694
1710
|
}
|
|
1695
1711
|
peekById(entryId) {
|
|
1696
1712
|
if (this.state.entryKeys === null) {
|
|
1697
1713
|
throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
|
|
1698
1714
|
}
|
|
1699
|
-
|
|
1715
|
+
const entry = peekEntryById(this.state, entryId);
|
|
1716
|
+
if (entry === null) return null;
|
|
1717
|
+
return toPublicEntry(entry);
|
|
1700
1718
|
}
|
|
1701
1719
|
updateById(entryId, value) {
|
|
1702
1720
|
if (this.state.entryKeys === null) {
|
|
1703
1721
|
throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
|
|
1704
1722
|
}
|
|
1705
|
-
|
|
1723
|
+
const entry = updateEntryById(this.state, entryId, value);
|
|
1724
|
+
if (entry === null) return null;
|
|
1725
|
+
return toPublicEntry(entry);
|
|
1706
1726
|
}
|
|
1707
1727
|
popFirst() {
|
|
1708
|
-
|
|
1728
|
+
const entry = popFirstEntry(this.state);
|
|
1729
|
+
if (entry === null) return null;
|
|
1730
|
+
return toPublicEntry(entry);
|
|
1709
1731
|
}
|
|
1710
1732
|
peekFirst() {
|
|
1711
1733
|
if (this.state.entryCount === 0) {
|
|
1712
1734
|
return null;
|
|
1713
1735
|
}
|
|
1714
|
-
return leafEntryAt(this.state.leftmostLeaf, 0);
|
|
1736
|
+
return toPublicEntry(leafEntryAt(this.state.leftmostLeaf, 0));
|
|
1715
1737
|
}
|
|
1716
1738
|
peekLast() {
|
|
1717
1739
|
if (this.state.entryCount === 0) {
|
|
1718
1740
|
return null;
|
|
1719
1741
|
}
|
|
1720
1742
|
const leaf = this.state.rightmostLeaf;
|
|
1721
|
-
return leafEntryAt(leaf, leafEntryCount(leaf) - 1);
|
|
1743
|
+
return toPublicEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
|
|
1722
1744
|
}
|
|
1723
1745
|
popLast() {
|
|
1724
|
-
|
|
1746
|
+
const entry = popLastEntry(this.state);
|
|
1747
|
+
if (entry === null) return null;
|
|
1748
|
+
return toPublicEntry(entry);
|
|
1725
1749
|
}
|
|
1726
1750
|
clear() {
|
|
1727
1751
|
const emptyLeaf = createLeafNode([], null);
|
|
@@ -1738,8 +1762,8 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1738
1762
|
const tier = computeAutoScaleTier(0);
|
|
1739
1763
|
this.state.maxLeafEntries = tier.maxLeaf;
|
|
1740
1764
|
this.state.maxBranchChildren = tier.maxBranch;
|
|
1741
|
-
this.state.minLeafEntries =
|
|
1742
|
-
this.state.minBranchChildren =
|
|
1765
|
+
this.state.minLeafEntries = minOccupancy(tier.maxLeaf);
|
|
1766
|
+
this.state.minBranchChildren = minOccupancy(tier.maxBranch);
|
|
1743
1767
|
this.state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
1744
1768
|
}
|
|
1745
1769
|
}
|
|
@@ -1754,39 +1778,65 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1754
1778
|
findFirst(key) {
|
|
1755
1779
|
const found = findFirstMatchingUserKey(this.state, key);
|
|
1756
1780
|
if (found === null) return null;
|
|
1757
|
-
return leafEntryAt(found.leaf, found.index);
|
|
1781
|
+
return toPublicEntry(leafEntryAt(found.leaf, found.index));
|
|
1758
1782
|
}
|
|
1783
|
+
/**
|
|
1784
|
+
* Returns the last entry whose key matches `key`, or `null` if not found.
|
|
1785
|
+
* Useful when `duplicateKeys` is `'allow'` and multiple entries share the same key.
|
|
1786
|
+
*/
|
|
1759
1787
|
findLast(key) {
|
|
1760
1788
|
const found = findLastMatchingUserKey(this.state, key);
|
|
1761
1789
|
if (found === null) return null;
|
|
1762
|
-
return leafEntryAt(found.leaf, found.index);
|
|
1790
|
+
return toPublicEntry(leafEntryAt(found.leaf, found.index));
|
|
1763
1791
|
}
|
|
1792
|
+
/**
|
|
1793
|
+
* Returns the smallest key in the tree that is strictly greater than `key`,
|
|
1794
|
+
* or `null` if no such key exists.
|
|
1795
|
+
*/
|
|
1764
1796
|
nextHigherKey(key) {
|
|
1765
1797
|
return findNextHigherKey(this.state, key);
|
|
1766
1798
|
}
|
|
1799
|
+
/**
|
|
1800
|
+
* Returns the largest key in the tree that is strictly less than `key`,
|
|
1801
|
+
* or `null` if no such key exists.
|
|
1802
|
+
*/
|
|
1767
1803
|
nextLowerKey(key) {
|
|
1768
1804
|
return findNextLowerKey(this.state, key);
|
|
1769
1805
|
}
|
|
1806
|
+
/**
|
|
1807
|
+
* Returns the entry for `key` if it exists; otherwise returns the entry with
|
|
1808
|
+
* the largest key strictly less than `key`. Returns `null` when the tree is
|
|
1809
|
+
* empty or every key is greater than `key`.
|
|
1810
|
+
*/
|
|
1770
1811
|
getPairOrNextLower(key) {
|
|
1771
1812
|
const found = findPairOrNextLower(this.state, key);
|
|
1772
1813
|
if (found === null) return null;
|
|
1773
|
-
return leafEntryAt(found.leaf, found.index);
|
|
1814
|
+
return toPublicEntry(leafEntryAt(found.leaf, found.index));
|
|
1774
1815
|
}
|
|
1816
|
+
/**
|
|
1817
|
+
* Returns the number of entries whose keys fall within [`startKey`, `endKey`].
|
|
1818
|
+
* Pass `options` to make either bound exclusive.
|
|
1819
|
+
*/
|
|
1775
1820
|
count(startKey, endKey, options) {
|
|
1776
1821
|
return countRangeEntries(this.state, startKey, endKey, options);
|
|
1777
1822
|
}
|
|
1823
|
+
/**
|
|
1824
|
+
* Deletes all entries whose keys fall within [`startKey`, `endKey`].
|
|
1825
|
+
* Pass `options` to make either bound exclusive.
|
|
1826
|
+
* @returns The number of entries deleted.
|
|
1827
|
+
*/
|
|
1778
1828
|
deleteRange(startKey, endKey, options) {
|
|
1779
1829
|
return deleteRangeEntries(this.state, startKey, endKey, options);
|
|
1780
1830
|
}
|
|
1781
1831
|
range(startKey, endKey, options) {
|
|
1782
|
-
return rangeQueryEntries(this.state, startKey, endKey, options);
|
|
1832
|
+
return rangeQueryEntries(this.state, startKey, endKey, options).map(toPublicEntry);
|
|
1783
1833
|
}
|
|
1784
1834
|
*entries() {
|
|
1785
1835
|
let leaf = this.state.leftmostLeaf;
|
|
1786
1836
|
while (leaf !== null) {
|
|
1787
1837
|
const count = leafEntryCount(leaf);
|
|
1788
1838
|
for (let i = 0; i < count; i += 1) {
|
|
1789
|
-
yield leafEntryAt(leaf, i);
|
|
1839
|
+
yield toPublicEntry(leafEntryAt(leaf, i));
|
|
1790
1840
|
}
|
|
1791
1841
|
leaf = leaf.next;
|
|
1792
1842
|
}
|
|
@@ -1796,7 +1846,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1796
1846
|
while (leaf !== null) {
|
|
1797
1847
|
const count = leafEntryCount(leaf);
|
|
1798
1848
|
for (let i = count - 1; i >= 0; i -= 1) {
|
|
1799
|
-
yield leafEntryAt(leaf, i);
|
|
1849
|
+
yield toPublicEntry(leafEntryAt(leaf, i));
|
|
1800
1850
|
}
|
|
1801
1851
|
leaf = leaf.prev;
|
|
1802
1852
|
}
|
|
@@ -1819,7 +1869,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1819
1869
|
while (leaf !== null) {
|
|
1820
1870
|
const count = leafEntryCount(leaf);
|
|
1821
1871
|
for (let i = 0; i < count; i += 1) {
|
|
1822
|
-
callback.call(thisArg, leafEntryAt(leaf, i));
|
|
1872
|
+
callback.call(thisArg, toPublicEntry(leafEntryAt(leaf, i)));
|
|
1823
1873
|
}
|
|
1824
1874
|
leaf = leaf.next;
|
|
1825
1875
|
}
|
|
@@ -1831,12 +1881,19 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1831
1881
|
while (leaf !== null) {
|
|
1832
1882
|
const count = leafEntryCount(leaf);
|
|
1833
1883
|
for (let i = 0; i < count; i += 1) {
|
|
1834
|
-
result[writeIdx++] = leafEntryAt(leaf, i);
|
|
1884
|
+
result[writeIdx++] = toPublicEntry(leafEntryAt(leaf, i));
|
|
1835
1885
|
}
|
|
1836
1886
|
leaf = leaf.next;
|
|
1837
1887
|
}
|
|
1838
1888
|
return result;
|
|
1839
1889
|
}
|
|
1890
|
+
/**
|
|
1891
|
+
* Returns a structurally independent `InMemoryBTree` with identical
|
|
1892
|
+
* configuration and entries. The tree structure (nodes, links, entry IDs)
|
|
1893
|
+
* is fully independent, but stored key and value references are shared
|
|
1894
|
+
* with the source tree.
|
|
1895
|
+
* Note: `EntryId` values are reassigned in the clone — IDs from the source tree are not valid for the clone.
|
|
1896
|
+
*/
|
|
1840
1897
|
clone() {
|
|
1841
1898
|
const cloned = new _InMemoryBTree(buildConfigFromState(this.state));
|
|
1842
1899
|
applyAutoScaleCapacitySnapshot(
|