@frostpillar/frostpillar-btree 0.2.6 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README-JA.md +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-UGGWGP4E.js} +659 -392
- 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 +659 -392
- 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 +852 -542
- package/dist/index.d.ts +1 -1
- package/dist/index.js +190 -147
- package/package.json +1 -1
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,68 @@ 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");
|
|
722
830
|
leaf.entries.push(borrowed);
|
|
723
831
|
writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
|
|
724
|
-
return;
|
|
832
|
+
return true;
|
|
725
833
|
}
|
|
726
834
|
if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
|
|
727
835
|
const borrowed = leftSibling.entries.pop();
|
|
728
|
-
if (borrowed === void 0)
|
|
836
|
+
if (borrowed === void 0)
|
|
837
|
+
throw new BTreeInvariantError("left leaf borrow failed");
|
|
729
838
|
leafUnshiftEntry(leaf, borrowed);
|
|
730
839
|
parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
|
|
731
840
|
updateMinKeyInAncestors(leaf);
|
|
732
|
-
return;
|
|
841
|
+
return true;
|
|
733
842
|
}
|
|
843
|
+
return false;
|
|
844
|
+
};
|
|
845
|
+
var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
|
|
734
846
|
if (leftSibling !== null) {
|
|
735
847
|
mergeLeafEntries(leftSibling, leaf);
|
|
736
848
|
detachLeafFromChain(state, leaf);
|
|
@@ -747,6 +859,24 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
|
747
859
|
}
|
|
748
860
|
throw new BTreeInvariantError("no leaf siblings to rebalance");
|
|
749
861
|
};
|
|
862
|
+
var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
863
|
+
if (leaf === state.root) {
|
|
864
|
+
if (state.entryCount === 0) {
|
|
865
|
+
state.leftmostLeaf = leaf;
|
|
866
|
+
state.rightmostLeaf = leaf;
|
|
867
|
+
}
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
|
|
871
|
+
const parent = leaf.parent;
|
|
872
|
+
if (parent === null)
|
|
873
|
+
throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
|
|
874
|
+
const leafIndex = leaf.indexInParent;
|
|
875
|
+
const { left, right } = findLeafSiblings(parent, leafIndex);
|
|
876
|
+
if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
|
|
877
|
+
return;
|
|
878
|
+
mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
|
|
879
|
+
};
|
|
750
880
|
|
|
751
881
|
// src/btree/deleteRange.ts
|
|
752
882
|
var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
@@ -759,16 +889,16 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
|
759
889
|
}
|
|
760
890
|
return { leaf, idx };
|
|
761
891
|
};
|
|
762
|
-
var findRemoveEnd = (state, leaf,
|
|
892
|
+
var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
|
|
763
893
|
const count = leafEntryCount(leaf);
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
|
|
769
|
-
removeEnd += 1;
|
|
894
|
+
const lastEntry = leafEntryAt(leaf, count - 1);
|
|
895
|
+
const cmpLast = state.compareKeys(lastEntry.key, endKey);
|
|
896
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
897
|
+
return count;
|
|
770
898
|
}
|
|
771
|
-
|
|
899
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
900
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
|
|
901
|
+
return endBound < count ? endBound : count;
|
|
772
902
|
};
|
|
773
903
|
var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
774
904
|
if (state.entryKeys !== null) {
|
|
@@ -785,10 +915,12 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
|
785
915
|
updateMinKeyInAncestors(leaf);
|
|
786
916
|
}
|
|
787
917
|
const countAfterSplice = leafEntryCount(leaf);
|
|
918
|
+
const rebalThreshold = leafRebalanceThreshold(state);
|
|
788
919
|
let safetyGuard = state.minLeafEntries + 4;
|
|
789
|
-
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) <
|
|
920
|
+
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
|
|
790
921
|
rebalanceAfterLeafRemoval(state, leaf);
|
|
791
|
-
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
922
|
+
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
923
|
+
break;
|
|
792
924
|
safetyGuard -= 1;
|
|
793
925
|
}
|
|
794
926
|
if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
|
|
@@ -816,10 +948,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
816
948
|
}
|
|
817
949
|
if (idx >= leafEntryCount(leaf)) break;
|
|
818
950
|
const count = leafEntryCount(leaf);
|
|
819
|
-
const removeEnd = findRemoveEnd(state, leaf,
|
|
951
|
+
const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
|
|
820
952
|
const removeCount = removeEnd - idx;
|
|
821
953
|
if (removeCount === 0) break;
|
|
822
|
-
const countAfterSplice = spliceLeafAndRebalance(
|
|
954
|
+
const countAfterSplice = spliceLeafAndRebalance(
|
|
955
|
+
state,
|
|
956
|
+
leaf,
|
|
957
|
+
idx,
|
|
958
|
+
removeCount
|
|
959
|
+
);
|
|
823
960
|
deleted += removeCount;
|
|
824
961
|
if (removeEnd < count) break;
|
|
825
962
|
if (!isLeafStillValid(state, leaf)) {
|
|
@@ -865,25 +1002,42 @@ var computeNextAutoScaleThreshold = (entryCount) => {
|
|
|
865
1002
|
}
|
|
866
1003
|
return Number.MAX_SAFE_INTEGER;
|
|
867
1004
|
};
|
|
868
|
-
var
|
|
869
|
-
if (typeof config.compareKeys !== "function") {
|
|
870
|
-
throw new BTreeValidationError("compareKeys must be a function.");
|
|
871
|
-
}
|
|
872
|
-
const autoScale = config.autoScale === true;
|
|
1005
|
+
var resolveInitialCapacity = (config, autoScale) => {
|
|
873
1006
|
if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
|
|
874
|
-
throw new BTreeValidationError(
|
|
1007
|
+
throw new BTreeValidationError(
|
|
1008
|
+
"autoScale conflicts with explicit capacity."
|
|
1009
|
+
);
|
|
875
1010
|
}
|
|
876
|
-
let maxLeafEntries;
|
|
877
|
-
let maxBranchChildren;
|
|
878
1011
|
if (autoScale) {
|
|
879
1012
|
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);
|
|
1013
|
+
return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
|
|
885
1014
|
}
|
|
1015
|
+
return {
|
|
1016
|
+
maxLeafEntries: normalizeNodeCapacity(
|
|
1017
|
+
config.maxLeafEntries,
|
|
1018
|
+
"maxLeafEntries",
|
|
1019
|
+
DEFAULT_MAX_LEAF_ENTRIES
|
|
1020
|
+
),
|
|
1021
|
+
maxBranchChildren: normalizeNodeCapacity(
|
|
1022
|
+
config.maxBranchChildren,
|
|
1023
|
+
"maxBranchChildren",
|
|
1024
|
+
DEFAULT_MAX_BRANCH_CHILDREN
|
|
1025
|
+
)
|
|
1026
|
+
};
|
|
1027
|
+
};
|
|
1028
|
+
var createInitialState = (config) => {
|
|
1029
|
+
if (typeof config.compareKeys !== "function") {
|
|
1030
|
+
throw new BTreeValidationError("compareKeys must be a function.");
|
|
1031
|
+
}
|
|
1032
|
+
const autoScale = config.autoScale === true;
|
|
1033
|
+
const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
|
|
1034
|
+
config,
|
|
1035
|
+
autoScale
|
|
1036
|
+
);
|
|
886
1037
|
const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
|
|
1038
|
+
const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
|
|
1039
|
+
config.deleteRebalancePolicy
|
|
1040
|
+
);
|
|
887
1041
|
const emptyLeaf = createLeafNode([], null);
|
|
888
1042
|
return {
|
|
889
1043
|
compareKeys: config.compareKeys,
|
|
@@ -899,6 +1053,7 @@ var createInitialState = (config) => {
|
|
|
899
1053
|
nextSequence: 0,
|
|
900
1054
|
entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
|
|
901
1055
|
autoScale,
|
|
1056
|
+
deleteRebalancePolicy,
|
|
902
1057
|
_nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
|
|
903
1058
|
_cursor: { leaf: emptyLeaf, index: 0 }
|
|
904
1059
|
};
|
|
@@ -914,7 +1069,9 @@ var maybeAutoScale = (state) => {
|
|
|
914
1069
|
state.maxBranchChildren = maxBranch;
|
|
915
1070
|
state.minBranchChildren = minOccupancy(maxBranch);
|
|
916
1071
|
}
|
|
917
|
-
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1072
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1073
|
+
state.entryCount
|
|
1074
|
+
);
|
|
918
1075
|
};
|
|
919
1076
|
var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
|
|
920
1077
|
if (!state.autoScale) {
|
|
@@ -941,6 +1098,14 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
|
|
|
941
1098
|
state.minLeafEntries = minOccupancy(normalizedLeaf);
|
|
942
1099
|
state.minBranchChildren = minOccupancy(normalizedBranch);
|
|
943
1100
|
};
|
|
1101
|
+
var resetAutoScaleToTier0 = (state) => {
|
|
1102
|
+
const tier = computeAutoScaleTier(0);
|
|
1103
|
+
state.maxLeafEntries = tier.maxLeaf;
|
|
1104
|
+
state.maxBranchChildren = tier.maxBranch;
|
|
1105
|
+
state.minLeafEntries = minOccupancy(tier.maxLeaf);
|
|
1106
|
+
state.minBranchChildren = minOccupancy(tier.maxBranch);
|
|
1107
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
1108
|
+
};
|
|
944
1109
|
|
|
945
1110
|
// src/btree/bulkLoad.ts
|
|
946
1111
|
var computeChunkBoundaries = (total, max, min) => {
|
|
@@ -961,7 +1126,11 @@ var computeChunkBoundaries = (total, max, min) => {
|
|
|
961
1126
|
return boundaries;
|
|
962
1127
|
};
|
|
963
1128
|
var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
964
|
-
const boundaries = computeChunkBoundaries(
|
|
1129
|
+
const boundaries = computeChunkBoundaries(
|
|
1130
|
+
entries.length,
|
|
1131
|
+
state.maxLeafEntries,
|
|
1132
|
+
state.minLeafEntries
|
|
1133
|
+
);
|
|
965
1134
|
const leaves = new Array(boundaries.length);
|
|
966
1135
|
let chunkStart = 0;
|
|
967
1136
|
for (let c = 0; c < boundaries.length; c += 1) {
|
|
@@ -969,7 +1138,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
|
969
1138
|
const chunk = new Array(chunkEnd - chunkStart);
|
|
970
1139
|
for (let i = chunkStart; i < chunkEnd; i += 1) {
|
|
971
1140
|
const seq = baseSequence + i;
|
|
972
|
-
chunk[i - chunkStart] =
|
|
1141
|
+
chunk[i - chunkStart] = createEntry(
|
|
1142
|
+
entries[i].key,
|
|
1143
|
+
seq,
|
|
1144
|
+
entries[i].value
|
|
1145
|
+
);
|
|
973
1146
|
ids[i] = seq;
|
|
974
1147
|
if (state.entryKeys !== null) {
|
|
975
1148
|
state.entryKeys.set(seq, entries[i].key);
|
|
@@ -1003,11 +1176,18 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
1003
1176
|
} else {
|
|
1004
1177
|
let currentLevel = leaves;
|
|
1005
1178
|
while (currentLevel.length > 1) {
|
|
1006
|
-
const bounds = computeChunkBoundaries(
|
|
1179
|
+
const bounds = computeChunkBoundaries(
|
|
1180
|
+
currentLevel.length,
|
|
1181
|
+
state.maxBranchChildren,
|
|
1182
|
+
state.minBranchChildren
|
|
1183
|
+
);
|
|
1007
1184
|
const nextLevel = new Array(bounds.length);
|
|
1008
1185
|
let start = 0;
|
|
1009
1186
|
for (let b = 0; b < bounds.length; b += 1) {
|
|
1010
|
-
nextLevel[b] = createBranchNode(
|
|
1187
|
+
nextLevel[b] = createBranchNode(
|
|
1188
|
+
currentLevel.slice(start, bounds[b]),
|
|
1189
|
+
null
|
|
1190
|
+
);
|
|
1011
1191
|
start = bounds[b];
|
|
1012
1192
|
}
|
|
1013
1193
|
currentLevel = nextLevel;
|
|
@@ -1018,9 +1198,12 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
1018
1198
|
return ids;
|
|
1019
1199
|
};
|
|
1020
1200
|
|
|
1021
|
-
// src/btree/
|
|
1201
|
+
// src/btree/split.ts
|
|
1022
1202
|
var insertChildAfter = (state, parent, existingChild, childToInsert) => {
|
|
1023
|
-
const newChildMinKey = {
|
|
1203
|
+
const newChildMinKey = {
|
|
1204
|
+
key: void 0,
|
|
1205
|
+
sequence: 0
|
|
1206
|
+
};
|
|
1024
1207
|
if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
|
|
1025
1208
|
throw new BTreeInvariantError("inserted child has no min key");
|
|
1026
1209
|
}
|
|
@@ -1058,7 +1241,10 @@ var splitLeaf = (state, leaf) => {
|
|
|
1058
1241
|
var splitBranch = (state, branch) => {
|
|
1059
1242
|
branchCompact(branch);
|
|
1060
1243
|
const splitAt = Math.ceil(branch.children.length / 2);
|
|
1061
|
-
const sibling = createBranchNode(
|
|
1244
|
+
const sibling = createBranchNode(
|
|
1245
|
+
branch.children.splice(splitAt),
|
|
1246
|
+
branch.parent
|
|
1247
|
+
);
|
|
1062
1248
|
branch.keys.splice(splitAt);
|
|
1063
1249
|
if (branch.parent === null) {
|
|
1064
1250
|
state.root = createBranchNode([branch, sibling], null);
|
|
@@ -1066,42 +1252,125 @@ var splitBranch = (state, branch) => {
|
|
|
1066
1252
|
}
|
|
1067
1253
|
insertChildAfter(state, branch.parent, branch, sibling);
|
|
1068
1254
|
};
|
|
1255
|
+
|
|
1256
|
+
// src/btree/entry-lookup.ts
|
|
1257
|
+
var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
1258
|
+
const targetLeaf = findLeafForKey(state, userKey, sequence);
|
|
1259
|
+
const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
|
|
1260
|
+
if (index >= leafEntryCount(targetLeaf)) return null;
|
|
1261
|
+
const entry = leafEntryAt(targetLeaf, index);
|
|
1262
|
+
if (entry.entryId !== sequence) return null;
|
|
1263
|
+
return { leaf: targetLeaf, index };
|
|
1264
|
+
};
|
|
1265
|
+
var peekEntryById = (state, entryId) => {
|
|
1266
|
+
if (state.entryKeys === null) {
|
|
1267
|
+
throw new BTreeInvariantError(
|
|
1268
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1269
|
+
);
|
|
1270
|
+
}
|
|
1271
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1272
|
+
if (userKey === void 0) return null;
|
|
1273
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1274
|
+
if (found === null) return null;
|
|
1275
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1276
|
+
return entry;
|
|
1277
|
+
};
|
|
1278
|
+
var updateEntryById = (state, entryId, newValue) => {
|
|
1279
|
+
if (state.entryKeys === null) {
|
|
1280
|
+
throw new BTreeInvariantError(
|
|
1281
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1285
|
+
if (userKey === void 0) return null;
|
|
1286
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1287
|
+
if (found === null) return null;
|
|
1288
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1289
|
+
const updated = createEntry(entry.key, entry.entryId, newValue);
|
|
1290
|
+
found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
|
|
1291
|
+
return updated;
|
|
1292
|
+
};
|
|
1293
|
+
var removeEntryById = (state, entryId) => {
|
|
1294
|
+
if (state.entryKeys === null) {
|
|
1295
|
+
throw new BTreeInvariantError(
|
|
1296
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1300
|
+
if (userKey === void 0) return null;
|
|
1301
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1302
|
+
if (found === null) return null;
|
|
1303
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1304
|
+
leafRemoveAt(found.leaf, found.index);
|
|
1305
|
+
state.entryCount -= 1;
|
|
1306
|
+
state.entryKeys.delete(entryId);
|
|
1307
|
+
if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
|
|
1308
|
+
updateMinKeyInAncestors(found.leaf);
|
|
1309
|
+
}
|
|
1310
|
+
if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
|
|
1311
|
+
rebalanceAfterLeafRemoval(state, found.leaf);
|
|
1312
|
+
}
|
|
1313
|
+
return entry;
|
|
1314
|
+
};
|
|
1315
|
+
|
|
1316
|
+
// src/btree/mutations.ts
|
|
1317
|
+
var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
|
|
1318
|
+
if (state.duplicateKeys === "allow") return null;
|
|
1319
|
+
if (insertAt > 0) {
|
|
1320
|
+
const candidate = leafEntryAt(targetLeaf, insertAt - 1);
|
|
1321
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1322
|
+
return {
|
|
1323
|
+
leaf: targetLeaf,
|
|
1324
|
+
physIndex: targetLeaf.entryOffset + insertAt - 1,
|
|
1325
|
+
entry: candidate
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
} else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
|
|
1329
|
+
const prevLeaf = targetLeaf.prev;
|
|
1330
|
+
const prevCount = leafEntryCount(prevLeaf);
|
|
1331
|
+
const candidate = leafEntryAt(prevLeaf, prevCount - 1);
|
|
1332
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1333
|
+
return {
|
|
1334
|
+
leaf: prevLeaf,
|
|
1335
|
+
physIndex: prevLeaf.entryOffset + prevCount - 1,
|
|
1336
|
+
entry: candidate
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
return null;
|
|
1341
|
+
};
|
|
1069
1342
|
var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
|
|
1070
1343
|
const sequence = state.nextSequence;
|
|
1071
1344
|
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
|
-
}
|
|
1345
|
+
const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
|
|
1346
|
+
if (dup !== null) {
|
|
1347
|
+
if (state.duplicateKeys === "reject") {
|
|
1348
|
+
throw new BTreeValidationError("Duplicate key rejected.");
|
|
1349
|
+
}
|
|
1350
|
+
dup.leaf.entries[dup.physIndex] = createEntry(
|
|
1351
|
+
dup.entry.key,
|
|
1352
|
+
dup.entry.entryId,
|
|
1353
|
+
value
|
|
1354
|
+
);
|
|
1355
|
+
return dup.entry.entryId;
|
|
1093
1356
|
}
|
|
1094
1357
|
if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
|
|
1095
1358
|
throw new BTreeValidationError("Sequence overflow.");
|
|
1096
1359
|
}
|
|
1097
1360
|
state.nextSequence += 1;
|
|
1098
|
-
leafInsertAt(
|
|
1361
|
+
leafInsertAt(
|
|
1362
|
+
targetLeaf,
|
|
1363
|
+
insertAt,
|
|
1364
|
+
createEntry(key, sequence, value)
|
|
1365
|
+
);
|
|
1099
1366
|
state.entryCount += 1;
|
|
1100
1367
|
if (state.entryKeys !== null) {
|
|
1101
1368
|
state.entryKeys.set(sequence, key);
|
|
1102
1369
|
}
|
|
1103
|
-
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1104
|
-
|
|
1370
|
+
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1371
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1372
|
+
if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
|
|
1373
|
+
splitLeaf(state, targetLeaf);
|
|
1105
1374
|
maybeAutoScale(state);
|
|
1106
1375
|
return sequence;
|
|
1107
1376
|
};
|
|
@@ -1112,7 +1381,8 @@ var putEntry = (state, key, value) => {
|
|
|
1112
1381
|
var popFirstEntry = (state) => {
|
|
1113
1382
|
if (state.entryCount === 0) return null;
|
|
1114
1383
|
const firstEntry = leafShiftEntry(state.leftmostLeaf);
|
|
1115
|
-
if (firstEntry === void 0)
|
|
1384
|
+
if (firstEntry === void 0)
|
|
1385
|
+
throw new BTreeInvariantError("leftmost leaf empty but count > 0");
|
|
1116
1386
|
state.entryCount -= 1;
|
|
1117
1387
|
if (state.entryKeys !== null) {
|
|
1118
1388
|
state.entryKeys.delete(firstEntry.entryId);
|
|
@@ -1128,7 +1398,8 @@ var popFirstEntry = (state) => {
|
|
|
1128
1398
|
var popLastEntry = (state) => {
|
|
1129
1399
|
if (state.entryCount === 0) return null;
|
|
1130
1400
|
const lastEntry = leafPopEntry(state.rightmostLeaf);
|
|
1131
|
-
if (lastEntry === void 0)
|
|
1401
|
+
if (lastEntry === void 0)
|
|
1402
|
+
throw new BTreeInvariantError("rightmost leaf empty but count > 0");
|
|
1132
1403
|
state.entryCount -= 1;
|
|
1133
1404
|
if (state.entryKeys !== null) {
|
|
1134
1405
|
state.entryKeys.delete(lastEntry.entryId);
|
|
@@ -1149,70 +1420,22 @@ var removeFirstMatchingEntry = (state, key) => {
|
|
|
1149
1420
|
if (state.entryKeys !== null) {
|
|
1150
1421
|
state.entryKeys.delete(targetEntry.entryId);
|
|
1151
1422
|
}
|
|
1152
|
-
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1423
|
+
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1424
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1153
1425
|
if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
|
|
1154
1426
|
rebalanceAfterLeafRemoval(state, targetLeaf);
|
|
1155
1427
|
}
|
|
1156
1428
|
return targetEntry;
|
|
1157
1429
|
};
|
|
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
1430
|
var putManyEntries = (state, entries) => {
|
|
1210
1431
|
if (entries.length === 0) return [];
|
|
1211
1432
|
const strictlyAscending = state.duplicateKeys !== "allow";
|
|
1212
1433
|
for (let i = 1; i < entries.length; i += 1) {
|
|
1213
1434
|
const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
|
|
1214
1435
|
if (cmp > 0) {
|
|
1215
|
-
throw new BTreeValidationError(
|
|
1436
|
+
throw new BTreeValidationError(
|
|
1437
|
+
"putMany: entries not in ascending order."
|
|
1438
|
+
);
|
|
1216
1439
|
}
|
|
1217
1440
|
if (strictlyAscending && cmp === 0) {
|
|
1218
1441
|
throw new BTreeValidationError(
|
|
@@ -1225,7 +1448,12 @@ var putManyEntries = (state, entries) => {
|
|
|
1225
1448
|
let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
|
|
1226
1449
|
for (let i = 0; i < entries.length; i += 1) {
|
|
1227
1450
|
const entry = entries[i];
|
|
1228
|
-
const targetLeaf = findLeafFromHint(
|
|
1451
|
+
const targetLeaf = findLeafFromHint(
|
|
1452
|
+
state,
|
|
1453
|
+
hintLeaf,
|
|
1454
|
+
entry.key,
|
|
1455
|
+
state.nextSequence
|
|
1456
|
+
);
|
|
1229
1457
|
ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
|
|
1230
1458
|
hintLeaf = targetLeaf;
|
|
1231
1459
|
}
|
|
@@ -1240,7 +1468,8 @@ var buildConfigFromState = (state) => {
|
|
|
1240
1468
|
compareKeys: state.compareKeys,
|
|
1241
1469
|
duplicateKeys: state.duplicateKeys,
|
|
1242
1470
|
enableEntryIdLookup: state.entryKeys !== null,
|
|
1243
|
-
autoScale: state.autoScale
|
|
1471
|
+
autoScale: state.autoScale,
|
|
1472
|
+
deleteRebalancePolicy: state.deleteRebalancePolicy
|
|
1244
1473
|
};
|
|
1245
1474
|
if (!state.autoScale) {
|
|
1246
1475
|
config.maxLeafEntries = state.maxLeafEntries;
|
|
@@ -1260,17 +1489,17 @@ var serializeToJSON = (state) => {
|
|
|
1260
1489
|
}
|
|
1261
1490
|
leaf = leaf.next;
|
|
1262
1491
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
enableEntryIdLookup: state.entryKeys !== null,
|
|
1270
|
-
autoScale: state.autoScale
|
|
1271
|
-
},
|
|
1272
|
-
entries
|
|
1492
|
+
const config = {
|
|
1493
|
+
maxLeafEntries: state.maxLeafEntries,
|
|
1494
|
+
maxBranchChildren: state.maxBranchChildren,
|
|
1495
|
+
duplicateKeys: state.duplicateKeys,
|
|
1496
|
+
enableEntryIdLookup: state.entryKeys !== null,
|
|
1497
|
+
autoScale: state.autoScale
|
|
1273
1498
|
};
|
|
1499
|
+
if (state.deleteRebalancePolicy !== "standard") {
|
|
1500
|
+
config.deleteRebalancePolicy = state.deleteRebalancePolicy;
|
|
1501
|
+
}
|
|
1502
|
+
return { version: 1, config, entries };
|
|
1274
1503
|
};
|
|
1275
1504
|
var MAX_SERIALIZED_ENTRIES = 1e6;
|
|
1276
1505
|
var validateStructure = (json) => {
|
|
@@ -1333,13 +1562,28 @@ var validateBTreeJSON = (json) => {
|
|
|
1333
1562
|
validateStructure(json);
|
|
1334
1563
|
validateConfig(json.config);
|
|
1335
1564
|
};
|
|
1565
|
+
var validateBTreeJSONSortOrder = (json, compareKeys) => {
|
|
1566
|
+
const strict = json.config.duplicateKeys !== "allow";
|
|
1567
|
+
for (let i = 1; i < json.entries.length; i += 1) {
|
|
1568
|
+
const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
|
|
1569
|
+
if (cmp > 0) {
|
|
1570
|
+
throw new BTreeValidationError("fromJSON: entries not sorted.");
|
|
1571
|
+
}
|
|
1572
|
+
if (strict && cmp === 0) {
|
|
1573
|
+
throw new BTreeValidationError(
|
|
1574
|
+
'fromJSON: duplicate keys require duplicateKeys "allow".'
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
};
|
|
1336
1579
|
var buildConfigFromJSON = (json, compareKeys) => {
|
|
1337
1580
|
const cfg = json.config;
|
|
1338
1581
|
const config = {
|
|
1339
1582
|
compareKeys,
|
|
1340
1583
|
duplicateKeys: cfg.duplicateKeys,
|
|
1341
1584
|
enableEntryIdLookup: cfg.enableEntryIdLookup,
|
|
1342
|
-
autoScale: cfg.autoScale
|
|
1585
|
+
autoScale: cfg.autoScale,
|
|
1586
|
+
deleteRebalancePolicy: cfg.deleteRebalancePolicy
|
|
1343
1587
|
};
|
|
1344
1588
|
if (!cfg.autoScale) {
|
|
1345
1589
|
config.maxLeafEntries = cfg.maxLeafEntries;
|
|
@@ -1348,6 +1592,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
|
|
|
1348
1592
|
return config;
|
|
1349
1593
|
};
|
|
1350
1594
|
|
|
1595
|
+
// src/btree/traversal.ts
|
|
1596
|
+
var snapshotEntries = (state) => {
|
|
1597
|
+
const result = new Array(state.entryCount);
|
|
1598
|
+
let leaf = state.leftmostLeaf;
|
|
1599
|
+
let writeIdx = 0;
|
|
1600
|
+
while (leaf !== null) {
|
|
1601
|
+
const count = leafEntryCount(leaf);
|
|
1602
|
+
for (let i = 0; i < count; i += 1) {
|
|
1603
|
+
result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
|
|
1604
|
+
}
|
|
1605
|
+
leaf = leaf.next;
|
|
1606
|
+
}
|
|
1607
|
+
return result;
|
|
1608
|
+
};
|
|
1609
|
+
var collectInternalEntries = (state) => {
|
|
1610
|
+
const result = new Array(state.entryCount);
|
|
1611
|
+
let leaf = state.leftmostLeaf;
|
|
1612
|
+
let writeIdx = 0;
|
|
1613
|
+
while (leaf !== null) {
|
|
1614
|
+
const count = leafEntryCount(leaf);
|
|
1615
|
+
for (let i = 0; i < count; i += 1) {
|
|
1616
|
+
result[writeIdx++] = leafEntryAt(leaf, i);
|
|
1617
|
+
}
|
|
1618
|
+
leaf = leaf.next;
|
|
1619
|
+
}
|
|
1620
|
+
return result;
|
|
1621
|
+
};
|
|
1622
|
+
var forEachEntry = (state, callback, thisArg) => {
|
|
1623
|
+
let leaf = state.leftmostLeaf;
|
|
1624
|
+
while (leaf !== null) {
|
|
1625
|
+
const count = leafEntryCount(leaf);
|
|
1626
|
+
for (let i = 0; i < count; i += 1) {
|
|
1627
|
+
callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
|
|
1628
|
+
}
|
|
1629
|
+
leaf = leaf.next;
|
|
1630
|
+
}
|
|
1631
|
+
};
|
|
1632
|
+
|
|
1351
1633
|
// src/btree/integrity-helpers.ts
|
|
1352
1634
|
var nodeMinKey = (node) => {
|
|
1353
1635
|
if (isLeafNode(node)) {
|
|
@@ -1356,7 +1638,10 @@ var nodeMinKey = (node) => {
|
|
|
1356
1638
|
return { key: e.key, sequence: e.entryId };
|
|
1357
1639
|
}
|
|
1358
1640
|
if (node.childOffset >= node.keys.length) return null;
|
|
1359
|
-
return {
|
|
1641
|
+
return {
|
|
1642
|
+
key: node.keys[node.childOffset].key,
|
|
1643
|
+
sequence: node.keys[node.childOffset].sequence
|
|
1644
|
+
};
|
|
1360
1645
|
};
|
|
1361
1646
|
var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
|
|
1362
1647
|
const cmp = comparator(leftKey, rightKey);
|
|
@@ -1378,9 +1663,7 @@ var getNodeMaxKey = (node) => {
|
|
|
1378
1663
|
};
|
|
1379
1664
|
var validateComparatorResult = (result) => {
|
|
1380
1665
|
if (!Number.isFinite(result)) {
|
|
1381
|
-
throw new BTreeValidationError(
|
|
1382
|
-
"compareKeys must return a finite number."
|
|
1383
|
-
);
|
|
1666
|
+
throw new BTreeValidationError("compareKeys must return a finite number.");
|
|
1384
1667
|
}
|
|
1385
1668
|
return result;
|
|
1386
1669
|
};
|
|
@@ -1440,6 +1723,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
|
|
|
1440
1723
|
throw error;
|
|
1441
1724
|
}
|
|
1442
1725
|
};
|
|
1726
|
+
var validateAdjacentLeafOrdering = (state, previous, cursor) => {
|
|
1727
|
+
const prevMax = getNodeMaxKey(previous);
|
|
1728
|
+
const currentMin = nodeMinKey(cursor);
|
|
1729
|
+
if (prevMax === null || currentMin === null) {
|
|
1730
|
+
throw new BTreeInvariantError(
|
|
1731
|
+
"Non-empty tree leaf chain contains empty leaf node."
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1734
|
+
if (compareNodeKeys(
|
|
1735
|
+
state.compareKeys,
|
|
1736
|
+
prevMax.key,
|
|
1737
|
+
prevMax.sequence,
|
|
1738
|
+
currentMin.key,
|
|
1739
|
+
currentMin.sequence
|
|
1740
|
+
) > 0) {
|
|
1741
|
+
throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
|
|
1742
|
+
}
|
|
1743
|
+
const prevCount = leafEntryCount(previous);
|
|
1744
|
+
const curCount = leafEntryCount(cursor);
|
|
1745
|
+
if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
|
|
1746
|
+
leafEntryAt(previous, prevCount - 1).key,
|
|
1747
|
+
leafEntryAt(cursor, 0).key
|
|
1748
|
+
) === 0) {
|
|
1749
|
+
throw new BTreeInvariantError(
|
|
1750
|
+
"Duplicate user key detected across adjacent leaves with uniqueness policy."
|
|
1751
|
+
);
|
|
1752
|
+
}
|
|
1753
|
+
};
|
|
1443
1754
|
var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
1444
1755
|
if (!isLeafNode(cursor)) {
|
|
1445
1756
|
throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
|
|
@@ -1451,22 +1762,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
|
1451
1762
|
throw new BTreeInvariantError("Leaf prev pointer mismatch.");
|
|
1452
1763
|
}
|
|
1453
1764
|
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
|
-
}
|
|
1765
|
+
validateAdjacentLeafOrdering(state, previous, cursor);
|
|
1470
1766
|
}
|
|
1471
1767
|
};
|
|
1472
1768
|
var validateLeafLinks = (state, expectedLeafCount) => {
|
|
@@ -1475,7 +1771,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1475
1771
|
throw new BTreeInvariantError("Empty tree root must be a leaf node.");
|
|
1476
1772
|
}
|
|
1477
1773
|
if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
|
|
1478
|
-
throw new BTreeInvariantError(
|
|
1774
|
+
throw new BTreeInvariantError(
|
|
1775
|
+
"Empty tree leaf pointers must reference root leaf."
|
|
1776
|
+
);
|
|
1479
1777
|
}
|
|
1480
1778
|
return;
|
|
1481
1779
|
}
|
|
@@ -1500,7 +1798,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1500
1798
|
throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
|
|
1501
1799
|
}
|
|
1502
1800
|
if (leafCount !== expectedLeafCount) {
|
|
1503
|
-
throw new BTreeInvariantError(
|
|
1801
|
+
throw new BTreeInvariantError(
|
|
1802
|
+
"Leaf chain count mismatch with tree traversal count."
|
|
1803
|
+
);
|
|
1504
1804
|
}
|
|
1505
1805
|
};
|
|
1506
1806
|
|
|
@@ -1527,7 +1827,9 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1527
1827
|
leafEntryAt(node, index - 1).key,
|
|
1528
1828
|
leafEntryAt(node, index).key
|
|
1529
1829
|
) === 0) {
|
|
1530
|
-
throw new BTreeInvariantError(
|
|
1830
|
+
throw new BTreeInvariantError(
|
|
1831
|
+
"Duplicate user key detected in tree with uniqueness policy."
|
|
1832
|
+
);
|
|
1531
1833
|
}
|
|
1532
1834
|
}
|
|
1533
1835
|
}
|
|
@@ -1535,12 +1837,7 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1535
1837
|
const first = leafEntryAt(node, index - 2);
|
|
1536
1838
|
const second = leafEntryAt(node, index - 1);
|
|
1537
1839
|
const third = leafEntryAt(node, index);
|
|
1538
|
-
assertTransitivityAsInvariant(
|
|
1539
|
-
state,
|
|
1540
|
-
first.key,
|
|
1541
|
-
second.key,
|
|
1542
|
-
third.key
|
|
1543
|
-
);
|
|
1840
|
+
assertTransitivityAsInvariant(state, first.key, second.key, third.key);
|
|
1544
1841
|
}
|
|
1545
1842
|
if (count > state.maxLeafEntries) {
|
|
1546
1843
|
throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
|
|
@@ -1549,9 +1846,14 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1549
1846
|
var validateLeafNode = (state, node, depth) => {
|
|
1550
1847
|
validateLeafNodeOrdering(state, node);
|
|
1551
1848
|
const count = leafEntryCount(node);
|
|
1552
|
-
|
|
1849
|
+
let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
|
|
1850
|
+
if (state.deleteRebalancePolicy === "lazy") {
|
|
1851
|
+
baseMinLeaf = applyLazyThreshold(baseMinLeaf);
|
|
1852
|
+
}
|
|
1553
1853
|
if (node !== state.root && count < baseMinLeaf) {
|
|
1554
|
-
throw new BTreeInvariantError(
|
|
1854
|
+
throw new BTreeInvariantError(
|
|
1855
|
+
"Non-root leaf node violates minimum occupancy."
|
|
1856
|
+
);
|
|
1555
1857
|
}
|
|
1556
1858
|
const first = count === 0 ? null : leafEntryAt(node, 0);
|
|
1557
1859
|
const last = count === 0 ? null : leafEntryAt(node, count - 1);
|
|
@@ -1573,76 +1875,110 @@ var validateBranchStructure = (state, node) => {
|
|
|
1573
1875
|
}
|
|
1574
1876
|
const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
|
|
1575
1877
|
if (node !== state.root && liveCount < baseMinBranch) {
|
|
1576
|
-
throw new BTreeInvariantError(
|
|
1878
|
+
throw new BTreeInvariantError(
|
|
1879
|
+
"Non-root branch node violates minimum occupancy."
|
|
1880
|
+
);
|
|
1577
1881
|
}
|
|
1578
1882
|
if (liveCount > state.maxBranchChildren) {
|
|
1579
1883
|
throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
|
|
1580
1884
|
}
|
|
1581
1885
|
if (node.keys.length !== node.children.length) {
|
|
1582
|
-
throw new BTreeInvariantError(
|
|
1886
|
+
throw new BTreeInvariantError(
|
|
1887
|
+
"Branch keys array length does not match children array length."
|
|
1888
|
+
);
|
|
1583
1889
|
}
|
|
1584
1890
|
};
|
|
1585
1891
|
var validateBranchChild = (state, node, childIndex, depth) => {
|
|
1586
1892
|
const child = node.children[childIndex];
|
|
1587
1893
|
if (child.parent !== node) {
|
|
1588
|
-
throw new BTreeInvariantError(
|
|
1894
|
+
throw new BTreeInvariantError(
|
|
1895
|
+
"Child-parent pointer mismatch in branch node."
|
|
1896
|
+
);
|
|
1589
1897
|
}
|
|
1590
1898
|
if (child.indexInParent !== childIndex) {
|
|
1591
|
-
throw new BTreeInvariantError(
|
|
1899
|
+
throw new BTreeInvariantError(
|
|
1900
|
+
"Child indexInParent does not match actual position in parent."
|
|
1901
|
+
);
|
|
1592
1902
|
}
|
|
1593
1903
|
const childValidation = validateNode(state, child, depth + 1);
|
|
1594
1904
|
if (childValidation.minKey === null || childValidation.maxKey === null) {
|
|
1595
|
-
throw new BTreeInvariantError(
|
|
1905
|
+
throw new BTreeInvariantError(
|
|
1906
|
+
"Branch child must not be empty in non-root branch tree."
|
|
1907
|
+
);
|
|
1596
1908
|
}
|
|
1597
1909
|
const cachedMinKey = node.keys[childIndex];
|
|
1598
1910
|
const actualMinKey = nodeMinKey(child);
|
|
1599
|
-
if (actualMinKey === null || compareNodeKeys(
|
|
1600
|
-
|
|
1911
|
+
if (actualMinKey === null || compareNodeKeys(
|
|
1912
|
+
state.compareKeys,
|
|
1913
|
+
cachedMinKey.key,
|
|
1914
|
+
cachedMinKey.sequence,
|
|
1915
|
+
actualMinKey.key,
|
|
1916
|
+
actualMinKey.sequence
|
|
1917
|
+
) !== 0) {
|
|
1918
|
+
throw new BTreeInvariantError(
|
|
1919
|
+
"Branch cached key does not match actual child minimum key."
|
|
1920
|
+
);
|
|
1601
1921
|
}
|
|
1602
1922
|
return childValidation;
|
|
1603
1923
|
};
|
|
1924
|
+
var mergeChildValidation = (state, accumulated, childValidation) => {
|
|
1925
|
+
if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
|
|
1926
|
+
throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
|
|
1927
|
+
}
|
|
1928
|
+
if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
|
|
1929
|
+
accumulated.leafDepth = childValidation.leafDepth;
|
|
1930
|
+
}
|
|
1931
|
+
if (accumulated.previousChildMax !== null && compareNodeKeys(
|
|
1932
|
+
state.compareKeys,
|
|
1933
|
+
accumulated.previousChildMax.key,
|
|
1934
|
+
accumulated.previousChildMax.sequence,
|
|
1935
|
+
childValidation.minKey.key,
|
|
1936
|
+
childValidation.minKey.sequence
|
|
1937
|
+
) >= 0) {
|
|
1938
|
+
throw new BTreeInvariantError(
|
|
1939
|
+
"Branch child key ranges are not strictly ordered."
|
|
1940
|
+
);
|
|
1941
|
+
}
|
|
1942
|
+
if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
|
|
1943
|
+
accumulated.maxKey = childValidation.maxKey;
|
|
1944
|
+
accumulated.previousChildMax = childValidation.maxKey;
|
|
1945
|
+
accumulated.leafCount += childValidation.leafCount;
|
|
1946
|
+
accumulated.branchCount += childValidation.branchCount;
|
|
1947
|
+
accumulated.entryCount += childValidation.entryCount;
|
|
1948
|
+
};
|
|
1604
1949
|
var validateNode = (state, node, depth) => {
|
|
1605
1950
|
if (isLeafNode(node)) {
|
|
1606
1951
|
return validateLeafNode(state, node, depth);
|
|
1607
1952
|
}
|
|
1608
1953
|
validateBranchStructure(state, node);
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1954
|
+
const acc = {
|
|
1955
|
+
leafDepth: null,
|
|
1956
|
+
leafCount: 0,
|
|
1957
|
+
branchCount: 1,
|
|
1958
|
+
entryCount: 0,
|
|
1959
|
+
minKey: null,
|
|
1960
|
+
maxKey: null,
|
|
1961
|
+
previousChildMax: null
|
|
1962
|
+
};
|
|
1616
1963
|
for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
|
|
1617
1964
|
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;
|
|
1965
|
+
mergeChildValidation(state, acc, childValidation);
|
|
1639
1966
|
}
|
|
1640
|
-
return {
|
|
1967
|
+
return {
|
|
1968
|
+
minKey: acc.minKey,
|
|
1969
|
+
maxKey: acc.maxKey,
|
|
1970
|
+
leafDepth: acc.leafDepth,
|
|
1971
|
+
leafCount: acc.leafCount,
|
|
1972
|
+
branchCount: acc.branchCount,
|
|
1973
|
+
entryCount: acc.entryCount
|
|
1974
|
+
};
|
|
1641
1975
|
};
|
|
1642
1976
|
var assertInvariants = (state) => {
|
|
1643
1977
|
const validation = validateNode(state, state.root, 0);
|
|
1644
1978
|
if (validation.entryCount !== state.entryCount) {
|
|
1645
|
-
throw new BTreeInvariantError(
|
|
1979
|
+
throw new BTreeInvariantError(
|
|
1980
|
+
"Index entry count mismatch between tree traversal and tracked state."
|
|
1981
|
+
);
|
|
1646
1982
|
}
|
|
1647
1983
|
validateLeafLinks(state, validation.leafCount);
|
|
1648
1984
|
};
|
|
@@ -1698,7 +2034,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1698
2034
|
remove(key) {
|
|
1699
2035
|
const entry = removeFirstMatchingEntry(this.state, key);
|
|
1700
2036
|
if (entry === null) return null;
|
|
1701
|
-
return
|
|
2037
|
+
return freezeEntry(entry);
|
|
1702
2038
|
}
|
|
1703
2039
|
removeById(entryId) {
|
|
1704
2040
|
if (this.state.entryKeys === null) {
|
|
@@ -1706,7 +2042,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1706
2042
|
}
|
|
1707
2043
|
const entry = removeEntryById(this.state, entryId);
|
|
1708
2044
|
if (entry === null) return null;
|
|
1709
|
-
return
|
|
2045
|
+
return freezeEntry(entry);
|
|
1710
2046
|
}
|
|
1711
2047
|
peekById(entryId) {
|
|
1712
2048
|
if (this.state.entryKeys === null) {
|
|
@@ -1714,7 +2050,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1714
2050
|
}
|
|
1715
2051
|
const entry = peekEntryById(this.state, entryId);
|
|
1716
2052
|
if (entry === null) return null;
|
|
1717
|
-
return
|
|
2053
|
+
return freezeEntry(entry);
|
|
1718
2054
|
}
|
|
1719
2055
|
updateById(entryId, value) {
|
|
1720
2056
|
if (this.state.entryKeys === null) {
|
|
@@ -1722,30 +2058,30 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1722
2058
|
}
|
|
1723
2059
|
const entry = updateEntryById(this.state, entryId, value);
|
|
1724
2060
|
if (entry === null) return null;
|
|
1725
|
-
return
|
|
2061
|
+
return freezeEntry(entry);
|
|
1726
2062
|
}
|
|
1727
2063
|
popFirst() {
|
|
1728
2064
|
const entry = popFirstEntry(this.state);
|
|
1729
2065
|
if (entry === null) return null;
|
|
1730
|
-
return
|
|
2066
|
+
return freezeEntry(entry);
|
|
1731
2067
|
}
|
|
1732
2068
|
peekFirst() {
|
|
1733
2069
|
if (this.state.entryCount === 0) {
|
|
1734
2070
|
return null;
|
|
1735
2071
|
}
|
|
1736
|
-
return
|
|
2072
|
+
return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
|
|
1737
2073
|
}
|
|
1738
2074
|
peekLast() {
|
|
1739
2075
|
if (this.state.entryCount === 0) {
|
|
1740
2076
|
return null;
|
|
1741
2077
|
}
|
|
1742
2078
|
const leaf = this.state.rightmostLeaf;
|
|
1743
|
-
return
|
|
2079
|
+
return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
|
|
1744
2080
|
}
|
|
1745
2081
|
popLast() {
|
|
1746
2082
|
const entry = popLastEntry(this.state);
|
|
1747
2083
|
if (entry === null) return null;
|
|
1748
|
-
return
|
|
2084
|
+
return freezeEntry(entry);
|
|
1749
2085
|
}
|
|
1750
2086
|
clear() {
|
|
1751
2087
|
const emptyLeaf = createLeafNode([], null);
|
|
@@ -1755,16 +2091,9 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1755
2091
|
this.state.entryCount = 0;
|
|
1756
2092
|
this.state._cursor.leaf = emptyLeaf;
|
|
1757
2093
|
this.state._cursor.index = 0;
|
|
1758
|
-
|
|
1759
|
-
this.state.entryKeys.clear();
|
|
1760
|
-
}
|
|
2094
|
+
this.state.entryKeys?.clear();
|
|
1761
2095
|
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);
|
|
2096
|
+
resetAutoScaleToTier0(this.state);
|
|
1768
2097
|
}
|
|
1769
2098
|
}
|
|
1770
2099
|
get(key) {
|
|
@@ -1778,65 +2107,39 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1778
2107
|
findFirst(key) {
|
|
1779
2108
|
const found = findFirstMatchingUserKey(this.state, key);
|
|
1780
2109
|
if (found === null) return null;
|
|
1781
|
-
return
|
|
2110
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1782
2111
|
}
|
|
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
2112
|
findLast(key) {
|
|
1788
2113
|
const found = findLastMatchingUserKey(this.state, key);
|
|
1789
2114
|
if (found === null) return null;
|
|
1790
|
-
return
|
|
2115
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1791
2116
|
}
|
|
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
2117
|
nextHigherKey(key) {
|
|
1797
2118
|
return findNextHigherKey(this.state, key);
|
|
1798
2119
|
}
|
|
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
2120
|
nextLowerKey(key) {
|
|
1804
2121
|
return findNextLowerKey(this.state, key);
|
|
1805
2122
|
}
|
|
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
2123
|
getPairOrNextLower(key) {
|
|
1812
2124
|
const found = findPairOrNextLower(this.state, key);
|
|
1813
2125
|
if (found === null) return null;
|
|
1814
|
-
return
|
|
2126
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1815
2127
|
}
|
|
1816
|
-
/**
|
|
1817
|
-
* Returns the number of entries whose keys fall within [`startKey`, `endKey`].
|
|
1818
|
-
* Pass `options` to make either bound exclusive.
|
|
1819
|
-
*/
|
|
1820
2128
|
count(startKey, endKey, options) {
|
|
1821
2129
|
return countRangeEntries(this.state, startKey, endKey, options);
|
|
1822
2130
|
}
|
|
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
2131
|
deleteRange(startKey, endKey, options) {
|
|
1829
2132
|
return deleteRangeEntries(this.state, startKey, endKey, options);
|
|
1830
2133
|
}
|
|
1831
2134
|
range(startKey, endKey, options) {
|
|
1832
|
-
return
|
|
2135
|
+
return rangeQueryPublicEntries(this.state, startKey, endKey, options);
|
|
1833
2136
|
}
|
|
1834
2137
|
*entries() {
|
|
1835
2138
|
let leaf = this.state.leftmostLeaf;
|
|
1836
2139
|
while (leaf !== null) {
|
|
1837
2140
|
const count = leafEntryCount(leaf);
|
|
1838
2141
|
for (let i = 0; i < count; i += 1) {
|
|
1839
|
-
yield
|
|
2142
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1840
2143
|
}
|
|
1841
2144
|
leaf = leaf.next;
|
|
1842
2145
|
}
|
|
@@ -1846,7 +2149,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1846
2149
|
while (leaf !== null) {
|
|
1847
2150
|
const count = leafEntryCount(leaf);
|
|
1848
2151
|
for (let i = count - 1; i >= 0; i -= 1) {
|
|
1849
|
-
yield
|
|
2152
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1850
2153
|
}
|
|
1851
2154
|
leaf = leaf.prev;
|
|
1852
2155
|
}
|
|
@@ -1864,55 +2167,26 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1864
2167
|
[Symbol.iterator]() {
|
|
1865
2168
|
return this.entries();
|
|
1866
2169
|
}
|
|
2170
|
+
forEachRange(startKey, endKey, callback, options) {
|
|
2171
|
+
forEachRangeEntries(this.state, startKey, endKey, callback, options);
|
|
2172
|
+
}
|
|
1867
2173
|
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
|
-
}
|
|
2174
|
+
forEachEntry(this.state, callback, thisArg);
|
|
1876
2175
|
}
|
|
1877
2176
|
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;
|
|
2177
|
+
return snapshotEntries(this.state);
|
|
1889
2178
|
}
|
|
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
2179
|
clone() {
|
|
1898
|
-
const cloned = new _InMemoryBTree(
|
|
2180
|
+
const cloned = new _InMemoryBTree(
|
|
2181
|
+
buildConfigFromState(this.state)
|
|
2182
|
+
);
|
|
1899
2183
|
applyAutoScaleCapacitySnapshot(
|
|
1900
2184
|
cloned.state,
|
|
1901
2185
|
this.state.maxLeafEntries,
|
|
1902
2186
|
this.state.maxBranchChildren
|
|
1903
2187
|
);
|
|
1904
2188
|
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);
|
|
2189
|
+
cloned.putMany(collectInternalEntries(this.state));
|
|
1916
2190
|
}
|
|
1917
2191
|
return cloned;
|
|
1918
2192
|
}
|
|
@@ -1921,26 +2195,19 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1921
2195
|
}
|
|
1922
2196
|
static fromJSON(json, compareKeys) {
|
|
1923
2197
|
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));
|
|
2198
|
+
validateBTreeJSONSortOrder(json, compareKeys);
|
|
2199
|
+
const tree = new _InMemoryBTree(
|
|
2200
|
+
buildConfigFromJSON(json, compareKeys)
|
|
2201
|
+
);
|
|
1937
2202
|
applyAutoScaleCapacitySnapshot(
|
|
1938
2203
|
tree.state,
|
|
1939
2204
|
json.config.maxLeafEntries,
|
|
1940
2205
|
json.config.maxBranchChildren
|
|
1941
2206
|
);
|
|
1942
2207
|
if (json.entries.length > 0) {
|
|
1943
|
-
const pairs = new Array(
|
|
2208
|
+
const pairs = new Array(
|
|
2209
|
+
json.entries.length
|
|
2210
|
+
);
|
|
1944
2211
|
for (let i = 0; i < json.entries.length; i += 1) {
|
|
1945
2212
|
pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
|
|
1946
2213
|
}
|