@frostpillar/frostpillar-btree 0.2.6 → 0.2.8
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 +95 -73
- package/README.md +96 -74
- package/dist/InMemoryBTree.d.ts +3 -36
- package/dist/btree/autoScale.d.ts +1 -0
- package/dist/btree/entry-lookup.d.ts +8 -0
- package/dist/btree/mutations.d.ts +1 -3
- package/dist/btree/rangeQuery.d.ts +4 -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 +14 -3
- package/dist/{chunk-OWHENPGJ.js → chunk-OFXDCKLC.js} +663 -395
- package/dist/concurrency/ConcurrentInMemoryBTree.d.ts +3 -14
- package/dist/concurrency/coordinator.d.ts +21 -0
- package/dist/concurrency/syncLogValidation.d.ts +2 -0
- package/dist/core.cjs +663 -395
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1 -1
- package/dist/frostpillar-btree-core.min.js +1 -1
- package/dist/frostpillar-btree.min.js +1 -1
- package/dist/index.cjs +856 -545
- package/dist/index.d.ts +1 -1
- package/dist/index.js +190 -147
- package/package.json +2 -2
package/dist/core.cjs
CHANGED
|
@@ -68,11 +68,12 @@ var createBranchNode = (children, parent) => {
|
|
|
68
68
|
const child = children[i];
|
|
69
69
|
child.parent = branch;
|
|
70
70
|
child.indexInParent = i;
|
|
71
|
-
const target = {
|
|
71
|
+
const target = {
|
|
72
|
+
key: void 0,
|
|
73
|
+
sequence: 0
|
|
74
|
+
};
|
|
72
75
|
if (!writeMinKeyTo(child, target)) {
|
|
73
|
-
throw new BTreeInvariantError(
|
|
74
|
-
"branch child has no min key"
|
|
75
|
-
);
|
|
76
|
+
throw new BTreeInvariantError("branch child has no min key");
|
|
76
77
|
}
|
|
77
78
|
keys.push(target);
|
|
78
79
|
}
|
|
@@ -126,7 +127,14 @@ var leafInsertAt = (leaf, logicalIndex, entry) => {
|
|
|
126
127
|
leaf.entryOffset -= 1;
|
|
127
128
|
leaf.entries[phys - 1] = entry;
|
|
128
129
|
} else {
|
|
129
|
-
leaf.entries.
|
|
130
|
+
const len = leaf.entries.length;
|
|
131
|
+
if (phys >= len) {
|
|
132
|
+
leaf.entries.push(entry);
|
|
133
|
+
} else {
|
|
134
|
+
leaf.entries.push(leaf.entries[len - 1]);
|
|
135
|
+
leaf.entries.copyWithin(phys + 1, phys, len);
|
|
136
|
+
leaf.entries[phys] = entry;
|
|
137
|
+
}
|
|
130
138
|
}
|
|
131
139
|
};
|
|
132
140
|
var leafCompact = (leaf) => {
|
|
@@ -155,7 +163,11 @@ var branchInsertAt = (branch, logicalIndex, child, key) => {
|
|
|
155
163
|
const phys = branch.childOffset + logicalIndex;
|
|
156
164
|
const count = branch.children.length - branch.childOffset;
|
|
157
165
|
if (branch.childOffset > 0 && logicalIndex < count >>> 1) {
|
|
158
|
-
branch.children.copyWithin(
|
|
166
|
+
branch.children.copyWithin(
|
|
167
|
+
branch.childOffset - 1,
|
|
168
|
+
branch.childOffset,
|
|
169
|
+
phys
|
|
170
|
+
);
|
|
159
171
|
branch.keys.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
|
|
160
172
|
branch.childOffset -= 1;
|
|
161
173
|
branch.children[phys - 1] = child;
|
|
@@ -176,8 +188,16 @@ var branchRemoveAt = (branch, physIndex) => {
|
|
|
176
188
|
const logicalIndex = physIndex - branch.childOffset;
|
|
177
189
|
const count = branch.children.length - branch.childOffset;
|
|
178
190
|
if (logicalIndex < count - 1 - logicalIndex) {
|
|
179
|
-
branch.children.copyWithin(
|
|
180
|
-
|
|
191
|
+
branch.children.copyWithin(
|
|
192
|
+
branch.childOffset + 1,
|
|
193
|
+
branch.childOffset,
|
|
194
|
+
physIndex
|
|
195
|
+
);
|
|
196
|
+
branch.keys.copyWithin(
|
|
197
|
+
branch.childOffset + 1,
|
|
198
|
+
branch.childOffset,
|
|
199
|
+
physIndex
|
|
200
|
+
);
|
|
181
201
|
branch.childOffset += 1;
|
|
182
202
|
for (let i = branch.childOffset; i <= physIndex; i += 1) {
|
|
183
203
|
branch.children[i].indexInParent = i;
|
|
@@ -207,17 +227,21 @@ var normalizeDuplicateKeyPolicy = (value) => {
|
|
|
207
227
|
return "replace";
|
|
208
228
|
}
|
|
209
229
|
if (value !== "allow" && value !== "reject" && value !== "replace") {
|
|
210
|
-
throw new BTreeValidationError(
|
|
211
|
-
`Invalid duplicateKeys option.`
|
|
212
|
-
);
|
|
230
|
+
throw new BTreeValidationError(`Invalid duplicateKeys option.`);
|
|
213
231
|
}
|
|
214
232
|
return value;
|
|
215
233
|
};
|
|
216
|
-
var
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
234
|
+
var normalizeDeleteRebalancePolicy = (value) => {
|
|
235
|
+
if (value === void 0) {
|
|
236
|
+
return "standard";
|
|
237
|
+
}
|
|
238
|
+
if (value !== "standard" && value !== "lazy") {
|
|
239
|
+
throw new BTreeValidationError(`Invalid deleteRebalancePolicy option.`);
|
|
240
|
+
}
|
|
241
|
+
return value;
|
|
242
|
+
};
|
|
243
|
+
var freezeEntry = (entry) => Object.freeze(entry);
|
|
244
|
+
var createEntry = (key, entryId, value) => Object.freeze({ key, entryId, value });
|
|
221
245
|
var isLeafNode = (node) => {
|
|
222
246
|
return node.kind === NODE_LEAF;
|
|
223
247
|
};
|
|
@@ -367,7 +391,11 @@ var hasKeyEntry = (state, key) => {
|
|
|
367
391
|
var findNextHigherKey = (state, key) => {
|
|
368
392
|
if (state.entryCount === 0) return null;
|
|
369
393
|
const compare = state.compareKeys;
|
|
370
|
-
let leaf = findLeafForKey(
|
|
394
|
+
let leaf = findLeafForKey(
|
|
395
|
+
state,
|
|
396
|
+
key,
|
|
397
|
+
Number.MAX_SAFE_INTEGER
|
|
398
|
+
);
|
|
371
399
|
let idx = upperBoundInLeaf(state, leaf, key, Number.MAX_SAFE_INTEGER);
|
|
372
400
|
while (leaf !== null) {
|
|
373
401
|
if (idx < leafEntryCount(leaf)) {
|
|
@@ -470,55 +498,57 @@ var countRangeEntries = (state, startKey, endKey, options) => {
|
|
|
470
498
|
cursorIndex = 0;
|
|
471
499
|
continue;
|
|
472
500
|
}
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
501
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
502
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
476
503
|
count += leafCount - cursorIndex;
|
|
477
504
|
cursorLeaf = cursorLeaf.next;
|
|
478
505
|
cursorIndex = 0;
|
|
479
506
|
continue;
|
|
480
507
|
}
|
|
481
|
-
|
|
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;
|
|
508
|
+
count += findBoundaryEnd(state, cursorLeaf, endKey, upperExclusive, leafCount) - cursorIndex;
|
|
485
509
|
return count;
|
|
486
510
|
}
|
|
487
511
|
return count;
|
|
488
512
|
};
|
|
513
|
+
var isLastEntryInRange = (lastKey, endKey, compare, upperExclusive) => {
|
|
514
|
+
const cmp = compare(lastKey, endKey);
|
|
515
|
+
return upperExclusive ? cmp < 0 : cmp <= 0;
|
|
516
|
+
};
|
|
517
|
+
var findBoundaryEnd = (state, leaf, endKey, upperExclusive, leafCount) => {
|
|
518
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
519
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
|
|
520
|
+
return endBound < leafCount ? endBound : leafCount;
|
|
521
|
+
};
|
|
489
522
|
var RANGE_PREALLOC_THRESHOLD = 200;
|
|
490
|
-
var allocateRangeOutput = (state,
|
|
491
|
-
const firstLeafCount = leafEntryCount(
|
|
492
|
-
const firstLeafRemainder = firstLeafCount -
|
|
493
|
-
if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD &&
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
523
|
+
var allocateRangeOutput = (state, cursor, startKey, endKey, options) => {
|
|
524
|
+
const firstLeafCount = leafEntryCount(cursor.leaf);
|
|
525
|
+
const firstLeafRemainder = firstLeafCount - cursor.index;
|
|
526
|
+
if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursor.leaf.next !== null) {
|
|
527
|
+
const lastKey = leafEntryAt(cursor.leaf, firstLeafCount - 1).key;
|
|
528
|
+
if (isLastEntryInRange(lastKey, endKey, cursor.compare, cursor.upperExclusive)) {
|
|
529
|
+
return new Array(
|
|
530
|
+
countRangeEntries(state, startKey, endKey, options)
|
|
531
|
+
);
|
|
499
532
|
}
|
|
500
533
|
}
|
|
501
534
|
return [];
|
|
502
535
|
};
|
|
503
|
-
var
|
|
536
|
+
var appendLeafSlicePublic = (leaf, from, to, output, useIndexed, writeIdx) => {
|
|
504
537
|
if (useIndexed) {
|
|
505
538
|
for (let i = from; i < to; i += 1) {
|
|
506
|
-
output[writeIdx++] = leafEntryAt(leaf, i);
|
|
539
|
+
output[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
|
|
507
540
|
}
|
|
508
541
|
} else {
|
|
509
542
|
for (let i = from; i < to; i += 1) {
|
|
510
|
-
output.push(leafEntryAt(leaf, i));
|
|
543
|
+
output.push(freezeEntry(leafEntryAt(leaf, i)));
|
|
511
544
|
}
|
|
512
545
|
}
|
|
513
546
|
return writeIdx;
|
|
514
547
|
};
|
|
515
|
-
var
|
|
516
|
-
const
|
|
517
|
-
if (cursor === null) return [];
|
|
548
|
+
var collectPublicEntries = (state, cursor, endKey, output) => {
|
|
549
|
+
const { compare, upperExclusive } = cursor;
|
|
518
550
|
let cursorLeaf = cursor.leaf;
|
|
519
551
|
let cursorIndex = cursor.index;
|
|
520
|
-
const { compare, upperExclusive } = cursor;
|
|
521
|
-
const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
|
|
522
552
|
let writeIdx = 0;
|
|
523
553
|
const useIndexed = output.length > 0;
|
|
524
554
|
while (cursorLeaf !== null) {
|
|
@@ -528,24 +558,82 @@ var rangeQueryEntries = (state, startKey, endKey, options) => {
|
|
|
528
558
|
cursorIndex = 0;
|
|
529
559
|
continue;
|
|
530
560
|
}
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
561
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
562
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
563
|
+
writeIdx = appendLeafSlicePublic(
|
|
564
|
+
cursorLeaf,
|
|
565
|
+
cursorIndex,
|
|
566
|
+
leafCount,
|
|
567
|
+
output,
|
|
568
|
+
useIndexed,
|
|
569
|
+
writeIdx
|
|
570
|
+
);
|
|
535
571
|
cursorLeaf = cursorLeaf.next;
|
|
536
572
|
cursorIndex = 0;
|
|
537
573
|
continue;
|
|
538
574
|
}
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
575
|
+
const limit = findBoundaryEnd(
|
|
576
|
+
state,
|
|
577
|
+
cursorLeaf,
|
|
578
|
+
endKey,
|
|
579
|
+
upperExclusive,
|
|
580
|
+
leafCount
|
|
581
|
+
);
|
|
582
|
+
appendLeafSlicePublic(
|
|
583
|
+
cursorLeaf,
|
|
584
|
+
cursorIndex,
|
|
585
|
+
limit,
|
|
586
|
+
output,
|
|
587
|
+
useIndexed,
|
|
588
|
+
writeIdx
|
|
589
|
+
);
|
|
590
|
+
return;
|
|
544
591
|
}
|
|
592
|
+
};
|
|
593
|
+
var rangeQueryPublicEntries = (state, startKey, endKey, options) => {
|
|
594
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
595
|
+
if (cursor === null) return [];
|
|
596
|
+
const output = allocateRangeOutput(state, cursor, startKey, endKey, options);
|
|
597
|
+
collectPublicEntries(state, cursor, endKey, output);
|
|
545
598
|
return output;
|
|
546
599
|
};
|
|
600
|
+
var forEachRangeEntries = (state, startKey, endKey, callback, options) => {
|
|
601
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
602
|
+
if (cursor === null) return;
|
|
603
|
+
let cursorLeaf = cursor.leaf;
|
|
604
|
+
let cursorIndex = cursor.index;
|
|
605
|
+
const { compare, upperExclusive } = cursor;
|
|
606
|
+
while (cursorLeaf !== null) {
|
|
607
|
+
const leafCount = leafEntryCount(cursorLeaf);
|
|
608
|
+
if (cursorIndex >= leafCount) {
|
|
609
|
+
cursorLeaf = cursorLeaf.next;
|
|
610
|
+
cursorIndex = 0;
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
614
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
615
|
+
for (let i = cursorIndex; i < leafCount; i += 1) {
|
|
616
|
+
callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
|
|
617
|
+
}
|
|
618
|
+
cursorLeaf = cursorLeaf.next;
|
|
619
|
+
cursorIndex = 0;
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
const limit = findBoundaryEnd(
|
|
623
|
+
state,
|
|
624
|
+
cursorLeaf,
|
|
625
|
+
endKey,
|
|
626
|
+
upperExclusive,
|
|
627
|
+
leafCount
|
|
628
|
+
);
|
|
629
|
+
for (let i = cursorIndex; i < limit; i += 1) {
|
|
630
|
+
callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
|
|
631
|
+
}
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
};
|
|
547
635
|
|
|
548
|
-
// src/btree/rebalance.ts
|
|
636
|
+
// src/btree/rebalance-branch.ts
|
|
549
637
|
var updateMinKeyInAncestors = (node) => {
|
|
550
638
|
let current = node;
|
|
551
639
|
while (current.parent !== null) {
|
|
@@ -561,12 +649,9 @@ var requireParent = (node) => {
|
|
|
561
649
|
}
|
|
562
650
|
return node.parent;
|
|
563
651
|
};
|
|
564
|
-
var requireLeafNode = (node) => {
|
|
565
|
-
if (!isLeafNode(node)) throw new BTreeInvariantError("expected leaf, got branch");
|
|
566
|
-
return node;
|
|
567
|
-
};
|
|
568
652
|
var requireBranchNode = (node) => {
|
|
569
|
-
if (isLeafNode(node))
|
|
653
|
+
if (isLeafNode(node))
|
|
654
|
+
throw new BTreeInvariantError("expected branch, got leaf");
|
|
570
655
|
return node;
|
|
571
656
|
};
|
|
572
657
|
var removeChildFromBranch = (branch, childIndex) => {
|
|
@@ -575,27 +660,18 @@ var removeChildFromBranch = (branch, childIndex) => {
|
|
|
575
660
|
}
|
|
576
661
|
branchRemoveAt(branch, childIndex);
|
|
577
662
|
};
|
|
578
|
-
var detachLeafFromChain = (state, leaf) => {
|
|
579
|
-
if (leaf.prev !== null) {
|
|
580
|
-
leaf.prev.next = leaf.next;
|
|
581
|
-
} else if (leaf.next !== null) {
|
|
582
|
-
state.leftmostLeaf = leaf.next;
|
|
583
|
-
}
|
|
584
|
-
if (leaf.next !== null) {
|
|
585
|
-
leaf.next.prev = leaf.prev;
|
|
586
|
-
} else if (leaf.prev !== null) {
|
|
587
|
-
state.rightmostLeaf = leaf.prev;
|
|
588
|
-
}
|
|
589
|
-
leaf.prev = null;
|
|
590
|
-
leaf.next = null;
|
|
591
|
-
};
|
|
592
663
|
var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
|
|
593
664
|
const borrowedChild = leftSibling.children.pop();
|
|
594
|
-
if (borrowedChild === void 0)
|
|
665
|
+
if (borrowedChild === void 0)
|
|
666
|
+
throw new BTreeInvariantError("left branch borrow failed");
|
|
595
667
|
leftSibling.keys.pop();
|
|
596
668
|
borrowedChild.parent = branch;
|
|
597
|
-
const borrowedMinKey = {
|
|
598
|
-
|
|
669
|
+
const borrowedMinKey = {
|
|
670
|
+
key: void 0,
|
|
671
|
+
sequence: 0
|
|
672
|
+
};
|
|
673
|
+
if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
|
|
674
|
+
throw new BTreeInvariantError("borrowed child has no min key");
|
|
599
675
|
if (branch.childOffset > 0) {
|
|
600
676
|
branch.childOffset -= 1;
|
|
601
677
|
branch.children[branch.childOffset] = borrowedChild;
|
|
@@ -604,15 +680,20 @@ var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
|
|
|
604
680
|
} else {
|
|
605
681
|
branch.children.unshift(borrowedChild);
|
|
606
682
|
branch.keys.unshift(borrowedMinKey);
|
|
607
|
-
for (let i = 0; i < branch.children.length; i += 1)
|
|
683
|
+
for (let i = 0; i < branch.children.length; i += 1)
|
|
684
|
+
branch.children[i].indexInParent = i;
|
|
608
685
|
}
|
|
609
686
|
const parent = requireParent(branch);
|
|
610
|
-
parent.keys[branchIndex] = {
|
|
687
|
+
parent.keys[branchIndex] = {
|
|
688
|
+
key: borrowedMinKey.key,
|
|
689
|
+
sequence: borrowedMinKey.sequence
|
|
690
|
+
};
|
|
611
691
|
updateMinKeyInAncestors(branch);
|
|
612
692
|
};
|
|
613
693
|
var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
|
|
614
694
|
const shiftIdx = rightSibling.childOffset;
|
|
615
|
-
if (shiftIdx >= rightSibling.children.length)
|
|
695
|
+
if (shiftIdx >= rightSibling.children.length)
|
|
696
|
+
throw new BTreeInvariantError("right branch borrow failed");
|
|
616
697
|
const borrowedChild = rightSibling.children[shiftIdx];
|
|
617
698
|
rightSibling.childOffset += 1;
|
|
618
699
|
if (rightSibling.childOffset >= rightSibling.children.length >>> 1) {
|
|
@@ -620,8 +701,12 @@ var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
|
|
|
620
701
|
}
|
|
621
702
|
branch.children.push(borrowedChild);
|
|
622
703
|
borrowedChild.parent = branch;
|
|
623
|
-
const borrowedMinKey = {
|
|
624
|
-
|
|
704
|
+
const borrowedMinKey = {
|
|
705
|
+
key: void 0,
|
|
706
|
+
sequence: 0
|
|
707
|
+
};
|
|
708
|
+
if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
|
|
709
|
+
throw new BTreeInvariantError("borrowed child has no min key");
|
|
625
710
|
branch.keys.push(borrowedMinKey);
|
|
626
711
|
borrowedChild.indexInParent = branch.children.length - 1;
|
|
627
712
|
const parent = requireParent(branch);
|
|
@@ -674,7 +759,10 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
|
|
|
674
759
|
const parent = branch.parent;
|
|
675
760
|
if (parent === null) throw new BTreeInvariantError("branch has no parent");
|
|
676
761
|
const branchIndex = branch.indexInParent;
|
|
677
|
-
const { left: leftSibling, right: rightSibling } = findBranchSiblings(
|
|
762
|
+
const { left: leftSibling, right: rightSibling } = findBranchSiblings(
|
|
763
|
+
parent,
|
|
764
|
+
branchIndex
|
|
765
|
+
);
|
|
678
766
|
if (rightSibling !== null && branchChildCount(rightSibling) > state.minBranchChildren) {
|
|
679
767
|
borrowFromRightBranch(branch, rightSibling, branchIndex);
|
|
680
768
|
return;
|
|
@@ -693,44 +781,70 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
|
|
|
693
781
|
}
|
|
694
782
|
throw new BTreeInvariantError("no branch siblings to rebalance");
|
|
695
783
|
};
|
|
784
|
+
|
|
785
|
+
// src/btree/rebalance.ts
|
|
786
|
+
var requireLeafNode = (node) => {
|
|
787
|
+
if (!isLeafNode(node))
|
|
788
|
+
throw new BTreeInvariantError("expected leaf, got branch");
|
|
789
|
+
return node;
|
|
790
|
+
};
|
|
791
|
+
var detachLeafFromChain = (state, leaf) => {
|
|
792
|
+
if (leaf.prev !== null) {
|
|
793
|
+
leaf.prev.next = leaf.next;
|
|
794
|
+
} else if (leaf.next !== null) {
|
|
795
|
+
state.leftmostLeaf = leaf.next;
|
|
796
|
+
}
|
|
797
|
+
if (leaf.next !== null) {
|
|
798
|
+
leaf.next.prev = leaf.prev;
|
|
799
|
+
} else if (leaf.prev !== null) {
|
|
800
|
+
state.rightmostLeaf = leaf.prev;
|
|
801
|
+
}
|
|
802
|
+
leaf.prev = null;
|
|
803
|
+
leaf.next = null;
|
|
804
|
+
};
|
|
696
805
|
var mergeLeafEntries = (target, source) => {
|
|
697
806
|
if (target.entryOffset > 0) {
|
|
698
807
|
target.entries.copyWithin(0, target.entryOffset);
|
|
699
808
|
target.entries.length = target.entries.length - target.entryOffset;
|
|
700
809
|
target.entryOffset = 0;
|
|
701
810
|
}
|
|
702
|
-
|
|
703
|
-
for (let i = source.entryOffset; i < src.length; i += 1) target.entries.push(src[i]);
|
|
811
|
+
target.entries.push(...source.entries.slice(source.entryOffset));
|
|
704
812
|
};
|
|
705
|
-
var
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
state.rightmostLeaf = leaf;
|
|
710
|
-
}
|
|
711
|
-
return;
|
|
813
|
+
var applyLazyThreshold = (min) => Math.max(1, Math.ceil(min / 4));
|
|
814
|
+
var leafRebalanceThreshold = (state) => {
|
|
815
|
+
if (state.deleteRebalancePolicy === "lazy") {
|
|
816
|
+
return applyLazyThreshold(state.minLeafEntries);
|
|
712
817
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
const
|
|
717
|
-
const
|
|
718
|
-
|
|
818
|
+
return state.minLeafEntries;
|
|
819
|
+
};
|
|
820
|
+
var findLeafSiblings = (parent, leafIndex) => {
|
|
821
|
+
const left = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
|
|
822
|
+
const right = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
|
|
823
|
+
return { left, right };
|
|
824
|
+
};
|
|
825
|
+
var tryBorrowFromLeafSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
|
|
719
826
|
if (rightSibling !== null && leafEntryCount(rightSibling) > state.minLeafEntries) {
|
|
720
827
|
const borrowed = leafShiftEntry(rightSibling);
|
|
721
|
-
if (borrowed === void 0)
|
|
828
|
+
if (borrowed === void 0)
|
|
829
|
+
throw new BTreeInvariantError("right leaf borrow failed");
|
|
830
|
+
const leafWasEmpty = leafEntryCount(leaf) === 0;
|
|
722
831
|
leaf.entries.push(borrowed);
|
|
723
832
|
writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
|
|
724
|
-
|
|
833
|
+
if (leafWasEmpty) updateMinKeyInAncestors(leaf);
|
|
834
|
+
return true;
|
|
725
835
|
}
|
|
726
836
|
if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
|
|
727
837
|
const borrowed = leftSibling.entries.pop();
|
|
728
|
-
if (borrowed === void 0)
|
|
838
|
+
if (borrowed === void 0)
|
|
839
|
+
throw new BTreeInvariantError("left leaf borrow failed");
|
|
729
840
|
leafUnshiftEntry(leaf, borrowed);
|
|
730
841
|
parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
|
|
731
842
|
updateMinKeyInAncestors(leaf);
|
|
732
|
-
return;
|
|
843
|
+
return true;
|
|
733
844
|
}
|
|
845
|
+
return false;
|
|
846
|
+
};
|
|
847
|
+
var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
|
|
734
848
|
if (leftSibling !== null) {
|
|
735
849
|
mergeLeafEntries(leftSibling, leaf);
|
|
736
850
|
detachLeafFromChain(state, leaf);
|
|
@@ -739,14 +853,34 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
|
739
853
|
return;
|
|
740
854
|
}
|
|
741
855
|
if (rightSibling !== null) {
|
|
856
|
+
const leafWasEmpty = leafEntryCount(leaf) === 0;
|
|
742
857
|
mergeLeafEntries(leaf, rightSibling);
|
|
743
858
|
detachLeafFromChain(state, rightSibling);
|
|
859
|
+
if (leafWasEmpty) updateMinKeyInAncestors(leaf);
|
|
744
860
|
removeChildFromBranch(parent, leafIndex + 1);
|
|
745
861
|
rebalanceAfterBranchRemoval(state, parent);
|
|
746
862
|
return;
|
|
747
863
|
}
|
|
748
864
|
throw new BTreeInvariantError("no leaf siblings to rebalance");
|
|
749
865
|
};
|
|
866
|
+
var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
867
|
+
if (leaf === state.root) {
|
|
868
|
+
if (state.entryCount === 0) {
|
|
869
|
+
state.leftmostLeaf = leaf;
|
|
870
|
+
state.rightmostLeaf = leaf;
|
|
871
|
+
}
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
|
|
875
|
+
const parent = leaf.parent;
|
|
876
|
+
if (parent === null)
|
|
877
|
+
throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
|
|
878
|
+
const leafIndex = leaf.indexInParent;
|
|
879
|
+
const { left, right } = findLeafSiblings(parent, leafIndex);
|
|
880
|
+
if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
|
|
881
|
+
return;
|
|
882
|
+
mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
|
|
883
|
+
};
|
|
750
884
|
|
|
751
885
|
// src/btree/deleteRange.ts
|
|
752
886
|
var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
@@ -759,16 +893,16 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
|
759
893
|
}
|
|
760
894
|
return { leaf, idx };
|
|
761
895
|
};
|
|
762
|
-
var findRemoveEnd = (state, leaf,
|
|
896
|
+
var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
|
|
763
897
|
const count = leafEntryCount(leaf);
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
|
|
769
|
-
removeEnd += 1;
|
|
898
|
+
const lastEntry = leafEntryAt(leaf, count - 1);
|
|
899
|
+
const cmpLast = state.compareKeys(lastEntry.key, endKey);
|
|
900
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
901
|
+
return count;
|
|
770
902
|
}
|
|
771
|
-
|
|
903
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
904
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
|
|
905
|
+
return endBound < count ? endBound : count;
|
|
772
906
|
};
|
|
773
907
|
var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
774
908
|
if (state.entryKeys !== null) {
|
|
@@ -785,15 +919,14 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
|
785
919
|
updateMinKeyInAncestors(leaf);
|
|
786
920
|
}
|
|
787
921
|
const countAfterSplice = leafEntryCount(leaf);
|
|
922
|
+
const rebalThreshold = leafRebalanceThreshold(state);
|
|
788
923
|
let safetyGuard = state.minLeafEntries + 4;
|
|
789
|
-
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) <
|
|
924
|
+
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
|
|
790
925
|
rebalanceAfterLeafRemoval(state, leaf);
|
|
791
|
-
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
926
|
+
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
927
|
+
break;
|
|
792
928
|
safetyGuard -= 1;
|
|
793
929
|
}
|
|
794
|
-
if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
|
|
795
|
-
updateMinKeyInAncestors(leaf);
|
|
796
|
-
}
|
|
797
930
|
return countAfterSplice;
|
|
798
931
|
};
|
|
799
932
|
var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
|
|
@@ -816,10 +949,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
816
949
|
}
|
|
817
950
|
if (idx >= leafEntryCount(leaf)) break;
|
|
818
951
|
const count = leafEntryCount(leaf);
|
|
819
|
-
const removeEnd = findRemoveEnd(state, leaf,
|
|
952
|
+
const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
|
|
820
953
|
const removeCount = removeEnd - idx;
|
|
821
954
|
if (removeCount === 0) break;
|
|
822
|
-
const countAfterSplice = spliceLeafAndRebalance(
|
|
955
|
+
const countAfterSplice = spliceLeafAndRebalance(
|
|
956
|
+
state,
|
|
957
|
+
leaf,
|
|
958
|
+
idx,
|
|
959
|
+
removeCount
|
|
960
|
+
);
|
|
823
961
|
deleted += removeCount;
|
|
824
962
|
if (removeEnd < count) break;
|
|
825
963
|
if (!isLeafStillValid(state, leaf)) {
|
|
@@ -865,25 +1003,42 @@ var computeNextAutoScaleThreshold = (entryCount) => {
|
|
|
865
1003
|
}
|
|
866
1004
|
return Number.MAX_SAFE_INTEGER;
|
|
867
1005
|
};
|
|
868
|
-
var
|
|
869
|
-
if (typeof config.compareKeys !== "function") {
|
|
870
|
-
throw new BTreeValidationError("compareKeys must be a function.");
|
|
871
|
-
}
|
|
872
|
-
const autoScale = config.autoScale === true;
|
|
1006
|
+
var resolveInitialCapacity = (config, autoScale) => {
|
|
873
1007
|
if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
|
|
874
|
-
throw new BTreeValidationError(
|
|
1008
|
+
throw new BTreeValidationError(
|
|
1009
|
+
"autoScale conflicts with explicit capacity."
|
|
1010
|
+
);
|
|
875
1011
|
}
|
|
876
|
-
let maxLeafEntries;
|
|
877
|
-
let maxBranchChildren;
|
|
878
1012
|
if (autoScale) {
|
|
879
1013
|
const tier = computeAutoScaleTier(0);
|
|
880
|
-
maxLeafEntries
|
|
881
|
-
maxBranchChildren = tier.maxBranch;
|
|
882
|
-
} else {
|
|
883
|
-
maxLeafEntries = normalizeNodeCapacity(config.maxLeafEntries, "maxLeafEntries", DEFAULT_MAX_LEAF_ENTRIES);
|
|
884
|
-
maxBranchChildren = normalizeNodeCapacity(config.maxBranchChildren, "maxBranchChildren", DEFAULT_MAX_BRANCH_CHILDREN);
|
|
1014
|
+
return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
|
|
885
1015
|
}
|
|
1016
|
+
return {
|
|
1017
|
+
maxLeafEntries: normalizeNodeCapacity(
|
|
1018
|
+
config.maxLeafEntries,
|
|
1019
|
+
"maxLeafEntries",
|
|
1020
|
+
DEFAULT_MAX_LEAF_ENTRIES
|
|
1021
|
+
),
|
|
1022
|
+
maxBranchChildren: normalizeNodeCapacity(
|
|
1023
|
+
config.maxBranchChildren,
|
|
1024
|
+
"maxBranchChildren",
|
|
1025
|
+
DEFAULT_MAX_BRANCH_CHILDREN
|
|
1026
|
+
)
|
|
1027
|
+
};
|
|
1028
|
+
};
|
|
1029
|
+
var createInitialState = (config) => {
|
|
1030
|
+
if (typeof config.compareKeys !== "function") {
|
|
1031
|
+
throw new BTreeValidationError("compareKeys must be a function.");
|
|
1032
|
+
}
|
|
1033
|
+
const autoScale = config.autoScale === true;
|
|
1034
|
+
const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
|
|
1035
|
+
config,
|
|
1036
|
+
autoScale
|
|
1037
|
+
);
|
|
886
1038
|
const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
|
|
1039
|
+
const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
|
|
1040
|
+
config.deleteRebalancePolicy
|
|
1041
|
+
);
|
|
887
1042
|
const emptyLeaf = createLeafNode([], null);
|
|
888
1043
|
return {
|
|
889
1044
|
compareKeys: config.compareKeys,
|
|
@@ -899,6 +1054,7 @@ var createInitialState = (config) => {
|
|
|
899
1054
|
nextSequence: 0,
|
|
900
1055
|
entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
|
|
901
1056
|
autoScale,
|
|
1057
|
+
deleteRebalancePolicy,
|
|
902
1058
|
_nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
|
|
903
1059
|
_cursor: { leaf: emptyLeaf, index: 0 }
|
|
904
1060
|
};
|
|
@@ -914,7 +1070,9 @@ var maybeAutoScale = (state) => {
|
|
|
914
1070
|
state.maxBranchChildren = maxBranch;
|
|
915
1071
|
state.minBranchChildren = minOccupancy(maxBranch);
|
|
916
1072
|
}
|
|
917
|
-
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1073
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1074
|
+
state.entryCount
|
|
1075
|
+
);
|
|
918
1076
|
};
|
|
919
1077
|
var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
|
|
920
1078
|
if (!state.autoScale) {
|
|
@@ -941,6 +1099,14 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
|
|
|
941
1099
|
state.minLeafEntries = minOccupancy(normalizedLeaf);
|
|
942
1100
|
state.minBranchChildren = minOccupancy(normalizedBranch);
|
|
943
1101
|
};
|
|
1102
|
+
var resetAutoScaleToTier0 = (state) => {
|
|
1103
|
+
const tier = computeAutoScaleTier(0);
|
|
1104
|
+
state.maxLeafEntries = tier.maxLeaf;
|
|
1105
|
+
state.maxBranchChildren = tier.maxBranch;
|
|
1106
|
+
state.minLeafEntries = minOccupancy(tier.maxLeaf);
|
|
1107
|
+
state.minBranchChildren = minOccupancy(tier.maxBranch);
|
|
1108
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
1109
|
+
};
|
|
944
1110
|
|
|
945
1111
|
// src/btree/bulkLoad.ts
|
|
946
1112
|
var computeChunkBoundaries = (total, max, min) => {
|
|
@@ -961,7 +1127,11 @@ var computeChunkBoundaries = (total, max, min) => {
|
|
|
961
1127
|
return boundaries;
|
|
962
1128
|
};
|
|
963
1129
|
var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
964
|
-
const boundaries = computeChunkBoundaries(
|
|
1130
|
+
const boundaries = computeChunkBoundaries(
|
|
1131
|
+
entries.length,
|
|
1132
|
+
state.maxLeafEntries,
|
|
1133
|
+
state.minLeafEntries
|
|
1134
|
+
);
|
|
965
1135
|
const leaves = new Array(boundaries.length);
|
|
966
1136
|
let chunkStart = 0;
|
|
967
1137
|
for (let c = 0; c < boundaries.length; c += 1) {
|
|
@@ -969,7 +1139,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
|
969
1139
|
const chunk = new Array(chunkEnd - chunkStart);
|
|
970
1140
|
for (let i = chunkStart; i < chunkEnd; i += 1) {
|
|
971
1141
|
const seq = baseSequence + i;
|
|
972
|
-
chunk[i - chunkStart] =
|
|
1142
|
+
chunk[i - chunkStart] = createEntry(
|
|
1143
|
+
entries[i].key,
|
|
1144
|
+
seq,
|
|
1145
|
+
entries[i].value
|
|
1146
|
+
);
|
|
973
1147
|
ids[i] = seq;
|
|
974
1148
|
if (state.entryKeys !== null) {
|
|
975
1149
|
state.entryKeys.set(seq, entries[i].key);
|
|
@@ -1003,11 +1177,18 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
1003
1177
|
} else {
|
|
1004
1178
|
let currentLevel = leaves;
|
|
1005
1179
|
while (currentLevel.length > 1) {
|
|
1006
|
-
const bounds = computeChunkBoundaries(
|
|
1180
|
+
const bounds = computeChunkBoundaries(
|
|
1181
|
+
currentLevel.length,
|
|
1182
|
+
state.maxBranchChildren,
|
|
1183
|
+
state.minBranchChildren
|
|
1184
|
+
);
|
|
1007
1185
|
const nextLevel = new Array(bounds.length);
|
|
1008
1186
|
let start = 0;
|
|
1009
1187
|
for (let b = 0; b < bounds.length; b += 1) {
|
|
1010
|
-
nextLevel[b] = createBranchNode(
|
|
1188
|
+
nextLevel[b] = createBranchNode(
|
|
1189
|
+
currentLevel.slice(start, bounds[b]),
|
|
1190
|
+
null
|
|
1191
|
+
);
|
|
1011
1192
|
start = bounds[b];
|
|
1012
1193
|
}
|
|
1013
1194
|
currentLevel = nextLevel;
|
|
@@ -1018,9 +1199,12 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
1018
1199
|
return ids;
|
|
1019
1200
|
};
|
|
1020
1201
|
|
|
1021
|
-
// src/btree/
|
|
1202
|
+
// src/btree/split.ts
|
|
1022
1203
|
var insertChildAfter = (state, parent, existingChild, childToInsert) => {
|
|
1023
|
-
const newChildMinKey = {
|
|
1204
|
+
const newChildMinKey = {
|
|
1205
|
+
key: void 0,
|
|
1206
|
+
sequence: 0
|
|
1207
|
+
};
|
|
1024
1208
|
if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
|
|
1025
1209
|
throw new BTreeInvariantError("inserted child has no min key");
|
|
1026
1210
|
}
|
|
@@ -1058,7 +1242,10 @@ var splitLeaf = (state, leaf) => {
|
|
|
1058
1242
|
var splitBranch = (state, branch) => {
|
|
1059
1243
|
branchCompact(branch);
|
|
1060
1244
|
const splitAt = Math.ceil(branch.children.length / 2);
|
|
1061
|
-
const sibling = createBranchNode(
|
|
1245
|
+
const sibling = createBranchNode(
|
|
1246
|
+
branch.children.splice(splitAt),
|
|
1247
|
+
branch.parent
|
|
1248
|
+
);
|
|
1062
1249
|
branch.keys.splice(splitAt);
|
|
1063
1250
|
if (branch.parent === null) {
|
|
1064
1251
|
state.root = createBranchNode([branch, sibling], null);
|
|
@@ -1066,42 +1253,125 @@ var splitBranch = (state, branch) => {
|
|
|
1066
1253
|
}
|
|
1067
1254
|
insertChildAfter(state, branch.parent, branch, sibling);
|
|
1068
1255
|
};
|
|
1256
|
+
|
|
1257
|
+
// src/btree/entry-lookup.ts
|
|
1258
|
+
var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
1259
|
+
const targetLeaf = findLeafForKey(state, userKey, sequence);
|
|
1260
|
+
const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
|
|
1261
|
+
if (index >= leafEntryCount(targetLeaf)) return null;
|
|
1262
|
+
const entry = leafEntryAt(targetLeaf, index);
|
|
1263
|
+
if (entry.entryId !== sequence) return null;
|
|
1264
|
+
return { leaf: targetLeaf, index };
|
|
1265
|
+
};
|
|
1266
|
+
var peekEntryById = (state, entryId) => {
|
|
1267
|
+
if (state.entryKeys === null) {
|
|
1268
|
+
throw new BTreeInvariantError(
|
|
1269
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1270
|
+
);
|
|
1271
|
+
}
|
|
1272
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1273
|
+
if (userKey === void 0) return null;
|
|
1274
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1275
|
+
if (found === null) return null;
|
|
1276
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1277
|
+
return entry;
|
|
1278
|
+
};
|
|
1279
|
+
var updateEntryById = (state, entryId, newValue) => {
|
|
1280
|
+
if (state.entryKeys === null) {
|
|
1281
|
+
throw new BTreeInvariantError(
|
|
1282
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1286
|
+
if (userKey === void 0) return null;
|
|
1287
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1288
|
+
if (found === null) return null;
|
|
1289
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1290
|
+
const updated = createEntry(entry.key, entry.entryId, newValue);
|
|
1291
|
+
found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
|
|
1292
|
+
return updated;
|
|
1293
|
+
};
|
|
1294
|
+
var removeEntryById = (state, entryId) => {
|
|
1295
|
+
if (state.entryKeys === null) {
|
|
1296
|
+
throw new BTreeInvariantError(
|
|
1297
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1298
|
+
);
|
|
1299
|
+
}
|
|
1300
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1301
|
+
if (userKey === void 0) return null;
|
|
1302
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1303
|
+
if (found === null) return null;
|
|
1304
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1305
|
+
leafRemoveAt(found.leaf, found.index);
|
|
1306
|
+
state.entryCount -= 1;
|
|
1307
|
+
state.entryKeys.delete(entryId);
|
|
1308
|
+
if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
|
|
1309
|
+
updateMinKeyInAncestors(found.leaf);
|
|
1310
|
+
}
|
|
1311
|
+
if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
|
|
1312
|
+
rebalanceAfterLeafRemoval(state, found.leaf);
|
|
1313
|
+
}
|
|
1314
|
+
return entry;
|
|
1315
|
+
};
|
|
1316
|
+
|
|
1317
|
+
// src/btree/mutations.ts
|
|
1318
|
+
var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
|
|
1319
|
+
if (state.duplicateKeys === "allow") return null;
|
|
1320
|
+
if (insertAt > 0) {
|
|
1321
|
+
const candidate = leafEntryAt(targetLeaf, insertAt - 1);
|
|
1322
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1323
|
+
return {
|
|
1324
|
+
leaf: targetLeaf,
|
|
1325
|
+
physIndex: targetLeaf.entryOffset + insertAt - 1,
|
|
1326
|
+
entry: candidate
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
} else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
|
|
1330
|
+
const prevLeaf = targetLeaf.prev;
|
|
1331
|
+
const prevCount = leafEntryCount(prevLeaf);
|
|
1332
|
+
const candidate = leafEntryAt(prevLeaf, prevCount - 1);
|
|
1333
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1334
|
+
return {
|
|
1335
|
+
leaf: prevLeaf,
|
|
1336
|
+
physIndex: prevLeaf.entryOffset + prevCount - 1,
|
|
1337
|
+
entry: candidate
|
|
1338
|
+
};
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
return null;
|
|
1342
|
+
};
|
|
1069
1343
|
var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
|
|
1070
1344
|
const sequence = state.nextSequence;
|
|
1071
1345
|
const insertAt = upperBoundInLeaf(state, targetLeaf, key, sequence);
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
if (
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
existingEntry = candidate;
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
if (existingEntry !== null) {
|
|
1087
|
-
if (state.duplicateKeys === "reject") {
|
|
1088
|
-
throw new BTreeValidationError("Duplicate key rejected.");
|
|
1089
|
-
}
|
|
1090
|
-
existingEntry.value = value;
|
|
1091
|
-
return existingEntry.entryId;
|
|
1092
|
-
}
|
|
1346
|
+
const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
|
|
1347
|
+
if (dup !== null) {
|
|
1348
|
+
if (state.duplicateKeys === "reject") {
|
|
1349
|
+
throw new BTreeValidationError("Duplicate key rejected.");
|
|
1350
|
+
}
|
|
1351
|
+
dup.leaf.entries[dup.physIndex] = createEntry(
|
|
1352
|
+
dup.entry.key,
|
|
1353
|
+
dup.entry.entryId,
|
|
1354
|
+
value
|
|
1355
|
+
);
|
|
1356
|
+
return dup.entry.entryId;
|
|
1093
1357
|
}
|
|
1094
1358
|
if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
|
|
1095
1359
|
throw new BTreeValidationError("Sequence overflow.");
|
|
1096
1360
|
}
|
|
1097
1361
|
state.nextSequence += 1;
|
|
1098
|
-
leafInsertAt(
|
|
1362
|
+
leafInsertAt(
|
|
1363
|
+
targetLeaf,
|
|
1364
|
+
insertAt,
|
|
1365
|
+
createEntry(key, sequence, value)
|
|
1366
|
+
);
|
|
1099
1367
|
state.entryCount += 1;
|
|
1100
1368
|
if (state.entryKeys !== null) {
|
|
1101
1369
|
state.entryKeys.set(sequence, key);
|
|
1102
1370
|
}
|
|
1103
|
-
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1104
|
-
|
|
1371
|
+
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1372
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1373
|
+
if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
|
|
1374
|
+
splitLeaf(state, targetLeaf);
|
|
1105
1375
|
maybeAutoScale(state);
|
|
1106
1376
|
return sequence;
|
|
1107
1377
|
};
|
|
@@ -1112,7 +1382,8 @@ var putEntry = (state, key, value) => {
|
|
|
1112
1382
|
var popFirstEntry = (state) => {
|
|
1113
1383
|
if (state.entryCount === 0) return null;
|
|
1114
1384
|
const firstEntry = leafShiftEntry(state.leftmostLeaf);
|
|
1115
|
-
if (firstEntry === void 0)
|
|
1385
|
+
if (firstEntry === void 0)
|
|
1386
|
+
throw new BTreeInvariantError("leftmost leaf empty but count > 0");
|
|
1116
1387
|
state.entryCount -= 1;
|
|
1117
1388
|
if (state.entryKeys !== null) {
|
|
1118
1389
|
state.entryKeys.delete(firstEntry.entryId);
|
|
@@ -1128,7 +1399,8 @@ var popFirstEntry = (state) => {
|
|
|
1128
1399
|
var popLastEntry = (state) => {
|
|
1129
1400
|
if (state.entryCount === 0) return null;
|
|
1130
1401
|
const lastEntry = leafPopEntry(state.rightmostLeaf);
|
|
1131
|
-
if (lastEntry === void 0)
|
|
1402
|
+
if (lastEntry === void 0)
|
|
1403
|
+
throw new BTreeInvariantError("rightmost leaf empty but count > 0");
|
|
1132
1404
|
state.entryCount -= 1;
|
|
1133
1405
|
if (state.entryKeys !== null) {
|
|
1134
1406
|
state.entryKeys.delete(lastEntry.entryId);
|
|
@@ -1149,70 +1421,22 @@ var removeFirstMatchingEntry = (state, key) => {
|
|
|
1149
1421
|
if (state.entryKeys !== null) {
|
|
1150
1422
|
state.entryKeys.delete(targetEntry.entryId);
|
|
1151
1423
|
}
|
|
1152
|
-
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1424
|
+
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1425
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1153
1426
|
if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
|
|
1154
1427
|
rebalanceAfterLeafRemoval(state, targetLeaf);
|
|
1155
1428
|
}
|
|
1156
1429
|
return targetEntry;
|
|
1157
1430
|
};
|
|
1158
|
-
var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
1159
|
-
const targetLeaf = findLeafForKey(state, userKey, sequence);
|
|
1160
|
-
const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
|
|
1161
|
-
if (index >= leafEntryCount(targetLeaf)) return null;
|
|
1162
|
-
const entry = leafEntryAt(targetLeaf, index);
|
|
1163
|
-
if (entry.entryId !== sequence) return null;
|
|
1164
|
-
return { leaf: targetLeaf, index };
|
|
1165
|
-
};
|
|
1166
|
-
var removeEntryById = (state, entryId) => {
|
|
1167
|
-
if (state.entryKeys === null) {
|
|
1168
|
-
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1169
|
-
}
|
|
1170
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1171
|
-
if (userKey === void 0) return null;
|
|
1172
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1173
|
-
if (found === null) return null;
|
|
1174
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1175
|
-
leafRemoveAt(found.leaf, found.index);
|
|
1176
|
-
state.entryCount -= 1;
|
|
1177
|
-
state.entryKeys.delete(entryId);
|
|
1178
|
-
if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
|
|
1179
|
-
updateMinKeyInAncestors(found.leaf);
|
|
1180
|
-
}
|
|
1181
|
-
if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
|
|
1182
|
-
rebalanceAfterLeafRemoval(state, found.leaf);
|
|
1183
|
-
}
|
|
1184
|
-
return entry;
|
|
1185
|
-
};
|
|
1186
|
-
var peekEntryById = (state, entryId) => {
|
|
1187
|
-
if (state.entryKeys === null) {
|
|
1188
|
-
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1189
|
-
}
|
|
1190
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1191
|
-
if (userKey === void 0) return null;
|
|
1192
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1193
|
-
if (found === null) return null;
|
|
1194
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1195
|
-
return entry;
|
|
1196
|
-
};
|
|
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
|
-
}
|
|
1201
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1202
|
-
if (userKey === void 0) return null;
|
|
1203
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1204
|
-
if (found === null) return null;
|
|
1205
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1206
|
-
entry.value = newValue;
|
|
1207
|
-
return entry;
|
|
1208
|
-
};
|
|
1209
1431
|
var putManyEntries = (state, entries) => {
|
|
1210
1432
|
if (entries.length === 0) return [];
|
|
1211
1433
|
const strictlyAscending = state.duplicateKeys !== "allow";
|
|
1212
1434
|
for (let i = 1; i < entries.length; i += 1) {
|
|
1213
1435
|
const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
|
|
1214
1436
|
if (cmp > 0) {
|
|
1215
|
-
throw new BTreeValidationError(
|
|
1437
|
+
throw new BTreeValidationError(
|
|
1438
|
+
"putMany: entries not in ascending order."
|
|
1439
|
+
);
|
|
1216
1440
|
}
|
|
1217
1441
|
if (strictlyAscending && cmp === 0) {
|
|
1218
1442
|
throw new BTreeValidationError(
|
|
@@ -1225,7 +1449,12 @@ var putManyEntries = (state, entries) => {
|
|
|
1225
1449
|
let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
|
|
1226
1450
|
for (let i = 0; i < entries.length; i += 1) {
|
|
1227
1451
|
const entry = entries[i];
|
|
1228
|
-
const targetLeaf = findLeafFromHint(
|
|
1452
|
+
const targetLeaf = findLeafFromHint(
|
|
1453
|
+
state,
|
|
1454
|
+
hintLeaf,
|
|
1455
|
+
entry.key,
|
|
1456
|
+
state.nextSequence
|
|
1457
|
+
);
|
|
1229
1458
|
ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
|
|
1230
1459
|
hintLeaf = targetLeaf;
|
|
1231
1460
|
}
|
|
@@ -1240,7 +1469,8 @@ var buildConfigFromState = (state) => {
|
|
|
1240
1469
|
compareKeys: state.compareKeys,
|
|
1241
1470
|
duplicateKeys: state.duplicateKeys,
|
|
1242
1471
|
enableEntryIdLookup: state.entryKeys !== null,
|
|
1243
|
-
autoScale: state.autoScale
|
|
1472
|
+
autoScale: state.autoScale,
|
|
1473
|
+
deleteRebalancePolicy: state.deleteRebalancePolicy
|
|
1244
1474
|
};
|
|
1245
1475
|
if (!state.autoScale) {
|
|
1246
1476
|
config.maxLeafEntries = state.maxLeafEntries;
|
|
@@ -1260,17 +1490,17 @@ var serializeToJSON = (state) => {
|
|
|
1260
1490
|
}
|
|
1261
1491
|
leaf = leaf.next;
|
|
1262
1492
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
enableEntryIdLookup: state.entryKeys !== null,
|
|
1270
|
-
autoScale: state.autoScale
|
|
1271
|
-
},
|
|
1272
|
-
entries
|
|
1493
|
+
const config = {
|
|
1494
|
+
maxLeafEntries: state.maxLeafEntries,
|
|
1495
|
+
maxBranchChildren: state.maxBranchChildren,
|
|
1496
|
+
duplicateKeys: state.duplicateKeys,
|
|
1497
|
+
enableEntryIdLookup: state.entryKeys !== null,
|
|
1498
|
+
autoScale: state.autoScale
|
|
1273
1499
|
};
|
|
1500
|
+
if (state.deleteRebalancePolicy !== "standard") {
|
|
1501
|
+
config.deleteRebalancePolicy = state.deleteRebalancePolicy;
|
|
1502
|
+
}
|
|
1503
|
+
return { version: 1, config, entries };
|
|
1274
1504
|
};
|
|
1275
1505
|
var MAX_SERIALIZED_ENTRIES = 1e6;
|
|
1276
1506
|
var validateStructure = (json) => {
|
|
@@ -1333,13 +1563,28 @@ var validateBTreeJSON = (json) => {
|
|
|
1333
1563
|
validateStructure(json);
|
|
1334
1564
|
validateConfig(json.config);
|
|
1335
1565
|
};
|
|
1566
|
+
var validateBTreeJSONSortOrder = (json, compareKeys) => {
|
|
1567
|
+
const strict = json.config.duplicateKeys !== "allow";
|
|
1568
|
+
for (let i = 1; i < json.entries.length; i += 1) {
|
|
1569
|
+
const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
|
|
1570
|
+
if (cmp > 0) {
|
|
1571
|
+
throw new BTreeValidationError("fromJSON: entries not sorted.");
|
|
1572
|
+
}
|
|
1573
|
+
if (strict && cmp === 0) {
|
|
1574
|
+
throw new BTreeValidationError(
|
|
1575
|
+
'fromJSON: duplicate keys require duplicateKeys "allow".'
|
|
1576
|
+
);
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
};
|
|
1336
1580
|
var buildConfigFromJSON = (json, compareKeys) => {
|
|
1337
1581
|
const cfg = json.config;
|
|
1338
1582
|
const config = {
|
|
1339
1583
|
compareKeys,
|
|
1340
1584
|
duplicateKeys: cfg.duplicateKeys,
|
|
1341
1585
|
enableEntryIdLookup: cfg.enableEntryIdLookup,
|
|
1342
|
-
autoScale: cfg.autoScale
|
|
1586
|
+
autoScale: cfg.autoScale,
|
|
1587
|
+
deleteRebalancePolicy: cfg.deleteRebalancePolicy
|
|
1343
1588
|
};
|
|
1344
1589
|
if (!cfg.autoScale) {
|
|
1345
1590
|
config.maxLeafEntries = cfg.maxLeafEntries;
|
|
@@ -1348,6 +1593,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
|
|
|
1348
1593
|
return config;
|
|
1349
1594
|
};
|
|
1350
1595
|
|
|
1596
|
+
// src/btree/traversal.ts
|
|
1597
|
+
var snapshotEntries = (state) => {
|
|
1598
|
+
const result = new Array(state.entryCount);
|
|
1599
|
+
let leaf = state.leftmostLeaf;
|
|
1600
|
+
let writeIdx = 0;
|
|
1601
|
+
while (leaf !== null) {
|
|
1602
|
+
const count = leafEntryCount(leaf);
|
|
1603
|
+
for (let i = 0; i < count; i += 1) {
|
|
1604
|
+
result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
|
|
1605
|
+
}
|
|
1606
|
+
leaf = leaf.next;
|
|
1607
|
+
}
|
|
1608
|
+
return result;
|
|
1609
|
+
};
|
|
1610
|
+
var collectInternalEntries = (state) => {
|
|
1611
|
+
const result = new Array(state.entryCount);
|
|
1612
|
+
let leaf = state.leftmostLeaf;
|
|
1613
|
+
let writeIdx = 0;
|
|
1614
|
+
while (leaf !== null) {
|
|
1615
|
+
const count = leafEntryCount(leaf);
|
|
1616
|
+
for (let i = 0; i < count; i += 1) {
|
|
1617
|
+
result[writeIdx++] = leafEntryAt(leaf, i);
|
|
1618
|
+
}
|
|
1619
|
+
leaf = leaf.next;
|
|
1620
|
+
}
|
|
1621
|
+
return result;
|
|
1622
|
+
};
|
|
1623
|
+
var forEachEntry = (state, callback, thisArg) => {
|
|
1624
|
+
let leaf = state.leftmostLeaf;
|
|
1625
|
+
while (leaf !== null) {
|
|
1626
|
+
const count = leafEntryCount(leaf);
|
|
1627
|
+
for (let i = 0; i < count; i += 1) {
|
|
1628
|
+
callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
|
|
1629
|
+
}
|
|
1630
|
+
leaf = leaf.next;
|
|
1631
|
+
}
|
|
1632
|
+
};
|
|
1633
|
+
|
|
1351
1634
|
// src/btree/integrity-helpers.ts
|
|
1352
1635
|
var nodeMinKey = (node) => {
|
|
1353
1636
|
if (isLeafNode(node)) {
|
|
@@ -1356,7 +1639,10 @@ var nodeMinKey = (node) => {
|
|
|
1356
1639
|
return { key: e.key, sequence: e.entryId };
|
|
1357
1640
|
}
|
|
1358
1641
|
if (node.childOffset >= node.keys.length) return null;
|
|
1359
|
-
return {
|
|
1642
|
+
return {
|
|
1643
|
+
key: node.keys[node.childOffset].key,
|
|
1644
|
+
sequence: node.keys[node.childOffset].sequence
|
|
1645
|
+
};
|
|
1360
1646
|
};
|
|
1361
1647
|
var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
|
|
1362
1648
|
const cmp = comparator(leftKey, rightKey);
|
|
@@ -1378,9 +1664,7 @@ var getNodeMaxKey = (node) => {
|
|
|
1378
1664
|
};
|
|
1379
1665
|
var validateComparatorResult = (result) => {
|
|
1380
1666
|
if (!Number.isFinite(result)) {
|
|
1381
|
-
throw new BTreeValidationError(
|
|
1382
|
-
"compareKeys must return a finite number."
|
|
1383
|
-
);
|
|
1667
|
+
throw new BTreeValidationError("compareKeys must return a finite number.");
|
|
1384
1668
|
}
|
|
1385
1669
|
return result;
|
|
1386
1670
|
};
|
|
@@ -1440,6 +1724,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
|
|
|
1440
1724
|
throw error;
|
|
1441
1725
|
}
|
|
1442
1726
|
};
|
|
1727
|
+
var validateAdjacentLeafOrdering = (state, previous, cursor) => {
|
|
1728
|
+
const prevMax = getNodeMaxKey(previous);
|
|
1729
|
+
const currentMin = nodeMinKey(cursor);
|
|
1730
|
+
if (prevMax === null || currentMin === null) {
|
|
1731
|
+
throw new BTreeInvariantError(
|
|
1732
|
+
"Non-empty tree leaf chain contains empty leaf node."
|
|
1733
|
+
);
|
|
1734
|
+
}
|
|
1735
|
+
if (compareNodeKeys(
|
|
1736
|
+
state.compareKeys,
|
|
1737
|
+
prevMax.key,
|
|
1738
|
+
prevMax.sequence,
|
|
1739
|
+
currentMin.key,
|
|
1740
|
+
currentMin.sequence
|
|
1741
|
+
) > 0) {
|
|
1742
|
+
throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
|
|
1743
|
+
}
|
|
1744
|
+
const prevCount = leafEntryCount(previous);
|
|
1745
|
+
const curCount = leafEntryCount(cursor);
|
|
1746
|
+
if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
|
|
1747
|
+
leafEntryAt(previous, prevCount - 1).key,
|
|
1748
|
+
leafEntryAt(cursor, 0).key
|
|
1749
|
+
) === 0) {
|
|
1750
|
+
throw new BTreeInvariantError(
|
|
1751
|
+
"Duplicate user key detected across adjacent leaves with uniqueness policy."
|
|
1752
|
+
);
|
|
1753
|
+
}
|
|
1754
|
+
};
|
|
1443
1755
|
var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
1444
1756
|
if (!isLeafNode(cursor)) {
|
|
1445
1757
|
throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
|
|
@@ -1451,22 +1763,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
|
1451
1763
|
throw new BTreeInvariantError("Leaf prev pointer mismatch.");
|
|
1452
1764
|
}
|
|
1453
1765
|
if (previous !== null && isLeafNode(previous)) {
|
|
1454
|
-
|
|
1455
|
-
const currentMin = nodeMinKey(cursor);
|
|
1456
|
-
if (prevMax === null || currentMin === null) {
|
|
1457
|
-
throw new BTreeInvariantError("Non-empty tree leaf chain contains empty leaf node.");
|
|
1458
|
-
}
|
|
1459
|
-
if (compareNodeKeys(state.compareKeys, prevMax.key, prevMax.sequence, currentMin.key, currentMin.sequence) > 0) {
|
|
1460
|
-
throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
|
|
1461
|
-
}
|
|
1462
|
-
const prevCount = leafEntryCount(previous);
|
|
1463
|
-
const curCount = leafEntryCount(cursor);
|
|
1464
|
-
if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
|
|
1465
|
-
leafEntryAt(previous, prevCount - 1).key,
|
|
1466
|
-
leafEntryAt(cursor, 0).key
|
|
1467
|
-
) === 0) {
|
|
1468
|
-
throw new BTreeInvariantError("Duplicate user key detected across adjacent leaves with uniqueness policy.");
|
|
1469
|
-
}
|
|
1766
|
+
validateAdjacentLeafOrdering(state, previous, cursor);
|
|
1470
1767
|
}
|
|
1471
1768
|
};
|
|
1472
1769
|
var validateLeafLinks = (state, expectedLeafCount) => {
|
|
@@ -1475,7 +1772,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1475
1772
|
throw new BTreeInvariantError("Empty tree root must be a leaf node.");
|
|
1476
1773
|
}
|
|
1477
1774
|
if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
|
|
1478
|
-
throw new BTreeInvariantError(
|
|
1775
|
+
throw new BTreeInvariantError(
|
|
1776
|
+
"Empty tree leaf pointers must reference root leaf."
|
|
1777
|
+
);
|
|
1479
1778
|
}
|
|
1480
1779
|
return;
|
|
1481
1780
|
}
|
|
@@ -1500,7 +1799,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1500
1799
|
throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
|
|
1501
1800
|
}
|
|
1502
1801
|
if (leafCount !== expectedLeafCount) {
|
|
1503
|
-
throw new BTreeInvariantError(
|
|
1802
|
+
throw new BTreeInvariantError(
|
|
1803
|
+
"Leaf chain count mismatch with tree traversal count."
|
|
1804
|
+
);
|
|
1504
1805
|
}
|
|
1505
1806
|
};
|
|
1506
1807
|
|
|
@@ -1527,7 +1828,9 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1527
1828
|
leafEntryAt(node, index - 1).key,
|
|
1528
1829
|
leafEntryAt(node, index).key
|
|
1529
1830
|
) === 0) {
|
|
1530
|
-
throw new BTreeInvariantError(
|
|
1831
|
+
throw new BTreeInvariantError(
|
|
1832
|
+
"Duplicate user key detected in tree with uniqueness policy."
|
|
1833
|
+
);
|
|
1531
1834
|
}
|
|
1532
1835
|
}
|
|
1533
1836
|
}
|
|
@@ -1535,12 +1838,7 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1535
1838
|
const first = leafEntryAt(node, index - 2);
|
|
1536
1839
|
const second = leafEntryAt(node, index - 1);
|
|
1537
1840
|
const third = leafEntryAt(node, index);
|
|
1538
|
-
assertTransitivityAsInvariant(
|
|
1539
|
-
state,
|
|
1540
|
-
first.key,
|
|
1541
|
-
second.key,
|
|
1542
|
-
third.key
|
|
1543
|
-
);
|
|
1841
|
+
assertTransitivityAsInvariant(state, first.key, second.key, third.key);
|
|
1544
1842
|
}
|
|
1545
1843
|
if (count > state.maxLeafEntries) {
|
|
1546
1844
|
throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
|
|
@@ -1549,9 +1847,14 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1549
1847
|
var validateLeafNode = (state, node, depth) => {
|
|
1550
1848
|
validateLeafNodeOrdering(state, node);
|
|
1551
1849
|
const count = leafEntryCount(node);
|
|
1552
|
-
|
|
1850
|
+
let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
|
|
1851
|
+
if (state.deleteRebalancePolicy === "lazy") {
|
|
1852
|
+
baseMinLeaf = applyLazyThreshold(baseMinLeaf);
|
|
1853
|
+
}
|
|
1553
1854
|
if (node !== state.root && count < baseMinLeaf) {
|
|
1554
|
-
throw new BTreeInvariantError(
|
|
1855
|
+
throw new BTreeInvariantError(
|
|
1856
|
+
"Non-root leaf node violates minimum occupancy."
|
|
1857
|
+
);
|
|
1555
1858
|
}
|
|
1556
1859
|
const first = count === 0 ? null : leafEntryAt(node, 0);
|
|
1557
1860
|
const last = count === 0 ? null : leafEntryAt(node, count - 1);
|
|
@@ -1573,76 +1876,110 @@ var validateBranchStructure = (state, node) => {
|
|
|
1573
1876
|
}
|
|
1574
1877
|
const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
|
|
1575
1878
|
if (node !== state.root && liveCount < baseMinBranch) {
|
|
1576
|
-
throw new BTreeInvariantError(
|
|
1879
|
+
throw new BTreeInvariantError(
|
|
1880
|
+
"Non-root branch node violates minimum occupancy."
|
|
1881
|
+
);
|
|
1577
1882
|
}
|
|
1578
1883
|
if (liveCount > state.maxBranchChildren) {
|
|
1579
1884
|
throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
|
|
1580
1885
|
}
|
|
1581
1886
|
if (node.keys.length !== node.children.length) {
|
|
1582
|
-
throw new BTreeInvariantError(
|
|
1887
|
+
throw new BTreeInvariantError(
|
|
1888
|
+
"Branch keys array length does not match children array length."
|
|
1889
|
+
);
|
|
1583
1890
|
}
|
|
1584
1891
|
};
|
|
1585
1892
|
var validateBranchChild = (state, node, childIndex, depth) => {
|
|
1586
1893
|
const child = node.children[childIndex];
|
|
1587
1894
|
if (child.parent !== node) {
|
|
1588
|
-
throw new BTreeInvariantError(
|
|
1895
|
+
throw new BTreeInvariantError(
|
|
1896
|
+
"Child-parent pointer mismatch in branch node."
|
|
1897
|
+
);
|
|
1589
1898
|
}
|
|
1590
1899
|
if (child.indexInParent !== childIndex) {
|
|
1591
|
-
throw new BTreeInvariantError(
|
|
1900
|
+
throw new BTreeInvariantError(
|
|
1901
|
+
"Child indexInParent does not match actual position in parent."
|
|
1902
|
+
);
|
|
1592
1903
|
}
|
|
1593
1904
|
const childValidation = validateNode(state, child, depth + 1);
|
|
1594
1905
|
if (childValidation.minKey === null || childValidation.maxKey === null) {
|
|
1595
|
-
throw new BTreeInvariantError(
|
|
1906
|
+
throw new BTreeInvariantError(
|
|
1907
|
+
"Branch child must not be empty in non-root branch tree."
|
|
1908
|
+
);
|
|
1596
1909
|
}
|
|
1597
1910
|
const cachedMinKey = node.keys[childIndex];
|
|
1598
1911
|
const actualMinKey = nodeMinKey(child);
|
|
1599
|
-
if (actualMinKey === null || compareNodeKeys(
|
|
1600
|
-
|
|
1912
|
+
if (actualMinKey === null || compareNodeKeys(
|
|
1913
|
+
state.compareKeys,
|
|
1914
|
+
cachedMinKey.key,
|
|
1915
|
+
cachedMinKey.sequence,
|
|
1916
|
+
actualMinKey.key,
|
|
1917
|
+
actualMinKey.sequence
|
|
1918
|
+
) !== 0) {
|
|
1919
|
+
throw new BTreeInvariantError(
|
|
1920
|
+
"Branch cached key does not match actual child minimum key."
|
|
1921
|
+
);
|
|
1601
1922
|
}
|
|
1602
1923
|
return childValidation;
|
|
1603
1924
|
};
|
|
1925
|
+
var mergeChildValidation = (state, accumulated, childValidation) => {
|
|
1926
|
+
if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
|
|
1927
|
+
throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
|
|
1928
|
+
}
|
|
1929
|
+
if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
|
|
1930
|
+
accumulated.leafDepth = childValidation.leafDepth;
|
|
1931
|
+
}
|
|
1932
|
+
if (accumulated.previousChildMax !== null && compareNodeKeys(
|
|
1933
|
+
state.compareKeys,
|
|
1934
|
+
accumulated.previousChildMax.key,
|
|
1935
|
+
accumulated.previousChildMax.sequence,
|
|
1936
|
+
childValidation.minKey.key,
|
|
1937
|
+
childValidation.minKey.sequence
|
|
1938
|
+
) >= 0) {
|
|
1939
|
+
throw new BTreeInvariantError(
|
|
1940
|
+
"Branch child key ranges are not strictly ordered."
|
|
1941
|
+
);
|
|
1942
|
+
}
|
|
1943
|
+
if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
|
|
1944
|
+
accumulated.maxKey = childValidation.maxKey;
|
|
1945
|
+
accumulated.previousChildMax = childValidation.maxKey;
|
|
1946
|
+
accumulated.leafCount += childValidation.leafCount;
|
|
1947
|
+
accumulated.branchCount += childValidation.branchCount;
|
|
1948
|
+
accumulated.entryCount += childValidation.entryCount;
|
|
1949
|
+
};
|
|
1604
1950
|
var validateNode = (state, node, depth) => {
|
|
1605
1951
|
if (isLeafNode(node)) {
|
|
1606
1952
|
return validateLeafNode(state, node, depth);
|
|
1607
1953
|
}
|
|
1608
1954
|
validateBranchStructure(state, node);
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1955
|
+
const acc = {
|
|
1956
|
+
leafDepth: null,
|
|
1957
|
+
leafCount: 0,
|
|
1958
|
+
branchCount: 1,
|
|
1959
|
+
entryCount: 0,
|
|
1960
|
+
minKey: null,
|
|
1961
|
+
maxKey: null,
|
|
1962
|
+
previousChildMax: null
|
|
1963
|
+
};
|
|
1616
1964
|
for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
|
|
1617
1965
|
const childValidation = validateBranchChild(state, node, childIndex, depth);
|
|
1618
|
-
|
|
1619
|
-
throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
|
|
1620
|
-
}
|
|
1621
|
-
if (leafDepth === null && childValidation.leafDepth !== null) {
|
|
1622
|
-
leafDepth = childValidation.leafDepth;
|
|
1623
|
-
}
|
|
1624
|
-
if (previousChildMax !== null && compareNodeKeys(
|
|
1625
|
-
state.compareKeys,
|
|
1626
|
-
previousChildMax.key,
|
|
1627
|
-
previousChildMax.sequence,
|
|
1628
|
-
childValidation.minKey.key,
|
|
1629
|
-
childValidation.minKey.sequence
|
|
1630
|
-
) >= 0) {
|
|
1631
|
-
throw new BTreeInvariantError("Branch child key ranges are not strictly ordered.");
|
|
1632
|
-
}
|
|
1633
|
-
if (minKey === null) minKey = childValidation.minKey;
|
|
1634
|
-
maxKey = childValidation.maxKey;
|
|
1635
|
-
previousChildMax = childValidation.maxKey;
|
|
1636
|
-
leafCount += childValidation.leafCount;
|
|
1637
|
-
branchCount += childValidation.branchCount;
|
|
1638
|
-
entryCount += childValidation.entryCount;
|
|
1966
|
+
mergeChildValidation(state, acc, childValidation);
|
|
1639
1967
|
}
|
|
1640
|
-
return {
|
|
1968
|
+
return {
|
|
1969
|
+
minKey: acc.minKey,
|
|
1970
|
+
maxKey: acc.maxKey,
|
|
1971
|
+
leafDepth: acc.leafDepth,
|
|
1972
|
+
leafCount: acc.leafCount,
|
|
1973
|
+
branchCount: acc.branchCount,
|
|
1974
|
+
entryCount: acc.entryCount
|
|
1975
|
+
};
|
|
1641
1976
|
};
|
|
1642
1977
|
var assertInvariants = (state) => {
|
|
1643
1978
|
const validation = validateNode(state, state.root, 0);
|
|
1644
1979
|
if (validation.entryCount !== state.entryCount) {
|
|
1645
|
-
throw new BTreeInvariantError(
|
|
1980
|
+
throw new BTreeInvariantError(
|
|
1981
|
+
"Index entry count mismatch between tree traversal and tracked state."
|
|
1982
|
+
);
|
|
1646
1983
|
}
|
|
1647
1984
|
validateLeafLinks(state, validation.leafCount);
|
|
1648
1985
|
};
|
|
@@ -1698,7 +2035,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1698
2035
|
remove(key) {
|
|
1699
2036
|
const entry = removeFirstMatchingEntry(this.state, key);
|
|
1700
2037
|
if (entry === null) return null;
|
|
1701
|
-
return
|
|
2038
|
+
return freezeEntry(entry);
|
|
1702
2039
|
}
|
|
1703
2040
|
removeById(entryId) {
|
|
1704
2041
|
if (this.state.entryKeys === null) {
|
|
@@ -1706,7 +2043,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1706
2043
|
}
|
|
1707
2044
|
const entry = removeEntryById(this.state, entryId);
|
|
1708
2045
|
if (entry === null) return null;
|
|
1709
|
-
return
|
|
2046
|
+
return freezeEntry(entry);
|
|
1710
2047
|
}
|
|
1711
2048
|
peekById(entryId) {
|
|
1712
2049
|
if (this.state.entryKeys === null) {
|
|
@@ -1714,7 +2051,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1714
2051
|
}
|
|
1715
2052
|
const entry = peekEntryById(this.state, entryId);
|
|
1716
2053
|
if (entry === null) return null;
|
|
1717
|
-
return
|
|
2054
|
+
return freezeEntry(entry);
|
|
1718
2055
|
}
|
|
1719
2056
|
updateById(entryId, value) {
|
|
1720
2057
|
if (this.state.entryKeys === null) {
|
|
@@ -1722,30 +2059,30 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1722
2059
|
}
|
|
1723
2060
|
const entry = updateEntryById(this.state, entryId, value);
|
|
1724
2061
|
if (entry === null) return null;
|
|
1725
|
-
return
|
|
2062
|
+
return freezeEntry(entry);
|
|
1726
2063
|
}
|
|
1727
2064
|
popFirst() {
|
|
1728
2065
|
const entry = popFirstEntry(this.state);
|
|
1729
2066
|
if (entry === null) return null;
|
|
1730
|
-
return
|
|
2067
|
+
return freezeEntry(entry);
|
|
1731
2068
|
}
|
|
1732
2069
|
peekFirst() {
|
|
1733
2070
|
if (this.state.entryCount === 0) {
|
|
1734
2071
|
return null;
|
|
1735
2072
|
}
|
|
1736
|
-
return
|
|
2073
|
+
return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
|
|
1737
2074
|
}
|
|
1738
2075
|
peekLast() {
|
|
1739
2076
|
if (this.state.entryCount === 0) {
|
|
1740
2077
|
return null;
|
|
1741
2078
|
}
|
|
1742
2079
|
const leaf = this.state.rightmostLeaf;
|
|
1743
|
-
return
|
|
2080
|
+
return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
|
|
1744
2081
|
}
|
|
1745
2082
|
popLast() {
|
|
1746
2083
|
const entry = popLastEntry(this.state);
|
|
1747
2084
|
if (entry === null) return null;
|
|
1748
|
-
return
|
|
2085
|
+
return freezeEntry(entry);
|
|
1749
2086
|
}
|
|
1750
2087
|
clear() {
|
|
1751
2088
|
const emptyLeaf = createLeafNode([], null);
|
|
@@ -1755,16 +2092,9 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1755
2092
|
this.state.entryCount = 0;
|
|
1756
2093
|
this.state._cursor.leaf = emptyLeaf;
|
|
1757
2094
|
this.state._cursor.index = 0;
|
|
1758
|
-
|
|
1759
|
-
this.state.entryKeys.clear();
|
|
1760
|
-
}
|
|
2095
|
+
this.state.entryKeys?.clear();
|
|
1761
2096
|
if (this.state.autoScale) {
|
|
1762
|
-
|
|
1763
|
-
this.state.maxLeafEntries = tier.maxLeaf;
|
|
1764
|
-
this.state.maxBranchChildren = tier.maxBranch;
|
|
1765
|
-
this.state.minLeafEntries = minOccupancy(tier.maxLeaf);
|
|
1766
|
-
this.state.minBranchChildren = minOccupancy(tier.maxBranch);
|
|
1767
|
-
this.state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
2097
|
+
resetAutoScaleToTier0(this.state);
|
|
1768
2098
|
}
|
|
1769
2099
|
}
|
|
1770
2100
|
get(key) {
|
|
@@ -1778,65 +2108,39 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1778
2108
|
findFirst(key) {
|
|
1779
2109
|
const found = findFirstMatchingUserKey(this.state, key);
|
|
1780
2110
|
if (found === null) return null;
|
|
1781
|
-
return
|
|
2111
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1782
2112
|
}
|
|
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
|
-
*/
|
|
1787
2113
|
findLast(key) {
|
|
1788
2114
|
const found = findLastMatchingUserKey(this.state, key);
|
|
1789
2115
|
if (found === null) return null;
|
|
1790
|
-
return
|
|
2116
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1791
2117
|
}
|
|
1792
|
-
/**
|
|
1793
|
-
* Returns the smallest key in the tree that is strictly greater than `key`,
|
|
1794
|
-
* or `null` if no such key exists.
|
|
1795
|
-
*/
|
|
1796
2118
|
nextHigherKey(key) {
|
|
1797
2119
|
return findNextHigherKey(this.state, key);
|
|
1798
2120
|
}
|
|
1799
|
-
/**
|
|
1800
|
-
* Returns the largest key in the tree that is strictly less than `key`,
|
|
1801
|
-
* or `null` if no such key exists.
|
|
1802
|
-
*/
|
|
1803
2121
|
nextLowerKey(key) {
|
|
1804
2122
|
return findNextLowerKey(this.state, key);
|
|
1805
2123
|
}
|
|
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
|
-
*/
|
|
1811
2124
|
getPairOrNextLower(key) {
|
|
1812
2125
|
const found = findPairOrNextLower(this.state, key);
|
|
1813
2126
|
if (found === null) return null;
|
|
1814
|
-
return
|
|
2127
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1815
2128
|
}
|
|
1816
|
-
/**
|
|
1817
|
-
* Returns the number of entries whose keys fall within [`startKey`, `endKey`].
|
|
1818
|
-
* Pass `options` to make either bound exclusive.
|
|
1819
|
-
*/
|
|
1820
2129
|
count(startKey, endKey, options) {
|
|
1821
2130
|
return countRangeEntries(this.state, startKey, endKey, options);
|
|
1822
2131
|
}
|
|
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
|
-
*/
|
|
1828
2132
|
deleteRange(startKey, endKey, options) {
|
|
1829
2133
|
return deleteRangeEntries(this.state, startKey, endKey, options);
|
|
1830
2134
|
}
|
|
1831
2135
|
range(startKey, endKey, options) {
|
|
1832
|
-
return
|
|
2136
|
+
return rangeQueryPublicEntries(this.state, startKey, endKey, options);
|
|
1833
2137
|
}
|
|
1834
2138
|
*entries() {
|
|
1835
2139
|
let leaf = this.state.leftmostLeaf;
|
|
1836
2140
|
while (leaf !== null) {
|
|
1837
2141
|
const count = leafEntryCount(leaf);
|
|
1838
2142
|
for (let i = 0; i < count; i += 1) {
|
|
1839
|
-
yield
|
|
2143
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1840
2144
|
}
|
|
1841
2145
|
leaf = leaf.next;
|
|
1842
2146
|
}
|
|
@@ -1846,7 +2150,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1846
2150
|
while (leaf !== null) {
|
|
1847
2151
|
const count = leafEntryCount(leaf);
|
|
1848
2152
|
for (let i = count - 1; i >= 0; i -= 1) {
|
|
1849
|
-
yield
|
|
2153
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1850
2154
|
}
|
|
1851
2155
|
leaf = leaf.prev;
|
|
1852
2156
|
}
|
|
@@ -1864,55 +2168,26 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1864
2168
|
[Symbol.iterator]() {
|
|
1865
2169
|
return this.entries();
|
|
1866
2170
|
}
|
|
2171
|
+
forEachRange(startKey, endKey, callback, options) {
|
|
2172
|
+
forEachRangeEntries(this.state, startKey, endKey, callback, options);
|
|
2173
|
+
}
|
|
1867
2174
|
forEach(callback, thisArg) {
|
|
1868
|
-
|
|
1869
|
-
while (leaf !== null) {
|
|
1870
|
-
const count = leafEntryCount(leaf);
|
|
1871
|
-
for (let i = 0; i < count; i += 1) {
|
|
1872
|
-
callback.call(thisArg, toPublicEntry(leafEntryAt(leaf, i)));
|
|
1873
|
-
}
|
|
1874
|
-
leaf = leaf.next;
|
|
1875
|
-
}
|
|
2175
|
+
forEachEntry(this.state, callback, thisArg);
|
|
1876
2176
|
}
|
|
1877
2177
|
snapshot() {
|
|
1878
|
-
|
|
1879
|
-
let leaf = this.state.leftmostLeaf;
|
|
1880
|
-
let writeIdx = 0;
|
|
1881
|
-
while (leaf !== null) {
|
|
1882
|
-
const count = leafEntryCount(leaf);
|
|
1883
|
-
for (let i = 0; i < count; i += 1) {
|
|
1884
|
-
result[writeIdx++] = toPublicEntry(leafEntryAt(leaf, i));
|
|
1885
|
-
}
|
|
1886
|
-
leaf = leaf.next;
|
|
1887
|
-
}
|
|
1888
|
-
return result;
|
|
2178
|
+
return snapshotEntries(this.state);
|
|
1889
2179
|
}
|
|
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
|
-
*/
|
|
1897
2180
|
clone() {
|
|
1898
|
-
const cloned = new _InMemoryBTree(
|
|
2181
|
+
const cloned = new _InMemoryBTree(
|
|
2182
|
+
buildConfigFromState(this.state)
|
|
2183
|
+
);
|
|
1899
2184
|
applyAutoScaleCapacitySnapshot(
|
|
1900
2185
|
cloned.state,
|
|
1901
2186
|
this.state.maxLeafEntries,
|
|
1902
2187
|
this.state.maxBranchChildren
|
|
1903
2188
|
);
|
|
1904
2189
|
if (this.state.entryCount > 0) {
|
|
1905
|
-
|
|
1906
|
-
let leaf = this.state.leftmostLeaf;
|
|
1907
|
-
let writeIdx = 0;
|
|
1908
|
-
while (leaf !== null) {
|
|
1909
|
-
const count = leafEntryCount(leaf);
|
|
1910
|
-
for (let i = 0; i < count; i += 1) {
|
|
1911
|
-
pairs[writeIdx++] = leafEntryAt(leaf, i);
|
|
1912
|
-
}
|
|
1913
|
-
leaf = leaf.next;
|
|
1914
|
-
}
|
|
1915
|
-
cloned.putMany(pairs);
|
|
2190
|
+
cloned.putMany(collectInternalEntries(this.state));
|
|
1916
2191
|
}
|
|
1917
2192
|
return cloned;
|
|
1918
2193
|
}
|
|
@@ -1921,26 +2196,19 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1921
2196
|
}
|
|
1922
2197
|
static fromJSON(json, compareKeys) {
|
|
1923
2198
|
validateBTreeJSON(json);
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
throw new BTreeValidationError("fromJSON: entries not sorted.");
|
|
1929
|
-
}
|
|
1930
|
-
if (strict && cmp === 0) {
|
|
1931
|
-
throw new BTreeValidationError(
|
|
1932
|
-
'fromJSON: duplicate keys require duplicateKeys "allow".'
|
|
1933
|
-
);
|
|
1934
|
-
}
|
|
1935
|
-
}
|
|
1936
|
-
const tree = new _InMemoryBTree(buildConfigFromJSON(json, compareKeys));
|
|
2199
|
+
validateBTreeJSONSortOrder(json, compareKeys);
|
|
2200
|
+
const tree = new _InMemoryBTree(
|
|
2201
|
+
buildConfigFromJSON(json, compareKeys)
|
|
2202
|
+
);
|
|
1937
2203
|
applyAutoScaleCapacitySnapshot(
|
|
1938
2204
|
tree.state,
|
|
1939
2205
|
json.config.maxLeafEntries,
|
|
1940
2206
|
json.config.maxBranchChildren
|
|
1941
2207
|
);
|
|
1942
2208
|
if (json.entries.length > 0) {
|
|
1943
|
-
const pairs = new Array(
|
|
2209
|
+
const pairs = new Array(
|
|
2210
|
+
json.entries.length
|
|
2211
|
+
);
|
|
1944
2212
|
for (let i = 0; i < json.entries.length; i += 1) {
|
|
1945
2213
|
pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
|
|
1946
2214
|
}
|