@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
|
@@ -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,
|
|
@@ -219,6 +175,56 @@ var branchRemoveAt = (branch, physIndex) => {
|
|
|
219
175
|
}
|
|
220
176
|
};
|
|
221
177
|
|
|
178
|
+
// src/btree/types.ts
|
|
179
|
+
var DEFAULT_MAX_LEAF_ENTRIES = 64;
|
|
180
|
+
var DEFAULT_MAX_BRANCH_CHILDREN = 64;
|
|
181
|
+
var MIN_NODE_CAPACITY = 3;
|
|
182
|
+
var MAX_NODE_CAPACITY = 16384;
|
|
183
|
+
var NODE_LEAF = 0;
|
|
184
|
+
var normalizeDuplicateKeyPolicy = (value) => {
|
|
185
|
+
if (value === void 0) {
|
|
186
|
+
return "replace";
|
|
187
|
+
}
|
|
188
|
+
if (value !== "allow" && value !== "reject" && value !== "replace") {
|
|
189
|
+
throw new BTreeValidationError(
|
|
190
|
+
`Invalid duplicateKeys option.`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
return value;
|
|
194
|
+
};
|
|
195
|
+
var toPublicEntry = (entry) => ({
|
|
196
|
+
entryId: entry.entryId,
|
|
197
|
+
key: entry.key,
|
|
198
|
+
value: entry.value
|
|
199
|
+
});
|
|
200
|
+
var isLeafNode = (node) => {
|
|
201
|
+
return node.kind === NODE_LEAF;
|
|
202
|
+
};
|
|
203
|
+
var writeMinKeyTo = (node, target) => {
|
|
204
|
+
if (node.kind === NODE_LEAF) {
|
|
205
|
+
if (node.entryOffset >= node.entries.length) return false;
|
|
206
|
+
const e = node.entries[node.entryOffset];
|
|
207
|
+
target.key = e.key;
|
|
208
|
+
target.sequence = e.entryId;
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
if (node.childOffset >= node.keys.length) return false;
|
|
212
|
+
target.key = node.keys[node.childOffset].key;
|
|
213
|
+
target.sequence = node.keys[node.childOffset].sequence;
|
|
214
|
+
return true;
|
|
215
|
+
};
|
|
216
|
+
var normalizeNodeCapacity = (value, field, defaultValue) => {
|
|
217
|
+
if (value === void 0) {
|
|
218
|
+
return defaultValue;
|
|
219
|
+
}
|
|
220
|
+
if (!Number.isInteger(value) || value < MIN_NODE_CAPACITY || value > MAX_NODE_CAPACITY) {
|
|
221
|
+
throw new BTreeValidationError(
|
|
222
|
+
`${field}: integer ${MIN_NODE_CAPACITY}\u2013${MAX_NODE_CAPACITY} required.`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
return value;
|
|
226
|
+
};
|
|
227
|
+
|
|
222
228
|
// src/btree/navigation.ts
|
|
223
229
|
var selectBranchChild = (compare, branch, userKey, sequence) => {
|
|
224
230
|
const off = branch.childOffset;
|
|
@@ -232,7 +238,7 @@ var selectBranchChild = (compare, branch, userKey, sequence) => {
|
|
|
232
238
|
const mid = lower + upper >>> 1;
|
|
233
239
|
const k = branch.keys[mid];
|
|
234
240
|
const cmp = compare(k.key, userKey);
|
|
235
|
-
if ((cmp !== 0 ? cmp : k.sequence - sequence) <= 0) {
|
|
241
|
+
if ((cmp !== 0 ? cmp : k.sequence < sequence ? -1 : k.sequence > sequence ? 1 : 0) <= 0) {
|
|
236
242
|
selectedIndex = mid;
|
|
237
243
|
lower = mid + 1;
|
|
238
244
|
} else {
|
|
@@ -257,7 +263,7 @@ var lowerBoundInLeaf = (state, leaf, userKey, sequence) => {
|
|
|
257
263
|
const mid = lower + upper >>> 1;
|
|
258
264
|
const e = leaf.entries[mid];
|
|
259
265
|
const cmp = compare(e.key, userKey);
|
|
260
|
-
if ((cmp !== 0 ? cmp : e.entryId - sequence) < 0) {
|
|
266
|
+
if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) < 0) {
|
|
261
267
|
lower = mid + 1;
|
|
262
268
|
} else {
|
|
263
269
|
upper = mid;
|
|
@@ -273,7 +279,7 @@ var upperBoundInLeaf = (state, leaf, userKey, sequence) => {
|
|
|
273
279
|
const mid = lower + upper >>> 1;
|
|
274
280
|
const e = leaf.entries[mid];
|
|
275
281
|
const cmp = compare(e.key, userKey);
|
|
276
|
-
if ((cmp !== 0 ? cmp : e.entryId - sequence) <= 0) {
|
|
282
|
+
if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) <= 0) {
|
|
277
283
|
lower = mid + 1;
|
|
278
284
|
} else {
|
|
279
285
|
upper = mid;
|
|
@@ -410,6 +416,114 @@ var findPairOrNextLower = (state, key) => {
|
|
|
410
416
|
return null;
|
|
411
417
|
};
|
|
412
418
|
|
|
419
|
+
// src/btree/rangeQuery.ts
|
|
420
|
+
function isEmptyRange(compare, startKey, endKey, options) {
|
|
421
|
+
const cmp = compare(startKey, endKey);
|
|
422
|
+
if (cmp > 0) return true;
|
|
423
|
+
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
424
|
+
const upperExclusive = options?.upperBound === "exclusive";
|
|
425
|
+
return lowerExclusive && upperExclusive && cmp === 0;
|
|
426
|
+
}
|
|
427
|
+
var initRangeCursor = (state, startKey, endKey, options) => {
|
|
428
|
+
if (state.entryCount === 0) return null;
|
|
429
|
+
const compare = state.compareKeys;
|
|
430
|
+
if (isEmptyRange(compare, startKey, endKey, options)) return null;
|
|
431
|
+
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
432
|
+
const upperExclusive = options?.upperBound === "exclusive";
|
|
433
|
+
const startSeq = lowerExclusive ? Number.MAX_SAFE_INTEGER : 0;
|
|
434
|
+
const leaf = findLeafForKey(state, startKey, startSeq);
|
|
435
|
+
const index = lowerExclusive ? upperBoundInLeaf(state, leaf, startKey, Number.MAX_SAFE_INTEGER) : lowerBoundInLeaf(state, leaf, startKey, 0);
|
|
436
|
+
return { leaf, index, compare, upperExclusive };
|
|
437
|
+
};
|
|
438
|
+
var countRangeEntries = (state, startKey, endKey, options) => {
|
|
439
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
440
|
+
if (cursor === null) return 0;
|
|
441
|
+
let cursorLeaf = cursor.leaf;
|
|
442
|
+
let cursorIndex = cursor.index;
|
|
443
|
+
const { compare, upperExclusive } = cursor;
|
|
444
|
+
let count = 0;
|
|
445
|
+
while (cursorLeaf !== null) {
|
|
446
|
+
const leafCount = leafEntryCount(cursorLeaf);
|
|
447
|
+
if (cursorIndex >= leafCount) {
|
|
448
|
+
cursorLeaf = cursorLeaf.next;
|
|
449
|
+
cursorIndex = 0;
|
|
450
|
+
continue;
|
|
451
|
+
}
|
|
452
|
+
const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
|
|
453
|
+
const cmpLast = compare(lastEntry.key, endKey);
|
|
454
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
455
|
+
count += leafCount - cursorIndex;
|
|
456
|
+
cursorLeaf = cursorLeaf.next;
|
|
457
|
+
cursorIndex = 0;
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
461
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
|
|
462
|
+
const limit = endBound < leafCount ? endBound : leafCount;
|
|
463
|
+
count += limit - cursorIndex;
|
|
464
|
+
return count;
|
|
465
|
+
}
|
|
466
|
+
return count;
|
|
467
|
+
};
|
|
468
|
+
var RANGE_PREALLOC_THRESHOLD = 200;
|
|
469
|
+
var allocateRangeOutput = (state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options) => {
|
|
470
|
+
const firstLeafCount = leafEntryCount(cursorLeaf);
|
|
471
|
+
const firstLeafRemainder = firstLeafCount - cursorIndex;
|
|
472
|
+
if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursorLeaf.next !== null) {
|
|
473
|
+
const lastEntry = leafEntryAt(cursorLeaf, firstLeafCount - 1);
|
|
474
|
+
const cmpLast = compare(lastEntry.key, endKey);
|
|
475
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
476
|
+
const total = countRangeEntries(state, startKey, endKey, options);
|
|
477
|
+
return new Array(total);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
return [];
|
|
481
|
+
};
|
|
482
|
+
var appendLeafSlice = (leaf, from, to, output, useIndexed, writeIdx) => {
|
|
483
|
+
if (useIndexed) {
|
|
484
|
+
for (let i = from; i < to; i += 1) {
|
|
485
|
+
output[writeIdx++] = leafEntryAt(leaf, i);
|
|
486
|
+
}
|
|
487
|
+
} else {
|
|
488
|
+
for (let i = from; i < to; i += 1) {
|
|
489
|
+
output.push(leafEntryAt(leaf, i));
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return writeIdx;
|
|
493
|
+
};
|
|
494
|
+
var rangeQueryEntries = (state, startKey, endKey, options) => {
|
|
495
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
496
|
+
if (cursor === null) return [];
|
|
497
|
+
let cursorLeaf = cursor.leaf;
|
|
498
|
+
let cursorIndex = cursor.index;
|
|
499
|
+
const { compare, upperExclusive } = cursor;
|
|
500
|
+
const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
|
|
501
|
+
let writeIdx = 0;
|
|
502
|
+
const useIndexed = output.length > 0;
|
|
503
|
+
while (cursorLeaf !== null) {
|
|
504
|
+
const leafCount = leafEntryCount(cursorLeaf);
|
|
505
|
+
if (cursorIndex >= leafCount) {
|
|
506
|
+
cursorLeaf = cursorLeaf.next;
|
|
507
|
+
cursorIndex = 0;
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
|
|
511
|
+
const cmpLast = compare(lastEntry.key, endKey);
|
|
512
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
513
|
+
writeIdx = appendLeafSlice(cursorLeaf, cursorIndex, leafCount, output, useIndexed, writeIdx);
|
|
514
|
+
cursorLeaf = cursorLeaf.next;
|
|
515
|
+
cursorIndex = 0;
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
519
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
|
|
520
|
+
const limit = endBound < leafCount ? endBound : leafCount;
|
|
521
|
+
appendLeafSlice(cursorLeaf, cursorIndex, limit, output, useIndexed, writeIdx);
|
|
522
|
+
return output;
|
|
523
|
+
}
|
|
524
|
+
return output;
|
|
525
|
+
};
|
|
526
|
+
|
|
413
527
|
// src/btree/rebalance.ts
|
|
414
528
|
var updateMinKeyInAncestors = (node) => {
|
|
415
529
|
let current = node;
|
|
@@ -635,16 +749,7 @@ var findRemoveEnd = (state, leaf, idx, endKey, upperExclusive) => {
|
|
|
635
749
|
}
|
|
636
750
|
return removeEnd;
|
|
637
751
|
};
|
|
638
|
-
var
|
|
639
|
-
let h = 0;
|
|
640
|
-
let n = state.root;
|
|
641
|
-
while (!isLeafNode(n)) {
|
|
642
|
-
n = n.children[n.childOffset];
|
|
643
|
-
h += 1;
|
|
644
|
-
}
|
|
645
|
-
return h;
|
|
646
|
-
};
|
|
647
|
-
var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth) => {
|
|
752
|
+
var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
648
753
|
if (state.entryKeys !== null) {
|
|
649
754
|
for (let i = idx; i < idx + removeCount; i += 1) {
|
|
650
755
|
state.entryKeys.delete(leafEntryAt(leaf, i).entryId);
|
|
@@ -659,11 +764,11 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
|
|
|
659
764
|
updateMinKeyInAncestors(leaf);
|
|
660
765
|
}
|
|
661
766
|
const countAfterSplice = leafEntryCount(leaf);
|
|
662
|
-
let
|
|
663
|
-
while (
|
|
767
|
+
let safetyGuard = state.minLeafEntries + 4;
|
|
768
|
+
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < state.minLeafEntries) {
|
|
664
769
|
rebalanceAfterLeafRemoval(state, leaf);
|
|
665
770
|
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf) break;
|
|
666
|
-
|
|
771
|
+
safetyGuard -= 1;
|
|
667
772
|
}
|
|
668
773
|
if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
|
|
669
774
|
updateMinKeyInAncestors(leaf);
|
|
@@ -673,12 +778,9 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
|
|
|
673
778
|
var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
|
|
674
779
|
var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
675
780
|
if (state.entryCount === 0) return 0;
|
|
676
|
-
|
|
677
|
-
if (boundCompared > 0) return 0;
|
|
781
|
+
if (isEmptyRange(state.compareKeys, startKey, endKey, options)) return 0;
|
|
678
782
|
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
679
783
|
const upperExclusive = options?.upperBound === "exclusive";
|
|
680
|
-
if (lowerExclusive && upperExclusive && boundCompared === 0) return 0;
|
|
681
|
-
const treeHeight = computeTreeHeight(state);
|
|
682
784
|
let deleted = 0;
|
|
683
785
|
let needsNavigate = true;
|
|
684
786
|
let leaf = null;
|
|
@@ -696,7 +798,7 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
696
798
|
const removeEnd = findRemoveEnd(state, leaf, idx, endKey, upperExclusive);
|
|
697
799
|
const removeCount = removeEnd - idx;
|
|
698
800
|
if (removeCount === 0) break;
|
|
699
|
-
const countAfterSplice = spliceLeafAndRebalance(state, leaf, idx, removeCount
|
|
801
|
+
const countAfterSplice = spliceLeafAndRebalance(state, leaf, idx, removeCount);
|
|
700
802
|
deleted += removeCount;
|
|
701
803
|
if (removeEnd < count) break;
|
|
702
804
|
if (!isLeafStillValid(state, leaf)) {
|
|
@@ -715,6 +817,7 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
715
817
|
};
|
|
716
818
|
|
|
717
819
|
// src/btree/autoScale.ts
|
|
820
|
+
var minOccupancy = (max) => Math.ceil(max / 2);
|
|
718
821
|
var AUTO_SCALE_TIERS = [
|
|
719
822
|
{ threshold: 0, maxLeaf: 32, maxBranch: 32 },
|
|
720
823
|
{ threshold: 1e3, maxLeaf: 64, maxBranch: 64 },
|
|
@@ -766,8 +869,8 @@ var createInitialState = (config) => {
|
|
|
766
869
|
maxLeafEntries,
|
|
767
870
|
maxBranchChildren,
|
|
768
871
|
duplicateKeys,
|
|
769
|
-
minLeafEntries:
|
|
770
|
-
minBranchChildren:
|
|
872
|
+
minLeafEntries: minOccupancy(maxLeafEntries),
|
|
873
|
+
minBranchChildren: minOccupancy(maxBranchChildren),
|
|
771
874
|
root: emptyLeaf,
|
|
772
875
|
leftmostLeaf: emptyLeaf,
|
|
773
876
|
rightmostLeaf: emptyLeaf,
|
|
@@ -784,11 +887,11 @@ var maybeAutoScale = (state) => {
|
|
|
784
887
|
const { maxLeaf, maxBranch } = computeAutoScaleTier(state.entryCount);
|
|
785
888
|
if (maxLeaf > state.maxLeafEntries) {
|
|
786
889
|
state.maxLeafEntries = maxLeaf;
|
|
787
|
-
state.minLeafEntries =
|
|
890
|
+
state.minLeafEntries = minOccupancy(maxLeaf);
|
|
788
891
|
}
|
|
789
892
|
if (maxBranch > state.maxBranchChildren) {
|
|
790
893
|
state.maxBranchChildren = maxBranch;
|
|
791
|
-
state.minBranchChildren =
|
|
894
|
+
state.minBranchChildren = minOccupancy(maxBranch);
|
|
792
895
|
}
|
|
793
896
|
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(state.entryCount);
|
|
794
897
|
};
|
|
@@ -814,8 +917,8 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
|
|
|
814
917
|
}
|
|
815
918
|
state.maxLeafEntries = normalizedLeaf;
|
|
816
919
|
state.maxBranchChildren = normalizedBranch;
|
|
817
|
-
state.minLeafEntries =
|
|
818
|
-
state.minBranchChildren =
|
|
920
|
+
state.minLeafEntries = minOccupancy(normalizedLeaf);
|
|
921
|
+
state.minBranchChildren = minOccupancy(normalizedBranch);
|
|
819
922
|
};
|
|
820
923
|
|
|
821
924
|
// src/btree/bulkLoad.ts
|
|
@@ -1040,6 +1143,9 @@ var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
|
1040
1143
|
return { leaf: targetLeaf, index };
|
|
1041
1144
|
};
|
|
1042
1145
|
var removeEntryById = (state, entryId) => {
|
|
1146
|
+
if (state.entryKeys === null) {
|
|
1147
|
+
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1148
|
+
}
|
|
1043
1149
|
const userKey = state.entryKeys.get(entryId);
|
|
1044
1150
|
if (userKey === void 0) return null;
|
|
1045
1151
|
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
@@ -1057,6 +1163,9 @@ var removeEntryById = (state, entryId) => {
|
|
|
1057
1163
|
return entry;
|
|
1058
1164
|
};
|
|
1059
1165
|
var peekEntryById = (state, entryId) => {
|
|
1166
|
+
if (state.entryKeys === null) {
|
|
1167
|
+
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1168
|
+
}
|
|
1060
1169
|
const userKey = state.entryKeys.get(entryId);
|
|
1061
1170
|
if (userKey === void 0) return null;
|
|
1062
1171
|
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
@@ -1065,6 +1174,9 @@ var peekEntryById = (state, entryId) => {
|
|
|
1065
1174
|
return entry;
|
|
1066
1175
|
};
|
|
1067
1176
|
var updateEntryById = (state, entryId, newValue) => {
|
|
1177
|
+
if (state.entryKeys === null) {
|
|
1178
|
+
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1179
|
+
}
|
|
1068
1180
|
const userKey = state.entryKeys.get(entryId);
|
|
1069
1181
|
if (userKey === void 0) return null;
|
|
1070
1182
|
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
@@ -1078,9 +1190,12 @@ var putManyEntries = (state, entries) => {
|
|
|
1078
1190
|
const strictlyAscending = state.duplicateKeys !== "allow";
|
|
1079
1191
|
for (let i = 1; i < entries.length; i += 1) {
|
|
1080
1192
|
const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
|
|
1081
|
-
if (
|
|
1193
|
+
if (cmp > 0) {
|
|
1194
|
+
throw new BTreeValidationError("putMany: entries not in ascending order.");
|
|
1195
|
+
}
|
|
1196
|
+
if (strictlyAscending && cmp === 0) {
|
|
1082
1197
|
throw new BTreeValidationError(
|
|
1083
|
-
|
|
1198
|
+
state.duplicateKeys === "reject" ? "putMany: duplicate key rejected." : "putMany: equal keys not allowed in strict mode."
|
|
1084
1199
|
);
|
|
1085
1200
|
}
|
|
1086
1201
|
}
|
|
@@ -1098,109 +1213,6 @@ var putManyEntries = (state, entries) => {
|
|
|
1098
1213
|
return bulkLoadEntries(state, entries);
|
|
1099
1214
|
};
|
|
1100
1215
|
|
|
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
1216
|
// src/btree/serialization.ts
|
|
1205
1217
|
var buildConfigFromState = (state) => {
|
|
1206
1218
|
const config = {
|
|
@@ -1330,7 +1342,7 @@ var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
|
|
|
1330
1342
|
if (cmp !== 0) {
|
|
1331
1343
|
return cmp;
|
|
1332
1344
|
}
|
|
1333
|
-
return leftSeq - rightSeq;
|
|
1345
|
+
return leftSeq < rightSeq ? -1 : leftSeq > rightSeq ? 1 : 0;
|
|
1334
1346
|
};
|
|
1335
1347
|
var getNodeMaxKey = (node) => {
|
|
1336
1348
|
if (isLeafNode(node)) {
|
|
@@ -1663,44 +1675,56 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1663
1675
|
return putManyEntries(this.state, entries);
|
|
1664
1676
|
}
|
|
1665
1677
|
remove(key) {
|
|
1666
|
-
|
|
1678
|
+
const entry = removeFirstMatchingEntry(this.state, key);
|
|
1679
|
+
if (entry === null) return null;
|
|
1680
|
+
return toPublicEntry(entry);
|
|
1667
1681
|
}
|
|
1668
1682
|
removeById(entryId) {
|
|
1669
1683
|
if (this.state.entryKeys === null) {
|
|
1670
1684
|
throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
|
|
1671
1685
|
}
|
|
1672
|
-
|
|
1686
|
+
const entry = removeEntryById(this.state, entryId);
|
|
1687
|
+
if (entry === null) return null;
|
|
1688
|
+
return toPublicEntry(entry);
|
|
1673
1689
|
}
|
|
1674
1690
|
peekById(entryId) {
|
|
1675
1691
|
if (this.state.entryKeys === null) {
|
|
1676
1692
|
throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
|
|
1677
1693
|
}
|
|
1678
|
-
|
|
1694
|
+
const entry = peekEntryById(this.state, entryId);
|
|
1695
|
+
if (entry === null) return null;
|
|
1696
|
+
return toPublicEntry(entry);
|
|
1679
1697
|
}
|
|
1680
1698
|
updateById(entryId, value) {
|
|
1681
1699
|
if (this.state.entryKeys === null) {
|
|
1682
1700
|
throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
|
|
1683
1701
|
}
|
|
1684
|
-
|
|
1702
|
+
const entry = updateEntryById(this.state, entryId, value);
|
|
1703
|
+
if (entry === null) return null;
|
|
1704
|
+
return toPublicEntry(entry);
|
|
1685
1705
|
}
|
|
1686
1706
|
popFirst() {
|
|
1687
|
-
|
|
1707
|
+
const entry = popFirstEntry(this.state);
|
|
1708
|
+
if (entry === null) return null;
|
|
1709
|
+
return toPublicEntry(entry);
|
|
1688
1710
|
}
|
|
1689
1711
|
peekFirst() {
|
|
1690
1712
|
if (this.state.entryCount === 0) {
|
|
1691
1713
|
return null;
|
|
1692
1714
|
}
|
|
1693
|
-
return leafEntryAt(this.state.leftmostLeaf, 0);
|
|
1715
|
+
return toPublicEntry(leafEntryAt(this.state.leftmostLeaf, 0));
|
|
1694
1716
|
}
|
|
1695
1717
|
peekLast() {
|
|
1696
1718
|
if (this.state.entryCount === 0) {
|
|
1697
1719
|
return null;
|
|
1698
1720
|
}
|
|
1699
1721
|
const leaf = this.state.rightmostLeaf;
|
|
1700
|
-
return leafEntryAt(leaf, leafEntryCount(leaf) - 1);
|
|
1722
|
+
return toPublicEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
|
|
1701
1723
|
}
|
|
1702
1724
|
popLast() {
|
|
1703
|
-
|
|
1725
|
+
const entry = popLastEntry(this.state);
|
|
1726
|
+
if (entry === null) return null;
|
|
1727
|
+
return toPublicEntry(entry);
|
|
1704
1728
|
}
|
|
1705
1729
|
clear() {
|
|
1706
1730
|
const emptyLeaf = createLeafNode([], null);
|
|
@@ -1717,8 +1741,8 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1717
1741
|
const tier = computeAutoScaleTier(0);
|
|
1718
1742
|
this.state.maxLeafEntries = tier.maxLeaf;
|
|
1719
1743
|
this.state.maxBranchChildren = tier.maxBranch;
|
|
1720
|
-
this.state.minLeafEntries =
|
|
1721
|
-
this.state.minBranchChildren =
|
|
1744
|
+
this.state.minLeafEntries = minOccupancy(tier.maxLeaf);
|
|
1745
|
+
this.state.minBranchChildren = minOccupancy(tier.maxBranch);
|
|
1722
1746
|
this.state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
1723
1747
|
}
|
|
1724
1748
|
}
|
|
@@ -1733,39 +1757,65 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1733
1757
|
findFirst(key) {
|
|
1734
1758
|
const found = findFirstMatchingUserKey(this.state, key);
|
|
1735
1759
|
if (found === null) return null;
|
|
1736
|
-
return leafEntryAt(found.leaf, found.index);
|
|
1760
|
+
return toPublicEntry(leafEntryAt(found.leaf, found.index));
|
|
1737
1761
|
}
|
|
1762
|
+
/**
|
|
1763
|
+
* Returns the last entry whose key matches `key`, or `null` if not found.
|
|
1764
|
+
* Useful when `duplicateKeys` is `'allow'` and multiple entries share the same key.
|
|
1765
|
+
*/
|
|
1738
1766
|
findLast(key) {
|
|
1739
1767
|
const found = findLastMatchingUserKey(this.state, key);
|
|
1740
1768
|
if (found === null) return null;
|
|
1741
|
-
return leafEntryAt(found.leaf, found.index);
|
|
1769
|
+
return toPublicEntry(leafEntryAt(found.leaf, found.index));
|
|
1742
1770
|
}
|
|
1771
|
+
/**
|
|
1772
|
+
* Returns the smallest key in the tree that is strictly greater than `key`,
|
|
1773
|
+
* or `null` if no such key exists.
|
|
1774
|
+
*/
|
|
1743
1775
|
nextHigherKey(key) {
|
|
1744
1776
|
return findNextHigherKey(this.state, key);
|
|
1745
1777
|
}
|
|
1778
|
+
/**
|
|
1779
|
+
* Returns the largest key in the tree that is strictly less than `key`,
|
|
1780
|
+
* or `null` if no such key exists.
|
|
1781
|
+
*/
|
|
1746
1782
|
nextLowerKey(key) {
|
|
1747
1783
|
return findNextLowerKey(this.state, key);
|
|
1748
1784
|
}
|
|
1785
|
+
/**
|
|
1786
|
+
* Returns the entry for `key` if it exists; otherwise returns the entry with
|
|
1787
|
+
* the largest key strictly less than `key`. Returns `null` when the tree is
|
|
1788
|
+
* empty or every key is greater than `key`.
|
|
1789
|
+
*/
|
|
1749
1790
|
getPairOrNextLower(key) {
|
|
1750
1791
|
const found = findPairOrNextLower(this.state, key);
|
|
1751
1792
|
if (found === null) return null;
|
|
1752
|
-
return leafEntryAt(found.leaf, found.index);
|
|
1793
|
+
return toPublicEntry(leafEntryAt(found.leaf, found.index));
|
|
1753
1794
|
}
|
|
1795
|
+
/**
|
|
1796
|
+
* Returns the number of entries whose keys fall within [`startKey`, `endKey`].
|
|
1797
|
+
* Pass `options` to make either bound exclusive.
|
|
1798
|
+
*/
|
|
1754
1799
|
count(startKey, endKey, options) {
|
|
1755
1800
|
return countRangeEntries(this.state, startKey, endKey, options);
|
|
1756
1801
|
}
|
|
1802
|
+
/**
|
|
1803
|
+
* Deletes all entries whose keys fall within [`startKey`, `endKey`].
|
|
1804
|
+
* Pass `options` to make either bound exclusive.
|
|
1805
|
+
* @returns The number of entries deleted.
|
|
1806
|
+
*/
|
|
1757
1807
|
deleteRange(startKey, endKey, options) {
|
|
1758
1808
|
return deleteRangeEntries(this.state, startKey, endKey, options);
|
|
1759
1809
|
}
|
|
1760
1810
|
range(startKey, endKey, options) {
|
|
1761
|
-
return rangeQueryEntries(this.state, startKey, endKey, options);
|
|
1811
|
+
return rangeQueryEntries(this.state, startKey, endKey, options).map(toPublicEntry);
|
|
1762
1812
|
}
|
|
1763
1813
|
*entries() {
|
|
1764
1814
|
let leaf = this.state.leftmostLeaf;
|
|
1765
1815
|
while (leaf !== null) {
|
|
1766
1816
|
const count = leafEntryCount(leaf);
|
|
1767
1817
|
for (let i = 0; i < count; i += 1) {
|
|
1768
|
-
yield leafEntryAt(leaf, i);
|
|
1818
|
+
yield toPublicEntry(leafEntryAt(leaf, i));
|
|
1769
1819
|
}
|
|
1770
1820
|
leaf = leaf.next;
|
|
1771
1821
|
}
|
|
@@ -1775,7 +1825,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1775
1825
|
while (leaf !== null) {
|
|
1776
1826
|
const count = leafEntryCount(leaf);
|
|
1777
1827
|
for (let i = count - 1; i >= 0; i -= 1) {
|
|
1778
|
-
yield leafEntryAt(leaf, i);
|
|
1828
|
+
yield toPublicEntry(leafEntryAt(leaf, i));
|
|
1779
1829
|
}
|
|
1780
1830
|
leaf = leaf.prev;
|
|
1781
1831
|
}
|
|
@@ -1798,7 +1848,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1798
1848
|
while (leaf !== null) {
|
|
1799
1849
|
const count = leafEntryCount(leaf);
|
|
1800
1850
|
for (let i = 0; i < count; i += 1) {
|
|
1801
|
-
callback.call(thisArg, leafEntryAt(leaf, i));
|
|
1851
|
+
callback.call(thisArg, toPublicEntry(leafEntryAt(leaf, i)));
|
|
1802
1852
|
}
|
|
1803
1853
|
leaf = leaf.next;
|
|
1804
1854
|
}
|
|
@@ -1810,12 +1860,19 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1810
1860
|
while (leaf !== null) {
|
|
1811
1861
|
const count = leafEntryCount(leaf);
|
|
1812
1862
|
for (let i = 0; i < count; i += 1) {
|
|
1813
|
-
result[writeIdx++] = leafEntryAt(leaf, i);
|
|
1863
|
+
result[writeIdx++] = toPublicEntry(leafEntryAt(leaf, i));
|
|
1814
1864
|
}
|
|
1815
1865
|
leaf = leaf.next;
|
|
1816
1866
|
}
|
|
1817
1867
|
return result;
|
|
1818
1868
|
}
|
|
1869
|
+
/**
|
|
1870
|
+
* Returns a structurally independent `InMemoryBTree` with identical
|
|
1871
|
+
* configuration and entries. The tree structure (nodes, links, entry IDs)
|
|
1872
|
+
* is fully independent, but stored key and value references are shared
|
|
1873
|
+
* with the source tree.
|
|
1874
|
+
* Note: `EntryId` values are reassigned in the clone — IDs from the source tree are not valid for the clone.
|
|
1875
|
+
*/
|
|
1819
1876
|
clone() {
|
|
1820
1877
|
const cloned = new _InMemoryBTree(buildConfigFromState(this.state));
|
|
1821
1878
|
applyAutoScaleCapacitySnapshot(
|