@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
|
@@ -47,11 +47,12 @@ var createBranchNode = (children, parent) => {
|
|
|
47
47
|
const child = children[i];
|
|
48
48
|
child.parent = branch;
|
|
49
49
|
child.indexInParent = i;
|
|
50
|
-
const target = {
|
|
50
|
+
const target = {
|
|
51
|
+
key: void 0,
|
|
52
|
+
sequence: 0
|
|
53
|
+
};
|
|
51
54
|
if (!writeMinKeyTo(child, target)) {
|
|
52
|
-
throw new BTreeInvariantError(
|
|
53
|
-
"branch child has no min key"
|
|
54
|
-
);
|
|
55
|
+
throw new BTreeInvariantError("branch child has no min key");
|
|
55
56
|
}
|
|
56
57
|
keys.push(target);
|
|
57
58
|
}
|
|
@@ -105,7 +106,14 @@ var leafInsertAt = (leaf, logicalIndex, entry) => {
|
|
|
105
106
|
leaf.entryOffset -= 1;
|
|
106
107
|
leaf.entries[phys - 1] = entry;
|
|
107
108
|
} else {
|
|
108
|
-
leaf.entries.
|
|
109
|
+
const len = leaf.entries.length;
|
|
110
|
+
if (phys >= len) {
|
|
111
|
+
leaf.entries.push(entry);
|
|
112
|
+
} else {
|
|
113
|
+
leaf.entries.push(leaf.entries[len - 1]);
|
|
114
|
+
leaf.entries.copyWithin(phys + 1, phys, len);
|
|
115
|
+
leaf.entries[phys] = entry;
|
|
116
|
+
}
|
|
109
117
|
}
|
|
110
118
|
};
|
|
111
119
|
var leafCompact = (leaf) => {
|
|
@@ -134,7 +142,11 @@ var branchInsertAt = (branch, logicalIndex, child, key) => {
|
|
|
134
142
|
const phys = branch.childOffset + logicalIndex;
|
|
135
143
|
const count = branch.children.length - branch.childOffset;
|
|
136
144
|
if (branch.childOffset > 0 && logicalIndex < count >>> 1) {
|
|
137
|
-
branch.children.copyWithin(
|
|
145
|
+
branch.children.copyWithin(
|
|
146
|
+
branch.childOffset - 1,
|
|
147
|
+
branch.childOffset,
|
|
148
|
+
phys
|
|
149
|
+
);
|
|
138
150
|
branch.keys.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
|
|
139
151
|
branch.childOffset -= 1;
|
|
140
152
|
branch.children[phys - 1] = child;
|
|
@@ -155,8 +167,16 @@ var branchRemoveAt = (branch, physIndex) => {
|
|
|
155
167
|
const logicalIndex = physIndex - branch.childOffset;
|
|
156
168
|
const count = branch.children.length - branch.childOffset;
|
|
157
169
|
if (logicalIndex < count - 1 - logicalIndex) {
|
|
158
|
-
branch.children.copyWithin(
|
|
159
|
-
|
|
170
|
+
branch.children.copyWithin(
|
|
171
|
+
branch.childOffset + 1,
|
|
172
|
+
branch.childOffset,
|
|
173
|
+
physIndex
|
|
174
|
+
);
|
|
175
|
+
branch.keys.copyWithin(
|
|
176
|
+
branch.childOffset + 1,
|
|
177
|
+
branch.childOffset,
|
|
178
|
+
physIndex
|
|
179
|
+
);
|
|
160
180
|
branch.childOffset += 1;
|
|
161
181
|
for (let i = branch.childOffset; i <= physIndex; i += 1) {
|
|
162
182
|
branch.children[i].indexInParent = i;
|
|
@@ -186,17 +206,21 @@ var normalizeDuplicateKeyPolicy = (value) => {
|
|
|
186
206
|
return "replace";
|
|
187
207
|
}
|
|
188
208
|
if (value !== "allow" && value !== "reject" && value !== "replace") {
|
|
189
|
-
throw new BTreeValidationError(
|
|
190
|
-
|
|
191
|
-
|
|
209
|
+
throw new BTreeValidationError(`Invalid duplicateKeys option.`);
|
|
210
|
+
}
|
|
211
|
+
return value;
|
|
212
|
+
};
|
|
213
|
+
var normalizeDeleteRebalancePolicy = (value) => {
|
|
214
|
+
if (value === void 0) {
|
|
215
|
+
return "standard";
|
|
216
|
+
}
|
|
217
|
+
if (value !== "standard" && value !== "lazy") {
|
|
218
|
+
throw new BTreeValidationError(`Invalid deleteRebalancePolicy option.`);
|
|
192
219
|
}
|
|
193
220
|
return value;
|
|
194
221
|
};
|
|
195
|
-
var
|
|
196
|
-
|
|
197
|
-
key: entry.key,
|
|
198
|
-
value: entry.value
|
|
199
|
-
});
|
|
222
|
+
var freezeEntry = (entry) => Object.freeze(entry);
|
|
223
|
+
var createEntry = (key, entryId, value) => Object.freeze({ key, entryId, value });
|
|
200
224
|
var isLeafNode = (node) => {
|
|
201
225
|
return node.kind === NODE_LEAF;
|
|
202
226
|
};
|
|
@@ -346,7 +370,11 @@ var hasKeyEntry = (state, key) => {
|
|
|
346
370
|
var findNextHigherKey = (state, key) => {
|
|
347
371
|
if (state.entryCount === 0) return null;
|
|
348
372
|
const compare = state.compareKeys;
|
|
349
|
-
let leaf = findLeafForKey(
|
|
373
|
+
let leaf = findLeafForKey(
|
|
374
|
+
state,
|
|
375
|
+
key,
|
|
376
|
+
Number.MAX_SAFE_INTEGER
|
|
377
|
+
);
|
|
350
378
|
let idx = upperBoundInLeaf(state, leaf, key, Number.MAX_SAFE_INTEGER);
|
|
351
379
|
while (leaf !== null) {
|
|
352
380
|
if (idx < leafEntryCount(leaf)) {
|
|
@@ -449,55 +477,57 @@ var countRangeEntries = (state, startKey, endKey, options) => {
|
|
|
449
477
|
cursorIndex = 0;
|
|
450
478
|
continue;
|
|
451
479
|
}
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
480
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
481
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
455
482
|
count += leafCount - cursorIndex;
|
|
456
483
|
cursorLeaf = cursorLeaf.next;
|
|
457
484
|
cursorIndex = 0;
|
|
458
485
|
continue;
|
|
459
486
|
}
|
|
460
|
-
|
|
461
|
-
const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
|
|
462
|
-
const limit = endBound < leafCount ? endBound : leafCount;
|
|
463
|
-
count += limit - cursorIndex;
|
|
487
|
+
count += findBoundaryEnd(state, cursorLeaf, endKey, upperExclusive, leafCount) - cursorIndex;
|
|
464
488
|
return count;
|
|
465
489
|
}
|
|
466
490
|
return count;
|
|
467
491
|
};
|
|
492
|
+
var isLastEntryInRange = (lastKey, endKey, compare, upperExclusive) => {
|
|
493
|
+
const cmp = compare(lastKey, endKey);
|
|
494
|
+
return upperExclusive ? cmp < 0 : cmp <= 0;
|
|
495
|
+
};
|
|
496
|
+
var findBoundaryEnd = (state, leaf, endKey, upperExclusive, leafCount) => {
|
|
497
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
498
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
|
|
499
|
+
return endBound < leafCount ? endBound : leafCount;
|
|
500
|
+
};
|
|
468
501
|
var RANGE_PREALLOC_THRESHOLD = 200;
|
|
469
|
-
var allocateRangeOutput = (state,
|
|
470
|
-
const firstLeafCount = leafEntryCount(
|
|
471
|
-
const firstLeafRemainder = firstLeafCount -
|
|
472
|
-
if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD &&
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
502
|
+
var allocateRangeOutput = (state, cursor, startKey, endKey, options) => {
|
|
503
|
+
const firstLeafCount = leafEntryCount(cursor.leaf);
|
|
504
|
+
const firstLeafRemainder = firstLeafCount - cursor.index;
|
|
505
|
+
if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursor.leaf.next !== null) {
|
|
506
|
+
const lastKey = leafEntryAt(cursor.leaf, firstLeafCount - 1).key;
|
|
507
|
+
if (isLastEntryInRange(lastKey, endKey, cursor.compare, cursor.upperExclusive)) {
|
|
508
|
+
return new Array(
|
|
509
|
+
countRangeEntries(state, startKey, endKey, options)
|
|
510
|
+
);
|
|
478
511
|
}
|
|
479
512
|
}
|
|
480
513
|
return [];
|
|
481
514
|
};
|
|
482
|
-
var
|
|
515
|
+
var appendLeafSlicePublic = (leaf, from, to, output, useIndexed, writeIdx) => {
|
|
483
516
|
if (useIndexed) {
|
|
484
517
|
for (let i = from; i < to; i += 1) {
|
|
485
|
-
output[writeIdx++] = leafEntryAt(leaf, i);
|
|
518
|
+
output[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
|
|
486
519
|
}
|
|
487
520
|
} else {
|
|
488
521
|
for (let i = from; i < to; i += 1) {
|
|
489
|
-
output.push(leafEntryAt(leaf, i));
|
|
522
|
+
output.push(freezeEntry(leafEntryAt(leaf, i)));
|
|
490
523
|
}
|
|
491
524
|
}
|
|
492
525
|
return writeIdx;
|
|
493
526
|
};
|
|
494
|
-
var
|
|
495
|
-
const
|
|
496
|
-
if (cursor === null) return [];
|
|
527
|
+
var collectPublicEntries = (state, cursor, endKey, output) => {
|
|
528
|
+
const { compare, upperExclusive } = cursor;
|
|
497
529
|
let cursorLeaf = cursor.leaf;
|
|
498
530
|
let cursorIndex = cursor.index;
|
|
499
|
-
const { compare, upperExclusive } = cursor;
|
|
500
|
-
const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
|
|
501
531
|
let writeIdx = 0;
|
|
502
532
|
const useIndexed = output.length > 0;
|
|
503
533
|
while (cursorLeaf !== null) {
|
|
@@ -507,24 +537,82 @@ var rangeQueryEntries = (state, startKey, endKey, options) => {
|
|
|
507
537
|
cursorIndex = 0;
|
|
508
538
|
continue;
|
|
509
539
|
}
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
540
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
541
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
542
|
+
writeIdx = appendLeafSlicePublic(
|
|
543
|
+
cursorLeaf,
|
|
544
|
+
cursorIndex,
|
|
545
|
+
leafCount,
|
|
546
|
+
output,
|
|
547
|
+
useIndexed,
|
|
548
|
+
writeIdx
|
|
549
|
+
);
|
|
514
550
|
cursorLeaf = cursorLeaf.next;
|
|
515
551
|
cursorIndex = 0;
|
|
516
552
|
continue;
|
|
517
553
|
}
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
554
|
+
const limit = findBoundaryEnd(
|
|
555
|
+
state,
|
|
556
|
+
cursorLeaf,
|
|
557
|
+
endKey,
|
|
558
|
+
upperExclusive,
|
|
559
|
+
leafCount
|
|
560
|
+
);
|
|
561
|
+
appendLeafSlicePublic(
|
|
562
|
+
cursorLeaf,
|
|
563
|
+
cursorIndex,
|
|
564
|
+
limit,
|
|
565
|
+
output,
|
|
566
|
+
useIndexed,
|
|
567
|
+
writeIdx
|
|
568
|
+
);
|
|
569
|
+
return;
|
|
523
570
|
}
|
|
571
|
+
};
|
|
572
|
+
var rangeQueryPublicEntries = (state, startKey, endKey, options) => {
|
|
573
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
574
|
+
if (cursor === null) return [];
|
|
575
|
+
const output = allocateRangeOutput(state, cursor, startKey, endKey, options);
|
|
576
|
+
collectPublicEntries(state, cursor, endKey, output);
|
|
524
577
|
return output;
|
|
525
578
|
};
|
|
579
|
+
var forEachRangeEntries = (state, startKey, endKey, callback, options) => {
|
|
580
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
581
|
+
if (cursor === null) return;
|
|
582
|
+
let cursorLeaf = cursor.leaf;
|
|
583
|
+
let cursorIndex = cursor.index;
|
|
584
|
+
const { compare, upperExclusive } = cursor;
|
|
585
|
+
while (cursorLeaf !== null) {
|
|
586
|
+
const leafCount = leafEntryCount(cursorLeaf);
|
|
587
|
+
if (cursorIndex >= leafCount) {
|
|
588
|
+
cursorLeaf = cursorLeaf.next;
|
|
589
|
+
cursorIndex = 0;
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
593
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
594
|
+
for (let i = cursorIndex; i < leafCount; i += 1) {
|
|
595
|
+
callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
|
|
596
|
+
}
|
|
597
|
+
cursorLeaf = cursorLeaf.next;
|
|
598
|
+
cursorIndex = 0;
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
const limit = findBoundaryEnd(
|
|
602
|
+
state,
|
|
603
|
+
cursorLeaf,
|
|
604
|
+
endKey,
|
|
605
|
+
upperExclusive,
|
|
606
|
+
leafCount
|
|
607
|
+
);
|
|
608
|
+
for (let i = cursorIndex; i < limit; i += 1) {
|
|
609
|
+
callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
|
|
610
|
+
}
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
};
|
|
526
614
|
|
|
527
|
-
// src/btree/rebalance.ts
|
|
615
|
+
// src/btree/rebalance-branch.ts
|
|
528
616
|
var updateMinKeyInAncestors = (node) => {
|
|
529
617
|
let current = node;
|
|
530
618
|
while (current.parent !== null) {
|
|
@@ -540,12 +628,9 @@ var requireParent = (node) => {
|
|
|
540
628
|
}
|
|
541
629
|
return node.parent;
|
|
542
630
|
};
|
|
543
|
-
var requireLeafNode = (node) => {
|
|
544
|
-
if (!isLeafNode(node)) throw new BTreeInvariantError("expected leaf, got branch");
|
|
545
|
-
return node;
|
|
546
|
-
};
|
|
547
631
|
var requireBranchNode = (node) => {
|
|
548
|
-
if (isLeafNode(node))
|
|
632
|
+
if (isLeafNode(node))
|
|
633
|
+
throw new BTreeInvariantError("expected branch, got leaf");
|
|
549
634
|
return node;
|
|
550
635
|
};
|
|
551
636
|
var removeChildFromBranch = (branch, childIndex) => {
|
|
@@ -554,27 +639,18 @@ var removeChildFromBranch = (branch, childIndex) => {
|
|
|
554
639
|
}
|
|
555
640
|
branchRemoveAt(branch, childIndex);
|
|
556
641
|
};
|
|
557
|
-
var detachLeafFromChain = (state, leaf) => {
|
|
558
|
-
if (leaf.prev !== null) {
|
|
559
|
-
leaf.prev.next = leaf.next;
|
|
560
|
-
} else if (leaf.next !== null) {
|
|
561
|
-
state.leftmostLeaf = leaf.next;
|
|
562
|
-
}
|
|
563
|
-
if (leaf.next !== null) {
|
|
564
|
-
leaf.next.prev = leaf.prev;
|
|
565
|
-
} else if (leaf.prev !== null) {
|
|
566
|
-
state.rightmostLeaf = leaf.prev;
|
|
567
|
-
}
|
|
568
|
-
leaf.prev = null;
|
|
569
|
-
leaf.next = null;
|
|
570
|
-
};
|
|
571
642
|
var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
|
|
572
643
|
const borrowedChild = leftSibling.children.pop();
|
|
573
|
-
if (borrowedChild === void 0)
|
|
644
|
+
if (borrowedChild === void 0)
|
|
645
|
+
throw new BTreeInvariantError("left branch borrow failed");
|
|
574
646
|
leftSibling.keys.pop();
|
|
575
647
|
borrowedChild.parent = branch;
|
|
576
|
-
const borrowedMinKey = {
|
|
577
|
-
|
|
648
|
+
const borrowedMinKey = {
|
|
649
|
+
key: void 0,
|
|
650
|
+
sequence: 0
|
|
651
|
+
};
|
|
652
|
+
if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
|
|
653
|
+
throw new BTreeInvariantError("borrowed child has no min key");
|
|
578
654
|
if (branch.childOffset > 0) {
|
|
579
655
|
branch.childOffset -= 1;
|
|
580
656
|
branch.children[branch.childOffset] = borrowedChild;
|
|
@@ -583,15 +659,20 @@ var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
|
|
|
583
659
|
} else {
|
|
584
660
|
branch.children.unshift(borrowedChild);
|
|
585
661
|
branch.keys.unshift(borrowedMinKey);
|
|
586
|
-
for (let i = 0; i < branch.children.length; i += 1)
|
|
662
|
+
for (let i = 0; i < branch.children.length; i += 1)
|
|
663
|
+
branch.children[i].indexInParent = i;
|
|
587
664
|
}
|
|
588
665
|
const parent = requireParent(branch);
|
|
589
|
-
parent.keys[branchIndex] = {
|
|
666
|
+
parent.keys[branchIndex] = {
|
|
667
|
+
key: borrowedMinKey.key,
|
|
668
|
+
sequence: borrowedMinKey.sequence
|
|
669
|
+
};
|
|
590
670
|
updateMinKeyInAncestors(branch);
|
|
591
671
|
};
|
|
592
672
|
var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
|
|
593
673
|
const shiftIdx = rightSibling.childOffset;
|
|
594
|
-
if (shiftIdx >= rightSibling.children.length)
|
|
674
|
+
if (shiftIdx >= rightSibling.children.length)
|
|
675
|
+
throw new BTreeInvariantError("right branch borrow failed");
|
|
595
676
|
const borrowedChild = rightSibling.children[shiftIdx];
|
|
596
677
|
rightSibling.childOffset += 1;
|
|
597
678
|
if (rightSibling.childOffset >= rightSibling.children.length >>> 1) {
|
|
@@ -599,8 +680,12 @@ var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
|
|
|
599
680
|
}
|
|
600
681
|
branch.children.push(borrowedChild);
|
|
601
682
|
borrowedChild.parent = branch;
|
|
602
|
-
const borrowedMinKey = {
|
|
603
|
-
|
|
683
|
+
const borrowedMinKey = {
|
|
684
|
+
key: void 0,
|
|
685
|
+
sequence: 0
|
|
686
|
+
};
|
|
687
|
+
if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
|
|
688
|
+
throw new BTreeInvariantError("borrowed child has no min key");
|
|
604
689
|
branch.keys.push(borrowedMinKey);
|
|
605
690
|
borrowedChild.indexInParent = branch.children.length - 1;
|
|
606
691
|
const parent = requireParent(branch);
|
|
@@ -653,7 +738,10 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
|
|
|
653
738
|
const parent = branch.parent;
|
|
654
739
|
if (parent === null) throw new BTreeInvariantError("branch has no parent");
|
|
655
740
|
const branchIndex = branch.indexInParent;
|
|
656
|
-
const { left: leftSibling, right: rightSibling } = findBranchSiblings(
|
|
741
|
+
const { left: leftSibling, right: rightSibling } = findBranchSiblings(
|
|
742
|
+
parent,
|
|
743
|
+
branchIndex
|
|
744
|
+
);
|
|
657
745
|
if (rightSibling !== null && branchChildCount(rightSibling) > state.minBranchChildren) {
|
|
658
746
|
borrowFromRightBranch(branch, rightSibling, branchIndex);
|
|
659
747
|
return;
|
|
@@ -672,44 +760,68 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
|
|
|
672
760
|
}
|
|
673
761
|
throw new BTreeInvariantError("no branch siblings to rebalance");
|
|
674
762
|
};
|
|
763
|
+
|
|
764
|
+
// src/btree/rebalance.ts
|
|
765
|
+
var requireLeafNode = (node) => {
|
|
766
|
+
if (!isLeafNode(node))
|
|
767
|
+
throw new BTreeInvariantError("expected leaf, got branch");
|
|
768
|
+
return node;
|
|
769
|
+
};
|
|
770
|
+
var detachLeafFromChain = (state, leaf) => {
|
|
771
|
+
if (leaf.prev !== null) {
|
|
772
|
+
leaf.prev.next = leaf.next;
|
|
773
|
+
} else if (leaf.next !== null) {
|
|
774
|
+
state.leftmostLeaf = leaf.next;
|
|
775
|
+
}
|
|
776
|
+
if (leaf.next !== null) {
|
|
777
|
+
leaf.next.prev = leaf.prev;
|
|
778
|
+
} else if (leaf.prev !== null) {
|
|
779
|
+
state.rightmostLeaf = leaf.prev;
|
|
780
|
+
}
|
|
781
|
+
leaf.prev = null;
|
|
782
|
+
leaf.next = null;
|
|
783
|
+
};
|
|
675
784
|
var mergeLeafEntries = (target, source) => {
|
|
676
785
|
if (target.entryOffset > 0) {
|
|
677
786
|
target.entries.copyWithin(0, target.entryOffset);
|
|
678
787
|
target.entries.length = target.entries.length - target.entryOffset;
|
|
679
788
|
target.entryOffset = 0;
|
|
680
789
|
}
|
|
681
|
-
|
|
682
|
-
for (let i = source.entryOffset; i < src.length; i += 1) target.entries.push(src[i]);
|
|
790
|
+
target.entries.push(...source.entries.slice(source.entryOffset));
|
|
683
791
|
};
|
|
684
|
-
var
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
state.rightmostLeaf = leaf;
|
|
689
|
-
}
|
|
690
|
-
return;
|
|
792
|
+
var applyLazyThreshold = (min) => Math.max(1, Math.ceil(min / 4));
|
|
793
|
+
var leafRebalanceThreshold = (state) => {
|
|
794
|
+
if (state.deleteRebalancePolicy === "lazy") {
|
|
795
|
+
return applyLazyThreshold(state.minLeafEntries);
|
|
691
796
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
const
|
|
696
|
-
const
|
|
697
|
-
|
|
797
|
+
return state.minLeafEntries;
|
|
798
|
+
};
|
|
799
|
+
var findLeafSiblings = (parent, leafIndex) => {
|
|
800
|
+
const left = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
|
|
801
|
+
const right = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
|
|
802
|
+
return { left, right };
|
|
803
|
+
};
|
|
804
|
+
var tryBorrowFromLeafSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
|
|
698
805
|
if (rightSibling !== null && leafEntryCount(rightSibling) > state.minLeafEntries) {
|
|
699
806
|
const borrowed = leafShiftEntry(rightSibling);
|
|
700
|
-
if (borrowed === void 0)
|
|
807
|
+
if (borrowed === void 0)
|
|
808
|
+
throw new BTreeInvariantError("right leaf borrow failed");
|
|
701
809
|
leaf.entries.push(borrowed);
|
|
702
810
|
writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
|
|
703
|
-
return;
|
|
811
|
+
return true;
|
|
704
812
|
}
|
|
705
813
|
if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
|
|
706
814
|
const borrowed = leftSibling.entries.pop();
|
|
707
|
-
if (borrowed === void 0)
|
|
815
|
+
if (borrowed === void 0)
|
|
816
|
+
throw new BTreeInvariantError("left leaf borrow failed");
|
|
708
817
|
leafUnshiftEntry(leaf, borrowed);
|
|
709
818
|
parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
|
|
710
819
|
updateMinKeyInAncestors(leaf);
|
|
711
|
-
return;
|
|
820
|
+
return true;
|
|
712
821
|
}
|
|
822
|
+
return false;
|
|
823
|
+
};
|
|
824
|
+
var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
|
|
713
825
|
if (leftSibling !== null) {
|
|
714
826
|
mergeLeafEntries(leftSibling, leaf);
|
|
715
827
|
detachLeafFromChain(state, leaf);
|
|
@@ -726,6 +838,24 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
|
726
838
|
}
|
|
727
839
|
throw new BTreeInvariantError("no leaf siblings to rebalance");
|
|
728
840
|
};
|
|
841
|
+
var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
842
|
+
if (leaf === state.root) {
|
|
843
|
+
if (state.entryCount === 0) {
|
|
844
|
+
state.leftmostLeaf = leaf;
|
|
845
|
+
state.rightmostLeaf = leaf;
|
|
846
|
+
}
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
|
|
850
|
+
const parent = leaf.parent;
|
|
851
|
+
if (parent === null)
|
|
852
|
+
throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
|
|
853
|
+
const leafIndex = leaf.indexInParent;
|
|
854
|
+
const { left, right } = findLeafSiblings(parent, leafIndex);
|
|
855
|
+
if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
|
|
856
|
+
return;
|
|
857
|
+
mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
|
|
858
|
+
};
|
|
729
859
|
|
|
730
860
|
// src/btree/deleteRange.ts
|
|
731
861
|
var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
@@ -738,16 +868,16 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
|
738
868
|
}
|
|
739
869
|
return { leaf, idx };
|
|
740
870
|
};
|
|
741
|
-
var findRemoveEnd = (state, leaf,
|
|
871
|
+
var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
|
|
742
872
|
const count = leafEntryCount(leaf);
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
|
|
748
|
-
removeEnd += 1;
|
|
873
|
+
const lastEntry = leafEntryAt(leaf, count - 1);
|
|
874
|
+
const cmpLast = state.compareKeys(lastEntry.key, endKey);
|
|
875
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
876
|
+
return count;
|
|
749
877
|
}
|
|
750
|
-
|
|
878
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
879
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
|
|
880
|
+
return endBound < count ? endBound : count;
|
|
751
881
|
};
|
|
752
882
|
var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
753
883
|
if (state.entryKeys !== null) {
|
|
@@ -764,10 +894,12 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
|
764
894
|
updateMinKeyInAncestors(leaf);
|
|
765
895
|
}
|
|
766
896
|
const countAfterSplice = leafEntryCount(leaf);
|
|
897
|
+
const rebalThreshold = leafRebalanceThreshold(state);
|
|
767
898
|
let safetyGuard = state.minLeafEntries + 4;
|
|
768
|
-
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) <
|
|
899
|
+
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
|
|
769
900
|
rebalanceAfterLeafRemoval(state, leaf);
|
|
770
|
-
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
901
|
+
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
902
|
+
break;
|
|
771
903
|
safetyGuard -= 1;
|
|
772
904
|
}
|
|
773
905
|
if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
|
|
@@ -795,10 +927,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
795
927
|
}
|
|
796
928
|
if (idx >= leafEntryCount(leaf)) break;
|
|
797
929
|
const count = leafEntryCount(leaf);
|
|
798
|
-
const removeEnd = findRemoveEnd(state, leaf,
|
|
930
|
+
const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
|
|
799
931
|
const removeCount = removeEnd - idx;
|
|
800
932
|
if (removeCount === 0) break;
|
|
801
|
-
const countAfterSplice = spliceLeafAndRebalance(
|
|
933
|
+
const countAfterSplice = spliceLeafAndRebalance(
|
|
934
|
+
state,
|
|
935
|
+
leaf,
|
|
936
|
+
idx,
|
|
937
|
+
removeCount
|
|
938
|
+
);
|
|
802
939
|
deleted += removeCount;
|
|
803
940
|
if (removeEnd < count) break;
|
|
804
941
|
if (!isLeafStillValid(state, leaf)) {
|
|
@@ -844,25 +981,42 @@ var computeNextAutoScaleThreshold = (entryCount) => {
|
|
|
844
981
|
}
|
|
845
982
|
return Number.MAX_SAFE_INTEGER;
|
|
846
983
|
};
|
|
847
|
-
var
|
|
848
|
-
if (typeof config.compareKeys !== "function") {
|
|
849
|
-
throw new BTreeValidationError("compareKeys must be a function.");
|
|
850
|
-
}
|
|
851
|
-
const autoScale = config.autoScale === true;
|
|
984
|
+
var resolveInitialCapacity = (config, autoScale) => {
|
|
852
985
|
if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
|
|
853
|
-
throw new BTreeValidationError(
|
|
986
|
+
throw new BTreeValidationError(
|
|
987
|
+
"autoScale conflicts with explicit capacity."
|
|
988
|
+
);
|
|
854
989
|
}
|
|
855
|
-
let maxLeafEntries;
|
|
856
|
-
let maxBranchChildren;
|
|
857
990
|
if (autoScale) {
|
|
858
991
|
const tier = computeAutoScaleTier(0);
|
|
859
|
-
maxLeafEntries
|
|
860
|
-
maxBranchChildren = tier.maxBranch;
|
|
861
|
-
} else {
|
|
862
|
-
maxLeafEntries = normalizeNodeCapacity(config.maxLeafEntries, "maxLeafEntries", DEFAULT_MAX_LEAF_ENTRIES);
|
|
863
|
-
maxBranchChildren = normalizeNodeCapacity(config.maxBranchChildren, "maxBranchChildren", DEFAULT_MAX_BRANCH_CHILDREN);
|
|
992
|
+
return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
|
|
864
993
|
}
|
|
994
|
+
return {
|
|
995
|
+
maxLeafEntries: normalizeNodeCapacity(
|
|
996
|
+
config.maxLeafEntries,
|
|
997
|
+
"maxLeafEntries",
|
|
998
|
+
DEFAULT_MAX_LEAF_ENTRIES
|
|
999
|
+
),
|
|
1000
|
+
maxBranchChildren: normalizeNodeCapacity(
|
|
1001
|
+
config.maxBranchChildren,
|
|
1002
|
+
"maxBranchChildren",
|
|
1003
|
+
DEFAULT_MAX_BRANCH_CHILDREN
|
|
1004
|
+
)
|
|
1005
|
+
};
|
|
1006
|
+
};
|
|
1007
|
+
var createInitialState = (config) => {
|
|
1008
|
+
if (typeof config.compareKeys !== "function") {
|
|
1009
|
+
throw new BTreeValidationError("compareKeys must be a function.");
|
|
1010
|
+
}
|
|
1011
|
+
const autoScale = config.autoScale === true;
|
|
1012
|
+
const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
|
|
1013
|
+
config,
|
|
1014
|
+
autoScale
|
|
1015
|
+
);
|
|
865
1016
|
const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
|
|
1017
|
+
const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
|
|
1018
|
+
config.deleteRebalancePolicy
|
|
1019
|
+
);
|
|
866
1020
|
const emptyLeaf = createLeafNode([], null);
|
|
867
1021
|
return {
|
|
868
1022
|
compareKeys: config.compareKeys,
|
|
@@ -878,6 +1032,7 @@ var createInitialState = (config) => {
|
|
|
878
1032
|
nextSequence: 0,
|
|
879
1033
|
entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
|
|
880
1034
|
autoScale,
|
|
1035
|
+
deleteRebalancePolicy,
|
|
881
1036
|
_nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
|
|
882
1037
|
_cursor: { leaf: emptyLeaf, index: 0 }
|
|
883
1038
|
};
|
|
@@ -893,7 +1048,9 @@ var maybeAutoScale = (state) => {
|
|
|
893
1048
|
state.maxBranchChildren = maxBranch;
|
|
894
1049
|
state.minBranchChildren = minOccupancy(maxBranch);
|
|
895
1050
|
}
|
|
896
|
-
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1051
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1052
|
+
state.entryCount
|
|
1053
|
+
);
|
|
897
1054
|
};
|
|
898
1055
|
var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
|
|
899
1056
|
if (!state.autoScale) {
|
|
@@ -920,6 +1077,14 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
|
|
|
920
1077
|
state.minLeafEntries = minOccupancy(normalizedLeaf);
|
|
921
1078
|
state.minBranchChildren = minOccupancy(normalizedBranch);
|
|
922
1079
|
};
|
|
1080
|
+
var resetAutoScaleToTier0 = (state) => {
|
|
1081
|
+
const tier = computeAutoScaleTier(0);
|
|
1082
|
+
state.maxLeafEntries = tier.maxLeaf;
|
|
1083
|
+
state.maxBranchChildren = tier.maxBranch;
|
|
1084
|
+
state.minLeafEntries = minOccupancy(tier.maxLeaf);
|
|
1085
|
+
state.minBranchChildren = minOccupancy(tier.maxBranch);
|
|
1086
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
1087
|
+
};
|
|
923
1088
|
|
|
924
1089
|
// src/btree/bulkLoad.ts
|
|
925
1090
|
var computeChunkBoundaries = (total, max, min) => {
|
|
@@ -940,7 +1105,11 @@ var computeChunkBoundaries = (total, max, min) => {
|
|
|
940
1105
|
return boundaries;
|
|
941
1106
|
};
|
|
942
1107
|
var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
943
|
-
const boundaries = computeChunkBoundaries(
|
|
1108
|
+
const boundaries = computeChunkBoundaries(
|
|
1109
|
+
entries.length,
|
|
1110
|
+
state.maxLeafEntries,
|
|
1111
|
+
state.minLeafEntries
|
|
1112
|
+
);
|
|
944
1113
|
const leaves = new Array(boundaries.length);
|
|
945
1114
|
let chunkStart = 0;
|
|
946
1115
|
for (let c = 0; c < boundaries.length; c += 1) {
|
|
@@ -948,7 +1117,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
|
948
1117
|
const chunk = new Array(chunkEnd - chunkStart);
|
|
949
1118
|
for (let i = chunkStart; i < chunkEnd; i += 1) {
|
|
950
1119
|
const seq = baseSequence + i;
|
|
951
|
-
chunk[i - chunkStart] =
|
|
1120
|
+
chunk[i - chunkStart] = createEntry(
|
|
1121
|
+
entries[i].key,
|
|
1122
|
+
seq,
|
|
1123
|
+
entries[i].value
|
|
1124
|
+
);
|
|
952
1125
|
ids[i] = seq;
|
|
953
1126
|
if (state.entryKeys !== null) {
|
|
954
1127
|
state.entryKeys.set(seq, entries[i].key);
|
|
@@ -982,11 +1155,18 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
982
1155
|
} else {
|
|
983
1156
|
let currentLevel = leaves;
|
|
984
1157
|
while (currentLevel.length > 1) {
|
|
985
|
-
const bounds = computeChunkBoundaries(
|
|
1158
|
+
const bounds = computeChunkBoundaries(
|
|
1159
|
+
currentLevel.length,
|
|
1160
|
+
state.maxBranchChildren,
|
|
1161
|
+
state.minBranchChildren
|
|
1162
|
+
);
|
|
986
1163
|
const nextLevel = new Array(bounds.length);
|
|
987
1164
|
let start = 0;
|
|
988
1165
|
for (let b = 0; b < bounds.length; b += 1) {
|
|
989
|
-
nextLevel[b] = createBranchNode(
|
|
1166
|
+
nextLevel[b] = createBranchNode(
|
|
1167
|
+
currentLevel.slice(start, bounds[b]),
|
|
1168
|
+
null
|
|
1169
|
+
);
|
|
990
1170
|
start = bounds[b];
|
|
991
1171
|
}
|
|
992
1172
|
currentLevel = nextLevel;
|
|
@@ -997,9 +1177,12 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
997
1177
|
return ids;
|
|
998
1178
|
};
|
|
999
1179
|
|
|
1000
|
-
// src/btree/
|
|
1180
|
+
// src/btree/split.ts
|
|
1001
1181
|
var insertChildAfter = (state, parent, existingChild, childToInsert) => {
|
|
1002
|
-
const newChildMinKey = {
|
|
1182
|
+
const newChildMinKey = {
|
|
1183
|
+
key: void 0,
|
|
1184
|
+
sequence: 0
|
|
1185
|
+
};
|
|
1003
1186
|
if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
|
|
1004
1187
|
throw new BTreeInvariantError("inserted child has no min key");
|
|
1005
1188
|
}
|
|
@@ -1037,7 +1220,10 @@ var splitLeaf = (state, leaf) => {
|
|
|
1037
1220
|
var splitBranch = (state, branch) => {
|
|
1038
1221
|
branchCompact(branch);
|
|
1039
1222
|
const splitAt = Math.ceil(branch.children.length / 2);
|
|
1040
|
-
const sibling = createBranchNode(
|
|
1223
|
+
const sibling = createBranchNode(
|
|
1224
|
+
branch.children.splice(splitAt),
|
|
1225
|
+
branch.parent
|
|
1226
|
+
);
|
|
1041
1227
|
branch.keys.splice(splitAt);
|
|
1042
1228
|
if (branch.parent === null) {
|
|
1043
1229
|
state.root = createBranchNode([branch, sibling], null);
|
|
@@ -1045,42 +1231,125 @@ var splitBranch = (state, branch) => {
|
|
|
1045
1231
|
}
|
|
1046
1232
|
insertChildAfter(state, branch.parent, branch, sibling);
|
|
1047
1233
|
};
|
|
1234
|
+
|
|
1235
|
+
// src/btree/entry-lookup.ts
|
|
1236
|
+
var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
1237
|
+
const targetLeaf = findLeafForKey(state, userKey, sequence);
|
|
1238
|
+
const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
|
|
1239
|
+
if (index >= leafEntryCount(targetLeaf)) return null;
|
|
1240
|
+
const entry = leafEntryAt(targetLeaf, index);
|
|
1241
|
+
if (entry.entryId !== sequence) return null;
|
|
1242
|
+
return { leaf: targetLeaf, index };
|
|
1243
|
+
};
|
|
1244
|
+
var peekEntryById = (state, entryId) => {
|
|
1245
|
+
if (state.entryKeys === null) {
|
|
1246
|
+
throw new BTreeInvariantError(
|
|
1247
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1248
|
+
);
|
|
1249
|
+
}
|
|
1250
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1251
|
+
if (userKey === void 0) return null;
|
|
1252
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1253
|
+
if (found === null) return null;
|
|
1254
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1255
|
+
return entry;
|
|
1256
|
+
};
|
|
1257
|
+
var updateEntryById = (state, entryId, newValue) => {
|
|
1258
|
+
if (state.entryKeys === null) {
|
|
1259
|
+
throw new BTreeInvariantError(
|
|
1260
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1264
|
+
if (userKey === void 0) return null;
|
|
1265
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1266
|
+
if (found === null) return null;
|
|
1267
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1268
|
+
const updated = createEntry(entry.key, entry.entryId, newValue);
|
|
1269
|
+
found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
|
|
1270
|
+
return updated;
|
|
1271
|
+
};
|
|
1272
|
+
var removeEntryById = (state, entryId) => {
|
|
1273
|
+
if (state.entryKeys === null) {
|
|
1274
|
+
throw new BTreeInvariantError(
|
|
1275
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1276
|
+
);
|
|
1277
|
+
}
|
|
1278
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1279
|
+
if (userKey === void 0) return null;
|
|
1280
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1281
|
+
if (found === null) return null;
|
|
1282
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1283
|
+
leafRemoveAt(found.leaf, found.index);
|
|
1284
|
+
state.entryCount -= 1;
|
|
1285
|
+
state.entryKeys.delete(entryId);
|
|
1286
|
+
if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
|
|
1287
|
+
updateMinKeyInAncestors(found.leaf);
|
|
1288
|
+
}
|
|
1289
|
+
if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
|
|
1290
|
+
rebalanceAfterLeafRemoval(state, found.leaf);
|
|
1291
|
+
}
|
|
1292
|
+
return entry;
|
|
1293
|
+
};
|
|
1294
|
+
|
|
1295
|
+
// src/btree/mutations.ts
|
|
1296
|
+
var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
|
|
1297
|
+
if (state.duplicateKeys === "allow") return null;
|
|
1298
|
+
if (insertAt > 0) {
|
|
1299
|
+
const candidate = leafEntryAt(targetLeaf, insertAt - 1);
|
|
1300
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1301
|
+
return {
|
|
1302
|
+
leaf: targetLeaf,
|
|
1303
|
+
physIndex: targetLeaf.entryOffset + insertAt - 1,
|
|
1304
|
+
entry: candidate
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
} else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
|
|
1308
|
+
const prevLeaf = targetLeaf.prev;
|
|
1309
|
+
const prevCount = leafEntryCount(prevLeaf);
|
|
1310
|
+
const candidate = leafEntryAt(prevLeaf, prevCount - 1);
|
|
1311
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1312
|
+
return {
|
|
1313
|
+
leaf: prevLeaf,
|
|
1314
|
+
physIndex: prevLeaf.entryOffset + prevCount - 1,
|
|
1315
|
+
entry: candidate
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
return null;
|
|
1320
|
+
};
|
|
1048
1321
|
var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
|
|
1049
1322
|
const sequence = state.nextSequence;
|
|
1050
1323
|
const insertAt = upperBoundInLeaf(state, targetLeaf, key, sequence);
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
if (
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
existingEntry = candidate;
|
|
1063
|
-
}
|
|
1064
|
-
}
|
|
1065
|
-
if (existingEntry !== null) {
|
|
1066
|
-
if (state.duplicateKeys === "reject") {
|
|
1067
|
-
throw new BTreeValidationError("Duplicate key rejected.");
|
|
1068
|
-
}
|
|
1069
|
-
existingEntry.value = value;
|
|
1070
|
-
return existingEntry.entryId;
|
|
1071
|
-
}
|
|
1324
|
+
const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
|
|
1325
|
+
if (dup !== null) {
|
|
1326
|
+
if (state.duplicateKeys === "reject") {
|
|
1327
|
+
throw new BTreeValidationError("Duplicate key rejected.");
|
|
1328
|
+
}
|
|
1329
|
+
dup.leaf.entries[dup.physIndex] = createEntry(
|
|
1330
|
+
dup.entry.key,
|
|
1331
|
+
dup.entry.entryId,
|
|
1332
|
+
value
|
|
1333
|
+
);
|
|
1334
|
+
return dup.entry.entryId;
|
|
1072
1335
|
}
|
|
1073
1336
|
if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
|
|
1074
1337
|
throw new BTreeValidationError("Sequence overflow.");
|
|
1075
1338
|
}
|
|
1076
1339
|
state.nextSequence += 1;
|
|
1077
|
-
leafInsertAt(
|
|
1340
|
+
leafInsertAt(
|
|
1341
|
+
targetLeaf,
|
|
1342
|
+
insertAt,
|
|
1343
|
+
createEntry(key, sequence, value)
|
|
1344
|
+
);
|
|
1078
1345
|
state.entryCount += 1;
|
|
1079
1346
|
if (state.entryKeys !== null) {
|
|
1080
1347
|
state.entryKeys.set(sequence, key);
|
|
1081
1348
|
}
|
|
1082
|
-
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1083
|
-
|
|
1349
|
+
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1350
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1351
|
+
if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
|
|
1352
|
+
splitLeaf(state, targetLeaf);
|
|
1084
1353
|
maybeAutoScale(state);
|
|
1085
1354
|
return sequence;
|
|
1086
1355
|
};
|
|
@@ -1091,7 +1360,8 @@ var putEntry = (state, key, value) => {
|
|
|
1091
1360
|
var popFirstEntry = (state) => {
|
|
1092
1361
|
if (state.entryCount === 0) return null;
|
|
1093
1362
|
const firstEntry = leafShiftEntry(state.leftmostLeaf);
|
|
1094
|
-
if (firstEntry === void 0)
|
|
1363
|
+
if (firstEntry === void 0)
|
|
1364
|
+
throw new BTreeInvariantError("leftmost leaf empty but count > 0");
|
|
1095
1365
|
state.entryCount -= 1;
|
|
1096
1366
|
if (state.entryKeys !== null) {
|
|
1097
1367
|
state.entryKeys.delete(firstEntry.entryId);
|
|
@@ -1107,7 +1377,8 @@ var popFirstEntry = (state) => {
|
|
|
1107
1377
|
var popLastEntry = (state) => {
|
|
1108
1378
|
if (state.entryCount === 0) return null;
|
|
1109
1379
|
const lastEntry = leafPopEntry(state.rightmostLeaf);
|
|
1110
|
-
if (lastEntry === void 0)
|
|
1380
|
+
if (lastEntry === void 0)
|
|
1381
|
+
throw new BTreeInvariantError("rightmost leaf empty but count > 0");
|
|
1111
1382
|
state.entryCount -= 1;
|
|
1112
1383
|
if (state.entryKeys !== null) {
|
|
1113
1384
|
state.entryKeys.delete(lastEntry.entryId);
|
|
@@ -1128,70 +1399,22 @@ var removeFirstMatchingEntry = (state, key) => {
|
|
|
1128
1399
|
if (state.entryKeys !== null) {
|
|
1129
1400
|
state.entryKeys.delete(targetEntry.entryId);
|
|
1130
1401
|
}
|
|
1131
|
-
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1402
|
+
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1403
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1132
1404
|
if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
|
|
1133
1405
|
rebalanceAfterLeafRemoval(state, targetLeaf);
|
|
1134
1406
|
}
|
|
1135
1407
|
return targetEntry;
|
|
1136
1408
|
};
|
|
1137
|
-
var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
1138
|
-
const targetLeaf = findLeafForKey(state, userKey, sequence);
|
|
1139
|
-
const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
|
|
1140
|
-
if (index >= leafEntryCount(targetLeaf)) return null;
|
|
1141
|
-
const entry = leafEntryAt(targetLeaf, index);
|
|
1142
|
-
if (entry.entryId !== sequence) return null;
|
|
1143
|
-
return { leaf: targetLeaf, index };
|
|
1144
|
-
};
|
|
1145
|
-
var removeEntryById = (state, entryId) => {
|
|
1146
|
-
if (state.entryKeys === null) {
|
|
1147
|
-
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1148
|
-
}
|
|
1149
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1150
|
-
if (userKey === void 0) return null;
|
|
1151
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1152
|
-
if (found === null) return null;
|
|
1153
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1154
|
-
leafRemoveAt(found.leaf, found.index);
|
|
1155
|
-
state.entryCount -= 1;
|
|
1156
|
-
state.entryKeys.delete(entryId);
|
|
1157
|
-
if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
|
|
1158
|
-
updateMinKeyInAncestors(found.leaf);
|
|
1159
|
-
}
|
|
1160
|
-
if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
|
|
1161
|
-
rebalanceAfterLeafRemoval(state, found.leaf);
|
|
1162
|
-
}
|
|
1163
|
-
return entry;
|
|
1164
|
-
};
|
|
1165
|
-
var peekEntryById = (state, entryId) => {
|
|
1166
|
-
if (state.entryKeys === null) {
|
|
1167
|
-
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1168
|
-
}
|
|
1169
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1170
|
-
if (userKey === void 0) return null;
|
|
1171
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1172
|
-
if (found === null) return null;
|
|
1173
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1174
|
-
return entry;
|
|
1175
|
-
};
|
|
1176
|
-
var updateEntryById = (state, entryId, newValue) => {
|
|
1177
|
-
if (state.entryKeys === null) {
|
|
1178
|
-
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1179
|
-
}
|
|
1180
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1181
|
-
if (userKey === void 0) return null;
|
|
1182
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1183
|
-
if (found === null) return null;
|
|
1184
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1185
|
-
entry.value = newValue;
|
|
1186
|
-
return entry;
|
|
1187
|
-
};
|
|
1188
1409
|
var putManyEntries = (state, entries) => {
|
|
1189
1410
|
if (entries.length === 0) return [];
|
|
1190
1411
|
const strictlyAscending = state.duplicateKeys !== "allow";
|
|
1191
1412
|
for (let i = 1; i < entries.length; i += 1) {
|
|
1192
1413
|
const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
|
|
1193
1414
|
if (cmp > 0) {
|
|
1194
|
-
throw new BTreeValidationError(
|
|
1415
|
+
throw new BTreeValidationError(
|
|
1416
|
+
"putMany: entries not in ascending order."
|
|
1417
|
+
);
|
|
1195
1418
|
}
|
|
1196
1419
|
if (strictlyAscending && cmp === 0) {
|
|
1197
1420
|
throw new BTreeValidationError(
|
|
@@ -1204,7 +1427,12 @@ var putManyEntries = (state, entries) => {
|
|
|
1204
1427
|
let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
|
|
1205
1428
|
for (let i = 0; i < entries.length; i += 1) {
|
|
1206
1429
|
const entry = entries[i];
|
|
1207
|
-
const targetLeaf = findLeafFromHint(
|
|
1430
|
+
const targetLeaf = findLeafFromHint(
|
|
1431
|
+
state,
|
|
1432
|
+
hintLeaf,
|
|
1433
|
+
entry.key,
|
|
1434
|
+
state.nextSequence
|
|
1435
|
+
);
|
|
1208
1436
|
ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
|
|
1209
1437
|
hintLeaf = targetLeaf;
|
|
1210
1438
|
}
|
|
@@ -1219,7 +1447,8 @@ var buildConfigFromState = (state) => {
|
|
|
1219
1447
|
compareKeys: state.compareKeys,
|
|
1220
1448
|
duplicateKeys: state.duplicateKeys,
|
|
1221
1449
|
enableEntryIdLookup: state.entryKeys !== null,
|
|
1222
|
-
autoScale: state.autoScale
|
|
1450
|
+
autoScale: state.autoScale,
|
|
1451
|
+
deleteRebalancePolicy: state.deleteRebalancePolicy
|
|
1223
1452
|
};
|
|
1224
1453
|
if (!state.autoScale) {
|
|
1225
1454
|
config.maxLeafEntries = state.maxLeafEntries;
|
|
@@ -1239,17 +1468,17 @@ var serializeToJSON = (state) => {
|
|
|
1239
1468
|
}
|
|
1240
1469
|
leaf = leaf.next;
|
|
1241
1470
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
enableEntryIdLookup: state.entryKeys !== null,
|
|
1249
|
-
autoScale: state.autoScale
|
|
1250
|
-
},
|
|
1251
|
-
entries
|
|
1471
|
+
const config = {
|
|
1472
|
+
maxLeafEntries: state.maxLeafEntries,
|
|
1473
|
+
maxBranchChildren: state.maxBranchChildren,
|
|
1474
|
+
duplicateKeys: state.duplicateKeys,
|
|
1475
|
+
enableEntryIdLookup: state.entryKeys !== null,
|
|
1476
|
+
autoScale: state.autoScale
|
|
1252
1477
|
};
|
|
1478
|
+
if (state.deleteRebalancePolicy !== "standard") {
|
|
1479
|
+
config.deleteRebalancePolicy = state.deleteRebalancePolicy;
|
|
1480
|
+
}
|
|
1481
|
+
return { version: 1, config, entries };
|
|
1253
1482
|
};
|
|
1254
1483
|
var MAX_SERIALIZED_ENTRIES = 1e6;
|
|
1255
1484
|
var validateStructure = (json) => {
|
|
@@ -1312,13 +1541,28 @@ var validateBTreeJSON = (json) => {
|
|
|
1312
1541
|
validateStructure(json);
|
|
1313
1542
|
validateConfig(json.config);
|
|
1314
1543
|
};
|
|
1544
|
+
var validateBTreeJSONSortOrder = (json, compareKeys) => {
|
|
1545
|
+
const strict = json.config.duplicateKeys !== "allow";
|
|
1546
|
+
for (let i = 1; i < json.entries.length; i += 1) {
|
|
1547
|
+
const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
|
|
1548
|
+
if (cmp > 0) {
|
|
1549
|
+
throw new BTreeValidationError("fromJSON: entries not sorted.");
|
|
1550
|
+
}
|
|
1551
|
+
if (strict && cmp === 0) {
|
|
1552
|
+
throw new BTreeValidationError(
|
|
1553
|
+
'fromJSON: duplicate keys require duplicateKeys "allow".'
|
|
1554
|
+
);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
};
|
|
1315
1558
|
var buildConfigFromJSON = (json, compareKeys) => {
|
|
1316
1559
|
const cfg = json.config;
|
|
1317
1560
|
const config = {
|
|
1318
1561
|
compareKeys,
|
|
1319
1562
|
duplicateKeys: cfg.duplicateKeys,
|
|
1320
1563
|
enableEntryIdLookup: cfg.enableEntryIdLookup,
|
|
1321
|
-
autoScale: cfg.autoScale
|
|
1564
|
+
autoScale: cfg.autoScale,
|
|
1565
|
+
deleteRebalancePolicy: cfg.deleteRebalancePolicy
|
|
1322
1566
|
};
|
|
1323
1567
|
if (!cfg.autoScale) {
|
|
1324
1568
|
config.maxLeafEntries = cfg.maxLeafEntries;
|
|
@@ -1327,6 +1571,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
|
|
|
1327
1571
|
return config;
|
|
1328
1572
|
};
|
|
1329
1573
|
|
|
1574
|
+
// src/btree/traversal.ts
|
|
1575
|
+
var snapshotEntries = (state) => {
|
|
1576
|
+
const result = new Array(state.entryCount);
|
|
1577
|
+
let leaf = state.leftmostLeaf;
|
|
1578
|
+
let writeIdx = 0;
|
|
1579
|
+
while (leaf !== null) {
|
|
1580
|
+
const count = leafEntryCount(leaf);
|
|
1581
|
+
for (let i = 0; i < count; i += 1) {
|
|
1582
|
+
result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
|
|
1583
|
+
}
|
|
1584
|
+
leaf = leaf.next;
|
|
1585
|
+
}
|
|
1586
|
+
return result;
|
|
1587
|
+
};
|
|
1588
|
+
var collectInternalEntries = (state) => {
|
|
1589
|
+
const result = new Array(state.entryCount);
|
|
1590
|
+
let leaf = state.leftmostLeaf;
|
|
1591
|
+
let writeIdx = 0;
|
|
1592
|
+
while (leaf !== null) {
|
|
1593
|
+
const count = leafEntryCount(leaf);
|
|
1594
|
+
for (let i = 0; i < count; i += 1) {
|
|
1595
|
+
result[writeIdx++] = leafEntryAt(leaf, i);
|
|
1596
|
+
}
|
|
1597
|
+
leaf = leaf.next;
|
|
1598
|
+
}
|
|
1599
|
+
return result;
|
|
1600
|
+
};
|
|
1601
|
+
var forEachEntry = (state, callback, thisArg) => {
|
|
1602
|
+
let leaf = state.leftmostLeaf;
|
|
1603
|
+
while (leaf !== null) {
|
|
1604
|
+
const count = leafEntryCount(leaf);
|
|
1605
|
+
for (let i = 0; i < count; i += 1) {
|
|
1606
|
+
callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
|
|
1607
|
+
}
|
|
1608
|
+
leaf = leaf.next;
|
|
1609
|
+
}
|
|
1610
|
+
};
|
|
1611
|
+
|
|
1330
1612
|
// src/btree/integrity-helpers.ts
|
|
1331
1613
|
var nodeMinKey = (node) => {
|
|
1332
1614
|
if (isLeafNode(node)) {
|
|
@@ -1335,7 +1617,10 @@ var nodeMinKey = (node) => {
|
|
|
1335
1617
|
return { key: e.key, sequence: e.entryId };
|
|
1336
1618
|
}
|
|
1337
1619
|
if (node.childOffset >= node.keys.length) return null;
|
|
1338
|
-
return {
|
|
1620
|
+
return {
|
|
1621
|
+
key: node.keys[node.childOffset].key,
|
|
1622
|
+
sequence: node.keys[node.childOffset].sequence
|
|
1623
|
+
};
|
|
1339
1624
|
};
|
|
1340
1625
|
var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
|
|
1341
1626
|
const cmp = comparator(leftKey, rightKey);
|
|
@@ -1357,9 +1642,7 @@ var getNodeMaxKey = (node) => {
|
|
|
1357
1642
|
};
|
|
1358
1643
|
var validateComparatorResult = (result) => {
|
|
1359
1644
|
if (!Number.isFinite(result)) {
|
|
1360
|
-
throw new BTreeValidationError(
|
|
1361
|
-
"compareKeys must return a finite number."
|
|
1362
|
-
);
|
|
1645
|
+
throw new BTreeValidationError("compareKeys must return a finite number.");
|
|
1363
1646
|
}
|
|
1364
1647
|
return result;
|
|
1365
1648
|
};
|
|
@@ -1419,6 +1702,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
|
|
|
1419
1702
|
throw error;
|
|
1420
1703
|
}
|
|
1421
1704
|
};
|
|
1705
|
+
var validateAdjacentLeafOrdering = (state, previous, cursor) => {
|
|
1706
|
+
const prevMax = getNodeMaxKey(previous);
|
|
1707
|
+
const currentMin = nodeMinKey(cursor);
|
|
1708
|
+
if (prevMax === null || currentMin === null) {
|
|
1709
|
+
throw new BTreeInvariantError(
|
|
1710
|
+
"Non-empty tree leaf chain contains empty leaf node."
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1713
|
+
if (compareNodeKeys(
|
|
1714
|
+
state.compareKeys,
|
|
1715
|
+
prevMax.key,
|
|
1716
|
+
prevMax.sequence,
|
|
1717
|
+
currentMin.key,
|
|
1718
|
+
currentMin.sequence
|
|
1719
|
+
) > 0) {
|
|
1720
|
+
throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
|
|
1721
|
+
}
|
|
1722
|
+
const prevCount = leafEntryCount(previous);
|
|
1723
|
+
const curCount = leafEntryCount(cursor);
|
|
1724
|
+
if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
|
|
1725
|
+
leafEntryAt(previous, prevCount - 1).key,
|
|
1726
|
+
leafEntryAt(cursor, 0).key
|
|
1727
|
+
) === 0) {
|
|
1728
|
+
throw new BTreeInvariantError(
|
|
1729
|
+
"Duplicate user key detected across adjacent leaves with uniqueness policy."
|
|
1730
|
+
);
|
|
1731
|
+
}
|
|
1732
|
+
};
|
|
1422
1733
|
var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
1423
1734
|
if (!isLeafNode(cursor)) {
|
|
1424
1735
|
throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
|
|
@@ -1430,22 +1741,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
|
1430
1741
|
throw new BTreeInvariantError("Leaf prev pointer mismatch.");
|
|
1431
1742
|
}
|
|
1432
1743
|
if (previous !== null && isLeafNode(previous)) {
|
|
1433
|
-
|
|
1434
|
-
const currentMin = nodeMinKey(cursor);
|
|
1435
|
-
if (prevMax === null || currentMin === null) {
|
|
1436
|
-
throw new BTreeInvariantError("Non-empty tree leaf chain contains empty leaf node.");
|
|
1437
|
-
}
|
|
1438
|
-
if (compareNodeKeys(state.compareKeys, prevMax.key, prevMax.sequence, currentMin.key, currentMin.sequence) > 0) {
|
|
1439
|
-
throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
|
|
1440
|
-
}
|
|
1441
|
-
const prevCount = leafEntryCount(previous);
|
|
1442
|
-
const curCount = leafEntryCount(cursor);
|
|
1443
|
-
if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
|
|
1444
|
-
leafEntryAt(previous, prevCount - 1).key,
|
|
1445
|
-
leafEntryAt(cursor, 0).key
|
|
1446
|
-
) === 0) {
|
|
1447
|
-
throw new BTreeInvariantError("Duplicate user key detected across adjacent leaves with uniqueness policy.");
|
|
1448
|
-
}
|
|
1744
|
+
validateAdjacentLeafOrdering(state, previous, cursor);
|
|
1449
1745
|
}
|
|
1450
1746
|
};
|
|
1451
1747
|
var validateLeafLinks = (state, expectedLeafCount) => {
|
|
@@ -1454,7 +1750,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1454
1750
|
throw new BTreeInvariantError("Empty tree root must be a leaf node.");
|
|
1455
1751
|
}
|
|
1456
1752
|
if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
|
|
1457
|
-
throw new BTreeInvariantError(
|
|
1753
|
+
throw new BTreeInvariantError(
|
|
1754
|
+
"Empty tree leaf pointers must reference root leaf."
|
|
1755
|
+
);
|
|
1458
1756
|
}
|
|
1459
1757
|
return;
|
|
1460
1758
|
}
|
|
@@ -1479,7 +1777,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1479
1777
|
throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
|
|
1480
1778
|
}
|
|
1481
1779
|
if (leafCount !== expectedLeafCount) {
|
|
1482
|
-
throw new BTreeInvariantError(
|
|
1780
|
+
throw new BTreeInvariantError(
|
|
1781
|
+
"Leaf chain count mismatch with tree traversal count."
|
|
1782
|
+
);
|
|
1483
1783
|
}
|
|
1484
1784
|
};
|
|
1485
1785
|
|
|
@@ -1506,7 +1806,9 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1506
1806
|
leafEntryAt(node, index - 1).key,
|
|
1507
1807
|
leafEntryAt(node, index).key
|
|
1508
1808
|
) === 0) {
|
|
1509
|
-
throw new BTreeInvariantError(
|
|
1809
|
+
throw new BTreeInvariantError(
|
|
1810
|
+
"Duplicate user key detected in tree with uniqueness policy."
|
|
1811
|
+
);
|
|
1510
1812
|
}
|
|
1511
1813
|
}
|
|
1512
1814
|
}
|
|
@@ -1514,12 +1816,7 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1514
1816
|
const first = leafEntryAt(node, index - 2);
|
|
1515
1817
|
const second = leafEntryAt(node, index - 1);
|
|
1516
1818
|
const third = leafEntryAt(node, index);
|
|
1517
|
-
assertTransitivityAsInvariant(
|
|
1518
|
-
state,
|
|
1519
|
-
first.key,
|
|
1520
|
-
second.key,
|
|
1521
|
-
third.key
|
|
1522
|
-
);
|
|
1819
|
+
assertTransitivityAsInvariant(state, first.key, second.key, third.key);
|
|
1523
1820
|
}
|
|
1524
1821
|
if (count > state.maxLeafEntries) {
|
|
1525
1822
|
throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
|
|
@@ -1528,9 +1825,14 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1528
1825
|
var validateLeafNode = (state, node, depth) => {
|
|
1529
1826
|
validateLeafNodeOrdering(state, node);
|
|
1530
1827
|
const count = leafEntryCount(node);
|
|
1531
|
-
|
|
1828
|
+
let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
|
|
1829
|
+
if (state.deleteRebalancePolicy === "lazy") {
|
|
1830
|
+
baseMinLeaf = applyLazyThreshold(baseMinLeaf);
|
|
1831
|
+
}
|
|
1532
1832
|
if (node !== state.root && count < baseMinLeaf) {
|
|
1533
|
-
throw new BTreeInvariantError(
|
|
1833
|
+
throw new BTreeInvariantError(
|
|
1834
|
+
"Non-root leaf node violates minimum occupancy."
|
|
1835
|
+
);
|
|
1534
1836
|
}
|
|
1535
1837
|
const first = count === 0 ? null : leafEntryAt(node, 0);
|
|
1536
1838
|
const last = count === 0 ? null : leafEntryAt(node, count - 1);
|
|
@@ -1552,76 +1854,110 @@ var validateBranchStructure = (state, node) => {
|
|
|
1552
1854
|
}
|
|
1553
1855
|
const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
|
|
1554
1856
|
if (node !== state.root && liveCount < baseMinBranch) {
|
|
1555
|
-
throw new BTreeInvariantError(
|
|
1857
|
+
throw new BTreeInvariantError(
|
|
1858
|
+
"Non-root branch node violates minimum occupancy."
|
|
1859
|
+
);
|
|
1556
1860
|
}
|
|
1557
1861
|
if (liveCount > state.maxBranchChildren) {
|
|
1558
1862
|
throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
|
|
1559
1863
|
}
|
|
1560
1864
|
if (node.keys.length !== node.children.length) {
|
|
1561
|
-
throw new BTreeInvariantError(
|
|
1865
|
+
throw new BTreeInvariantError(
|
|
1866
|
+
"Branch keys array length does not match children array length."
|
|
1867
|
+
);
|
|
1562
1868
|
}
|
|
1563
1869
|
};
|
|
1564
1870
|
var validateBranchChild = (state, node, childIndex, depth) => {
|
|
1565
1871
|
const child = node.children[childIndex];
|
|
1566
1872
|
if (child.parent !== node) {
|
|
1567
|
-
throw new BTreeInvariantError(
|
|
1873
|
+
throw new BTreeInvariantError(
|
|
1874
|
+
"Child-parent pointer mismatch in branch node."
|
|
1875
|
+
);
|
|
1568
1876
|
}
|
|
1569
1877
|
if (child.indexInParent !== childIndex) {
|
|
1570
|
-
throw new BTreeInvariantError(
|
|
1878
|
+
throw new BTreeInvariantError(
|
|
1879
|
+
"Child indexInParent does not match actual position in parent."
|
|
1880
|
+
);
|
|
1571
1881
|
}
|
|
1572
1882
|
const childValidation = validateNode(state, child, depth + 1);
|
|
1573
1883
|
if (childValidation.minKey === null || childValidation.maxKey === null) {
|
|
1574
|
-
throw new BTreeInvariantError(
|
|
1884
|
+
throw new BTreeInvariantError(
|
|
1885
|
+
"Branch child must not be empty in non-root branch tree."
|
|
1886
|
+
);
|
|
1575
1887
|
}
|
|
1576
1888
|
const cachedMinKey = node.keys[childIndex];
|
|
1577
1889
|
const actualMinKey = nodeMinKey(child);
|
|
1578
|
-
if (actualMinKey === null || compareNodeKeys(
|
|
1579
|
-
|
|
1890
|
+
if (actualMinKey === null || compareNodeKeys(
|
|
1891
|
+
state.compareKeys,
|
|
1892
|
+
cachedMinKey.key,
|
|
1893
|
+
cachedMinKey.sequence,
|
|
1894
|
+
actualMinKey.key,
|
|
1895
|
+
actualMinKey.sequence
|
|
1896
|
+
) !== 0) {
|
|
1897
|
+
throw new BTreeInvariantError(
|
|
1898
|
+
"Branch cached key does not match actual child minimum key."
|
|
1899
|
+
);
|
|
1580
1900
|
}
|
|
1581
1901
|
return childValidation;
|
|
1582
1902
|
};
|
|
1903
|
+
var mergeChildValidation = (state, accumulated, childValidation) => {
|
|
1904
|
+
if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
|
|
1905
|
+
throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
|
|
1906
|
+
}
|
|
1907
|
+
if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
|
|
1908
|
+
accumulated.leafDepth = childValidation.leafDepth;
|
|
1909
|
+
}
|
|
1910
|
+
if (accumulated.previousChildMax !== null && compareNodeKeys(
|
|
1911
|
+
state.compareKeys,
|
|
1912
|
+
accumulated.previousChildMax.key,
|
|
1913
|
+
accumulated.previousChildMax.sequence,
|
|
1914
|
+
childValidation.minKey.key,
|
|
1915
|
+
childValidation.minKey.sequence
|
|
1916
|
+
) >= 0) {
|
|
1917
|
+
throw new BTreeInvariantError(
|
|
1918
|
+
"Branch child key ranges are not strictly ordered."
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1921
|
+
if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
|
|
1922
|
+
accumulated.maxKey = childValidation.maxKey;
|
|
1923
|
+
accumulated.previousChildMax = childValidation.maxKey;
|
|
1924
|
+
accumulated.leafCount += childValidation.leafCount;
|
|
1925
|
+
accumulated.branchCount += childValidation.branchCount;
|
|
1926
|
+
accumulated.entryCount += childValidation.entryCount;
|
|
1927
|
+
};
|
|
1583
1928
|
var validateNode = (state, node, depth) => {
|
|
1584
1929
|
if (isLeafNode(node)) {
|
|
1585
1930
|
return validateLeafNode(state, node, depth);
|
|
1586
1931
|
}
|
|
1587
1932
|
validateBranchStructure(state, node);
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1933
|
+
const acc = {
|
|
1934
|
+
leafDepth: null,
|
|
1935
|
+
leafCount: 0,
|
|
1936
|
+
branchCount: 1,
|
|
1937
|
+
entryCount: 0,
|
|
1938
|
+
minKey: null,
|
|
1939
|
+
maxKey: null,
|
|
1940
|
+
previousChildMax: null
|
|
1941
|
+
};
|
|
1595
1942
|
for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
|
|
1596
1943
|
const childValidation = validateBranchChild(state, node, childIndex, depth);
|
|
1597
|
-
|
|
1598
|
-
throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
|
|
1599
|
-
}
|
|
1600
|
-
if (leafDepth === null && childValidation.leafDepth !== null) {
|
|
1601
|
-
leafDepth = childValidation.leafDepth;
|
|
1602
|
-
}
|
|
1603
|
-
if (previousChildMax !== null && compareNodeKeys(
|
|
1604
|
-
state.compareKeys,
|
|
1605
|
-
previousChildMax.key,
|
|
1606
|
-
previousChildMax.sequence,
|
|
1607
|
-
childValidation.minKey.key,
|
|
1608
|
-
childValidation.minKey.sequence
|
|
1609
|
-
) >= 0) {
|
|
1610
|
-
throw new BTreeInvariantError("Branch child key ranges are not strictly ordered.");
|
|
1611
|
-
}
|
|
1612
|
-
if (minKey === null) minKey = childValidation.minKey;
|
|
1613
|
-
maxKey = childValidation.maxKey;
|
|
1614
|
-
previousChildMax = childValidation.maxKey;
|
|
1615
|
-
leafCount += childValidation.leafCount;
|
|
1616
|
-
branchCount += childValidation.branchCount;
|
|
1617
|
-
entryCount += childValidation.entryCount;
|
|
1944
|
+
mergeChildValidation(state, acc, childValidation);
|
|
1618
1945
|
}
|
|
1619
|
-
return {
|
|
1946
|
+
return {
|
|
1947
|
+
minKey: acc.minKey,
|
|
1948
|
+
maxKey: acc.maxKey,
|
|
1949
|
+
leafDepth: acc.leafDepth,
|
|
1950
|
+
leafCount: acc.leafCount,
|
|
1951
|
+
branchCount: acc.branchCount,
|
|
1952
|
+
entryCount: acc.entryCount
|
|
1953
|
+
};
|
|
1620
1954
|
};
|
|
1621
1955
|
var assertInvariants = (state) => {
|
|
1622
1956
|
const validation = validateNode(state, state.root, 0);
|
|
1623
1957
|
if (validation.entryCount !== state.entryCount) {
|
|
1624
|
-
throw new BTreeInvariantError(
|
|
1958
|
+
throw new BTreeInvariantError(
|
|
1959
|
+
"Index entry count mismatch between tree traversal and tracked state."
|
|
1960
|
+
);
|
|
1625
1961
|
}
|
|
1626
1962
|
validateLeafLinks(state, validation.leafCount);
|
|
1627
1963
|
};
|
|
@@ -1677,7 +2013,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1677
2013
|
remove(key) {
|
|
1678
2014
|
const entry = removeFirstMatchingEntry(this.state, key);
|
|
1679
2015
|
if (entry === null) return null;
|
|
1680
|
-
return
|
|
2016
|
+
return freezeEntry(entry);
|
|
1681
2017
|
}
|
|
1682
2018
|
removeById(entryId) {
|
|
1683
2019
|
if (this.state.entryKeys === null) {
|
|
@@ -1685,7 +2021,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1685
2021
|
}
|
|
1686
2022
|
const entry = removeEntryById(this.state, entryId);
|
|
1687
2023
|
if (entry === null) return null;
|
|
1688
|
-
return
|
|
2024
|
+
return freezeEntry(entry);
|
|
1689
2025
|
}
|
|
1690
2026
|
peekById(entryId) {
|
|
1691
2027
|
if (this.state.entryKeys === null) {
|
|
@@ -1693,7 +2029,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1693
2029
|
}
|
|
1694
2030
|
const entry = peekEntryById(this.state, entryId);
|
|
1695
2031
|
if (entry === null) return null;
|
|
1696
|
-
return
|
|
2032
|
+
return freezeEntry(entry);
|
|
1697
2033
|
}
|
|
1698
2034
|
updateById(entryId, value) {
|
|
1699
2035
|
if (this.state.entryKeys === null) {
|
|
@@ -1701,30 +2037,30 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1701
2037
|
}
|
|
1702
2038
|
const entry = updateEntryById(this.state, entryId, value);
|
|
1703
2039
|
if (entry === null) return null;
|
|
1704
|
-
return
|
|
2040
|
+
return freezeEntry(entry);
|
|
1705
2041
|
}
|
|
1706
2042
|
popFirst() {
|
|
1707
2043
|
const entry = popFirstEntry(this.state);
|
|
1708
2044
|
if (entry === null) return null;
|
|
1709
|
-
return
|
|
2045
|
+
return freezeEntry(entry);
|
|
1710
2046
|
}
|
|
1711
2047
|
peekFirst() {
|
|
1712
2048
|
if (this.state.entryCount === 0) {
|
|
1713
2049
|
return null;
|
|
1714
2050
|
}
|
|
1715
|
-
return
|
|
2051
|
+
return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
|
|
1716
2052
|
}
|
|
1717
2053
|
peekLast() {
|
|
1718
2054
|
if (this.state.entryCount === 0) {
|
|
1719
2055
|
return null;
|
|
1720
2056
|
}
|
|
1721
2057
|
const leaf = this.state.rightmostLeaf;
|
|
1722
|
-
return
|
|
2058
|
+
return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
|
|
1723
2059
|
}
|
|
1724
2060
|
popLast() {
|
|
1725
2061
|
const entry = popLastEntry(this.state);
|
|
1726
2062
|
if (entry === null) return null;
|
|
1727
|
-
return
|
|
2063
|
+
return freezeEntry(entry);
|
|
1728
2064
|
}
|
|
1729
2065
|
clear() {
|
|
1730
2066
|
const emptyLeaf = createLeafNode([], null);
|
|
@@ -1734,16 +2070,9 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1734
2070
|
this.state.entryCount = 0;
|
|
1735
2071
|
this.state._cursor.leaf = emptyLeaf;
|
|
1736
2072
|
this.state._cursor.index = 0;
|
|
1737
|
-
|
|
1738
|
-
this.state.entryKeys.clear();
|
|
1739
|
-
}
|
|
2073
|
+
this.state.entryKeys?.clear();
|
|
1740
2074
|
if (this.state.autoScale) {
|
|
1741
|
-
|
|
1742
|
-
this.state.maxLeafEntries = tier.maxLeaf;
|
|
1743
|
-
this.state.maxBranchChildren = tier.maxBranch;
|
|
1744
|
-
this.state.minLeafEntries = minOccupancy(tier.maxLeaf);
|
|
1745
|
-
this.state.minBranchChildren = minOccupancy(tier.maxBranch);
|
|
1746
|
-
this.state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
2075
|
+
resetAutoScaleToTier0(this.state);
|
|
1747
2076
|
}
|
|
1748
2077
|
}
|
|
1749
2078
|
get(key) {
|
|
@@ -1757,65 +2086,39 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1757
2086
|
findFirst(key) {
|
|
1758
2087
|
const found = findFirstMatchingUserKey(this.state, key);
|
|
1759
2088
|
if (found === null) return null;
|
|
1760
|
-
return
|
|
2089
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1761
2090
|
}
|
|
1762
|
-
/**
|
|
1763
|
-
* Returns the last entry whose key matches `key`, or `null` if not found.
|
|
1764
|
-
* Useful when `duplicateKeys` is `'allow'` and multiple entries share the same key.
|
|
1765
|
-
*/
|
|
1766
2091
|
findLast(key) {
|
|
1767
2092
|
const found = findLastMatchingUserKey(this.state, key);
|
|
1768
2093
|
if (found === null) return null;
|
|
1769
|
-
return
|
|
2094
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1770
2095
|
}
|
|
1771
|
-
/**
|
|
1772
|
-
* Returns the smallest key in the tree that is strictly greater than `key`,
|
|
1773
|
-
* or `null` if no such key exists.
|
|
1774
|
-
*/
|
|
1775
2096
|
nextHigherKey(key) {
|
|
1776
2097
|
return findNextHigherKey(this.state, key);
|
|
1777
2098
|
}
|
|
1778
|
-
/**
|
|
1779
|
-
* Returns the largest key in the tree that is strictly less than `key`,
|
|
1780
|
-
* or `null` if no such key exists.
|
|
1781
|
-
*/
|
|
1782
2099
|
nextLowerKey(key) {
|
|
1783
2100
|
return findNextLowerKey(this.state, key);
|
|
1784
2101
|
}
|
|
1785
|
-
/**
|
|
1786
|
-
* Returns the entry for `key` if it exists; otherwise returns the entry with
|
|
1787
|
-
* the largest key strictly less than `key`. Returns `null` when the tree is
|
|
1788
|
-
* empty or every key is greater than `key`.
|
|
1789
|
-
*/
|
|
1790
2102
|
getPairOrNextLower(key) {
|
|
1791
2103
|
const found = findPairOrNextLower(this.state, key);
|
|
1792
2104
|
if (found === null) return null;
|
|
1793
|
-
return
|
|
2105
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1794
2106
|
}
|
|
1795
|
-
/**
|
|
1796
|
-
* Returns the number of entries whose keys fall within [`startKey`, `endKey`].
|
|
1797
|
-
* Pass `options` to make either bound exclusive.
|
|
1798
|
-
*/
|
|
1799
2107
|
count(startKey, endKey, options) {
|
|
1800
2108
|
return countRangeEntries(this.state, startKey, endKey, options);
|
|
1801
2109
|
}
|
|
1802
|
-
/**
|
|
1803
|
-
* Deletes all entries whose keys fall within [`startKey`, `endKey`].
|
|
1804
|
-
* Pass `options` to make either bound exclusive.
|
|
1805
|
-
* @returns The number of entries deleted.
|
|
1806
|
-
*/
|
|
1807
2110
|
deleteRange(startKey, endKey, options) {
|
|
1808
2111
|
return deleteRangeEntries(this.state, startKey, endKey, options);
|
|
1809
2112
|
}
|
|
1810
2113
|
range(startKey, endKey, options) {
|
|
1811
|
-
return
|
|
2114
|
+
return rangeQueryPublicEntries(this.state, startKey, endKey, options);
|
|
1812
2115
|
}
|
|
1813
2116
|
*entries() {
|
|
1814
2117
|
let leaf = this.state.leftmostLeaf;
|
|
1815
2118
|
while (leaf !== null) {
|
|
1816
2119
|
const count = leafEntryCount(leaf);
|
|
1817
2120
|
for (let i = 0; i < count; i += 1) {
|
|
1818
|
-
yield
|
|
2121
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1819
2122
|
}
|
|
1820
2123
|
leaf = leaf.next;
|
|
1821
2124
|
}
|
|
@@ -1825,7 +2128,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1825
2128
|
while (leaf !== null) {
|
|
1826
2129
|
const count = leafEntryCount(leaf);
|
|
1827
2130
|
for (let i = count - 1; i >= 0; i -= 1) {
|
|
1828
|
-
yield
|
|
2131
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1829
2132
|
}
|
|
1830
2133
|
leaf = leaf.prev;
|
|
1831
2134
|
}
|
|
@@ -1843,55 +2146,26 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1843
2146
|
[Symbol.iterator]() {
|
|
1844
2147
|
return this.entries();
|
|
1845
2148
|
}
|
|
2149
|
+
forEachRange(startKey, endKey, callback, options) {
|
|
2150
|
+
forEachRangeEntries(this.state, startKey, endKey, callback, options);
|
|
2151
|
+
}
|
|
1846
2152
|
forEach(callback, thisArg) {
|
|
1847
|
-
|
|
1848
|
-
while (leaf !== null) {
|
|
1849
|
-
const count = leafEntryCount(leaf);
|
|
1850
|
-
for (let i = 0; i < count; i += 1) {
|
|
1851
|
-
callback.call(thisArg, toPublicEntry(leafEntryAt(leaf, i)));
|
|
1852
|
-
}
|
|
1853
|
-
leaf = leaf.next;
|
|
1854
|
-
}
|
|
2153
|
+
forEachEntry(this.state, callback, thisArg);
|
|
1855
2154
|
}
|
|
1856
2155
|
snapshot() {
|
|
1857
|
-
|
|
1858
|
-
let leaf = this.state.leftmostLeaf;
|
|
1859
|
-
let writeIdx = 0;
|
|
1860
|
-
while (leaf !== null) {
|
|
1861
|
-
const count = leafEntryCount(leaf);
|
|
1862
|
-
for (let i = 0; i < count; i += 1) {
|
|
1863
|
-
result[writeIdx++] = toPublicEntry(leafEntryAt(leaf, i));
|
|
1864
|
-
}
|
|
1865
|
-
leaf = leaf.next;
|
|
1866
|
-
}
|
|
1867
|
-
return result;
|
|
2156
|
+
return snapshotEntries(this.state);
|
|
1868
2157
|
}
|
|
1869
|
-
/**
|
|
1870
|
-
* Returns a structurally independent `InMemoryBTree` with identical
|
|
1871
|
-
* configuration and entries. The tree structure (nodes, links, entry IDs)
|
|
1872
|
-
* is fully independent, but stored key and value references are shared
|
|
1873
|
-
* with the source tree.
|
|
1874
|
-
* Note: `EntryId` values are reassigned in the clone — IDs from the source tree are not valid for the clone.
|
|
1875
|
-
*/
|
|
1876
2158
|
clone() {
|
|
1877
|
-
const cloned = new _InMemoryBTree(
|
|
2159
|
+
const cloned = new _InMemoryBTree(
|
|
2160
|
+
buildConfigFromState(this.state)
|
|
2161
|
+
);
|
|
1878
2162
|
applyAutoScaleCapacitySnapshot(
|
|
1879
2163
|
cloned.state,
|
|
1880
2164
|
this.state.maxLeafEntries,
|
|
1881
2165
|
this.state.maxBranchChildren
|
|
1882
2166
|
);
|
|
1883
2167
|
if (this.state.entryCount > 0) {
|
|
1884
|
-
|
|
1885
|
-
let leaf = this.state.leftmostLeaf;
|
|
1886
|
-
let writeIdx = 0;
|
|
1887
|
-
while (leaf !== null) {
|
|
1888
|
-
const count = leafEntryCount(leaf);
|
|
1889
|
-
for (let i = 0; i < count; i += 1) {
|
|
1890
|
-
pairs[writeIdx++] = leafEntryAt(leaf, i);
|
|
1891
|
-
}
|
|
1892
|
-
leaf = leaf.next;
|
|
1893
|
-
}
|
|
1894
|
-
cloned.putMany(pairs);
|
|
2168
|
+
cloned.putMany(collectInternalEntries(this.state));
|
|
1895
2169
|
}
|
|
1896
2170
|
return cloned;
|
|
1897
2171
|
}
|
|
@@ -1900,26 +2174,19 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1900
2174
|
}
|
|
1901
2175
|
static fromJSON(json, compareKeys) {
|
|
1902
2176
|
validateBTreeJSON(json);
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
throw new BTreeValidationError("fromJSON: entries not sorted.");
|
|
1908
|
-
}
|
|
1909
|
-
if (strict && cmp === 0) {
|
|
1910
|
-
throw new BTreeValidationError(
|
|
1911
|
-
'fromJSON: duplicate keys require duplicateKeys "allow".'
|
|
1912
|
-
);
|
|
1913
|
-
}
|
|
1914
|
-
}
|
|
1915
|
-
const tree = new _InMemoryBTree(buildConfigFromJSON(json, compareKeys));
|
|
2177
|
+
validateBTreeJSONSortOrder(json, compareKeys);
|
|
2178
|
+
const tree = new _InMemoryBTree(
|
|
2179
|
+
buildConfigFromJSON(json, compareKeys)
|
|
2180
|
+
);
|
|
1916
2181
|
applyAutoScaleCapacitySnapshot(
|
|
1917
2182
|
tree.state,
|
|
1918
2183
|
json.config.maxLeafEntries,
|
|
1919
2184
|
json.config.maxBranchChildren
|
|
1920
2185
|
);
|
|
1921
2186
|
if (json.entries.length > 0) {
|
|
1922
|
-
const pairs = new Array(
|
|
2187
|
+
const pairs = new Array(
|
|
2188
|
+
json.entries.length
|
|
2189
|
+
);
|
|
1923
2190
|
for (let i = 0; i < json.entries.length; i += 1) {
|
|
1924
2191
|
pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
|
|
1925
2192
|
}
|