@frostpillar/frostpillar-btree 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README-JA.md +95 -73
- package/README.md +96 -74
- package/dist/InMemoryBTree.d.ts +3 -36
- package/dist/btree/autoScale.d.ts +1 -0
- package/dist/btree/entry-lookup.d.ts +8 -0
- package/dist/btree/mutations.d.ts +1 -3
- package/dist/btree/rangeQuery.d.ts +4 -1
- package/dist/btree/rebalance-branch.d.ts +4 -0
- package/dist/btree/rebalance.d.ts +5 -2
- package/dist/btree/serialization.d.ts +3 -1
- package/dist/btree/split.d.ts +3 -0
- package/dist/btree/traversal.d.ts +7 -0
- package/dist/btree/types.d.ts +14 -3
- package/dist/{chunk-OWHENPGJ.js → chunk-OFXDCKLC.js} +663 -395
- package/dist/concurrency/ConcurrentInMemoryBTree.d.ts +3 -14
- package/dist/concurrency/coordinator.d.ts +21 -0
- package/dist/concurrency/syncLogValidation.d.ts +2 -0
- package/dist/core.cjs +663 -395
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1 -1
- package/dist/frostpillar-btree-core.min.js +1 -1
- package/dist/frostpillar-btree.min.js +1 -1
- package/dist/index.cjs +856 -545
- package/dist/index.d.ts +1 -1
- package/dist/index.js +190 -147
- package/package.json +2 -2
|
@@ -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,70 @@ 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");
|
|
809
|
+
const leafWasEmpty = leafEntryCount(leaf) === 0;
|
|
701
810
|
leaf.entries.push(borrowed);
|
|
702
811
|
writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
|
|
703
|
-
|
|
812
|
+
if (leafWasEmpty) updateMinKeyInAncestors(leaf);
|
|
813
|
+
return true;
|
|
704
814
|
}
|
|
705
815
|
if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
|
|
706
816
|
const borrowed = leftSibling.entries.pop();
|
|
707
|
-
if (borrowed === void 0)
|
|
817
|
+
if (borrowed === void 0)
|
|
818
|
+
throw new BTreeInvariantError("left leaf borrow failed");
|
|
708
819
|
leafUnshiftEntry(leaf, borrowed);
|
|
709
820
|
parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
|
|
710
821
|
updateMinKeyInAncestors(leaf);
|
|
711
|
-
return;
|
|
822
|
+
return true;
|
|
712
823
|
}
|
|
824
|
+
return false;
|
|
825
|
+
};
|
|
826
|
+
var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
|
|
713
827
|
if (leftSibling !== null) {
|
|
714
828
|
mergeLeafEntries(leftSibling, leaf);
|
|
715
829
|
detachLeafFromChain(state, leaf);
|
|
@@ -718,14 +832,34 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
|
718
832
|
return;
|
|
719
833
|
}
|
|
720
834
|
if (rightSibling !== null) {
|
|
835
|
+
const leafWasEmpty = leafEntryCount(leaf) === 0;
|
|
721
836
|
mergeLeafEntries(leaf, rightSibling);
|
|
722
837
|
detachLeafFromChain(state, rightSibling);
|
|
838
|
+
if (leafWasEmpty) updateMinKeyInAncestors(leaf);
|
|
723
839
|
removeChildFromBranch(parent, leafIndex + 1);
|
|
724
840
|
rebalanceAfterBranchRemoval(state, parent);
|
|
725
841
|
return;
|
|
726
842
|
}
|
|
727
843
|
throw new BTreeInvariantError("no leaf siblings to rebalance");
|
|
728
844
|
};
|
|
845
|
+
var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
846
|
+
if (leaf === state.root) {
|
|
847
|
+
if (state.entryCount === 0) {
|
|
848
|
+
state.leftmostLeaf = leaf;
|
|
849
|
+
state.rightmostLeaf = leaf;
|
|
850
|
+
}
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
|
|
854
|
+
const parent = leaf.parent;
|
|
855
|
+
if (parent === null)
|
|
856
|
+
throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
|
|
857
|
+
const leafIndex = leaf.indexInParent;
|
|
858
|
+
const { left, right } = findLeafSiblings(parent, leafIndex);
|
|
859
|
+
if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
|
|
860
|
+
return;
|
|
861
|
+
mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
|
|
862
|
+
};
|
|
729
863
|
|
|
730
864
|
// src/btree/deleteRange.ts
|
|
731
865
|
var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
@@ -738,16 +872,16 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
|
738
872
|
}
|
|
739
873
|
return { leaf, idx };
|
|
740
874
|
};
|
|
741
|
-
var findRemoveEnd = (state, leaf,
|
|
875
|
+
var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
|
|
742
876
|
const count = leafEntryCount(leaf);
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
|
|
748
|
-
removeEnd += 1;
|
|
877
|
+
const lastEntry = leafEntryAt(leaf, count - 1);
|
|
878
|
+
const cmpLast = state.compareKeys(lastEntry.key, endKey);
|
|
879
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
880
|
+
return count;
|
|
749
881
|
}
|
|
750
|
-
|
|
882
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
883
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
|
|
884
|
+
return endBound < count ? endBound : count;
|
|
751
885
|
};
|
|
752
886
|
var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
753
887
|
if (state.entryKeys !== null) {
|
|
@@ -764,15 +898,14 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
|
764
898
|
updateMinKeyInAncestors(leaf);
|
|
765
899
|
}
|
|
766
900
|
const countAfterSplice = leafEntryCount(leaf);
|
|
901
|
+
const rebalThreshold = leafRebalanceThreshold(state);
|
|
767
902
|
let safetyGuard = state.minLeafEntries + 4;
|
|
768
|
-
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) <
|
|
903
|
+
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
|
|
769
904
|
rebalanceAfterLeafRemoval(state, leaf);
|
|
770
|
-
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
905
|
+
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
906
|
+
break;
|
|
771
907
|
safetyGuard -= 1;
|
|
772
908
|
}
|
|
773
|
-
if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
|
|
774
|
-
updateMinKeyInAncestors(leaf);
|
|
775
|
-
}
|
|
776
909
|
return countAfterSplice;
|
|
777
910
|
};
|
|
778
911
|
var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
|
|
@@ -795,10 +928,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
795
928
|
}
|
|
796
929
|
if (idx >= leafEntryCount(leaf)) break;
|
|
797
930
|
const count = leafEntryCount(leaf);
|
|
798
|
-
const removeEnd = findRemoveEnd(state, leaf,
|
|
931
|
+
const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
|
|
799
932
|
const removeCount = removeEnd - idx;
|
|
800
933
|
if (removeCount === 0) break;
|
|
801
|
-
const countAfterSplice = spliceLeafAndRebalance(
|
|
934
|
+
const countAfterSplice = spliceLeafAndRebalance(
|
|
935
|
+
state,
|
|
936
|
+
leaf,
|
|
937
|
+
idx,
|
|
938
|
+
removeCount
|
|
939
|
+
);
|
|
802
940
|
deleted += removeCount;
|
|
803
941
|
if (removeEnd < count) break;
|
|
804
942
|
if (!isLeafStillValid(state, leaf)) {
|
|
@@ -844,25 +982,42 @@ var computeNextAutoScaleThreshold = (entryCount) => {
|
|
|
844
982
|
}
|
|
845
983
|
return Number.MAX_SAFE_INTEGER;
|
|
846
984
|
};
|
|
847
|
-
var
|
|
848
|
-
if (typeof config.compareKeys !== "function") {
|
|
849
|
-
throw new BTreeValidationError("compareKeys must be a function.");
|
|
850
|
-
}
|
|
851
|
-
const autoScale = config.autoScale === true;
|
|
985
|
+
var resolveInitialCapacity = (config, autoScale) => {
|
|
852
986
|
if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
|
|
853
|
-
throw new BTreeValidationError(
|
|
987
|
+
throw new BTreeValidationError(
|
|
988
|
+
"autoScale conflicts with explicit capacity."
|
|
989
|
+
);
|
|
854
990
|
}
|
|
855
|
-
let maxLeafEntries;
|
|
856
|
-
let maxBranchChildren;
|
|
857
991
|
if (autoScale) {
|
|
858
992
|
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);
|
|
993
|
+
return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
|
|
864
994
|
}
|
|
995
|
+
return {
|
|
996
|
+
maxLeafEntries: normalizeNodeCapacity(
|
|
997
|
+
config.maxLeafEntries,
|
|
998
|
+
"maxLeafEntries",
|
|
999
|
+
DEFAULT_MAX_LEAF_ENTRIES
|
|
1000
|
+
),
|
|
1001
|
+
maxBranchChildren: normalizeNodeCapacity(
|
|
1002
|
+
config.maxBranchChildren,
|
|
1003
|
+
"maxBranchChildren",
|
|
1004
|
+
DEFAULT_MAX_BRANCH_CHILDREN
|
|
1005
|
+
)
|
|
1006
|
+
};
|
|
1007
|
+
};
|
|
1008
|
+
var createInitialState = (config) => {
|
|
1009
|
+
if (typeof config.compareKeys !== "function") {
|
|
1010
|
+
throw new BTreeValidationError("compareKeys must be a function.");
|
|
1011
|
+
}
|
|
1012
|
+
const autoScale = config.autoScale === true;
|
|
1013
|
+
const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
|
|
1014
|
+
config,
|
|
1015
|
+
autoScale
|
|
1016
|
+
);
|
|
865
1017
|
const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
|
|
1018
|
+
const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
|
|
1019
|
+
config.deleteRebalancePolicy
|
|
1020
|
+
);
|
|
866
1021
|
const emptyLeaf = createLeafNode([], null);
|
|
867
1022
|
return {
|
|
868
1023
|
compareKeys: config.compareKeys,
|
|
@@ -878,6 +1033,7 @@ var createInitialState = (config) => {
|
|
|
878
1033
|
nextSequence: 0,
|
|
879
1034
|
entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
|
|
880
1035
|
autoScale,
|
|
1036
|
+
deleteRebalancePolicy,
|
|
881
1037
|
_nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
|
|
882
1038
|
_cursor: { leaf: emptyLeaf, index: 0 }
|
|
883
1039
|
};
|
|
@@ -893,7 +1049,9 @@ var maybeAutoScale = (state) => {
|
|
|
893
1049
|
state.maxBranchChildren = maxBranch;
|
|
894
1050
|
state.minBranchChildren = minOccupancy(maxBranch);
|
|
895
1051
|
}
|
|
896
|
-
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1052
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1053
|
+
state.entryCount
|
|
1054
|
+
);
|
|
897
1055
|
};
|
|
898
1056
|
var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
|
|
899
1057
|
if (!state.autoScale) {
|
|
@@ -920,6 +1078,14 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
|
|
|
920
1078
|
state.minLeafEntries = minOccupancy(normalizedLeaf);
|
|
921
1079
|
state.minBranchChildren = minOccupancy(normalizedBranch);
|
|
922
1080
|
};
|
|
1081
|
+
var resetAutoScaleToTier0 = (state) => {
|
|
1082
|
+
const tier = computeAutoScaleTier(0);
|
|
1083
|
+
state.maxLeafEntries = tier.maxLeaf;
|
|
1084
|
+
state.maxBranchChildren = tier.maxBranch;
|
|
1085
|
+
state.minLeafEntries = minOccupancy(tier.maxLeaf);
|
|
1086
|
+
state.minBranchChildren = minOccupancy(tier.maxBranch);
|
|
1087
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
1088
|
+
};
|
|
923
1089
|
|
|
924
1090
|
// src/btree/bulkLoad.ts
|
|
925
1091
|
var computeChunkBoundaries = (total, max, min) => {
|
|
@@ -940,7 +1106,11 @@ var computeChunkBoundaries = (total, max, min) => {
|
|
|
940
1106
|
return boundaries;
|
|
941
1107
|
};
|
|
942
1108
|
var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
943
|
-
const boundaries = computeChunkBoundaries(
|
|
1109
|
+
const boundaries = computeChunkBoundaries(
|
|
1110
|
+
entries.length,
|
|
1111
|
+
state.maxLeafEntries,
|
|
1112
|
+
state.minLeafEntries
|
|
1113
|
+
);
|
|
944
1114
|
const leaves = new Array(boundaries.length);
|
|
945
1115
|
let chunkStart = 0;
|
|
946
1116
|
for (let c = 0; c < boundaries.length; c += 1) {
|
|
@@ -948,7 +1118,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
|
948
1118
|
const chunk = new Array(chunkEnd - chunkStart);
|
|
949
1119
|
for (let i = chunkStart; i < chunkEnd; i += 1) {
|
|
950
1120
|
const seq = baseSequence + i;
|
|
951
|
-
chunk[i - chunkStart] =
|
|
1121
|
+
chunk[i - chunkStart] = createEntry(
|
|
1122
|
+
entries[i].key,
|
|
1123
|
+
seq,
|
|
1124
|
+
entries[i].value
|
|
1125
|
+
);
|
|
952
1126
|
ids[i] = seq;
|
|
953
1127
|
if (state.entryKeys !== null) {
|
|
954
1128
|
state.entryKeys.set(seq, entries[i].key);
|
|
@@ -982,11 +1156,18 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
982
1156
|
} else {
|
|
983
1157
|
let currentLevel = leaves;
|
|
984
1158
|
while (currentLevel.length > 1) {
|
|
985
|
-
const bounds = computeChunkBoundaries(
|
|
1159
|
+
const bounds = computeChunkBoundaries(
|
|
1160
|
+
currentLevel.length,
|
|
1161
|
+
state.maxBranchChildren,
|
|
1162
|
+
state.minBranchChildren
|
|
1163
|
+
);
|
|
986
1164
|
const nextLevel = new Array(bounds.length);
|
|
987
1165
|
let start = 0;
|
|
988
1166
|
for (let b = 0; b < bounds.length; b += 1) {
|
|
989
|
-
nextLevel[b] = createBranchNode(
|
|
1167
|
+
nextLevel[b] = createBranchNode(
|
|
1168
|
+
currentLevel.slice(start, bounds[b]),
|
|
1169
|
+
null
|
|
1170
|
+
);
|
|
990
1171
|
start = bounds[b];
|
|
991
1172
|
}
|
|
992
1173
|
currentLevel = nextLevel;
|
|
@@ -997,9 +1178,12 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
997
1178
|
return ids;
|
|
998
1179
|
};
|
|
999
1180
|
|
|
1000
|
-
// src/btree/
|
|
1181
|
+
// src/btree/split.ts
|
|
1001
1182
|
var insertChildAfter = (state, parent, existingChild, childToInsert) => {
|
|
1002
|
-
const newChildMinKey = {
|
|
1183
|
+
const newChildMinKey = {
|
|
1184
|
+
key: void 0,
|
|
1185
|
+
sequence: 0
|
|
1186
|
+
};
|
|
1003
1187
|
if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
|
|
1004
1188
|
throw new BTreeInvariantError("inserted child has no min key");
|
|
1005
1189
|
}
|
|
@@ -1037,7 +1221,10 @@ var splitLeaf = (state, leaf) => {
|
|
|
1037
1221
|
var splitBranch = (state, branch) => {
|
|
1038
1222
|
branchCompact(branch);
|
|
1039
1223
|
const splitAt = Math.ceil(branch.children.length / 2);
|
|
1040
|
-
const sibling = createBranchNode(
|
|
1224
|
+
const sibling = createBranchNode(
|
|
1225
|
+
branch.children.splice(splitAt),
|
|
1226
|
+
branch.parent
|
|
1227
|
+
);
|
|
1041
1228
|
branch.keys.splice(splitAt);
|
|
1042
1229
|
if (branch.parent === null) {
|
|
1043
1230
|
state.root = createBranchNode([branch, sibling], null);
|
|
@@ -1045,42 +1232,125 @@ var splitBranch = (state, branch) => {
|
|
|
1045
1232
|
}
|
|
1046
1233
|
insertChildAfter(state, branch.parent, branch, sibling);
|
|
1047
1234
|
};
|
|
1235
|
+
|
|
1236
|
+
// src/btree/entry-lookup.ts
|
|
1237
|
+
var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
1238
|
+
const targetLeaf = findLeafForKey(state, userKey, sequence);
|
|
1239
|
+
const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
|
|
1240
|
+
if (index >= leafEntryCount(targetLeaf)) return null;
|
|
1241
|
+
const entry = leafEntryAt(targetLeaf, index);
|
|
1242
|
+
if (entry.entryId !== sequence) return null;
|
|
1243
|
+
return { leaf: targetLeaf, index };
|
|
1244
|
+
};
|
|
1245
|
+
var peekEntryById = (state, entryId) => {
|
|
1246
|
+
if (state.entryKeys === null) {
|
|
1247
|
+
throw new BTreeInvariantError(
|
|
1248
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1251
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1252
|
+
if (userKey === void 0) return null;
|
|
1253
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1254
|
+
if (found === null) return null;
|
|
1255
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1256
|
+
return entry;
|
|
1257
|
+
};
|
|
1258
|
+
var updateEntryById = (state, entryId, newValue) => {
|
|
1259
|
+
if (state.entryKeys === null) {
|
|
1260
|
+
throw new BTreeInvariantError(
|
|
1261
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1265
|
+
if (userKey === void 0) return null;
|
|
1266
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1267
|
+
if (found === null) return null;
|
|
1268
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1269
|
+
const updated = createEntry(entry.key, entry.entryId, newValue);
|
|
1270
|
+
found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
|
|
1271
|
+
return updated;
|
|
1272
|
+
};
|
|
1273
|
+
var removeEntryById = (state, entryId) => {
|
|
1274
|
+
if (state.entryKeys === null) {
|
|
1275
|
+
throw new BTreeInvariantError(
|
|
1276
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1277
|
+
);
|
|
1278
|
+
}
|
|
1279
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1280
|
+
if (userKey === void 0) return null;
|
|
1281
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1282
|
+
if (found === null) return null;
|
|
1283
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1284
|
+
leafRemoveAt(found.leaf, found.index);
|
|
1285
|
+
state.entryCount -= 1;
|
|
1286
|
+
state.entryKeys.delete(entryId);
|
|
1287
|
+
if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
|
|
1288
|
+
updateMinKeyInAncestors(found.leaf);
|
|
1289
|
+
}
|
|
1290
|
+
if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
|
|
1291
|
+
rebalanceAfterLeafRemoval(state, found.leaf);
|
|
1292
|
+
}
|
|
1293
|
+
return entry;
|
|
1294
|
+
};
|
|
1295
|
+
|
|
1296
|
+
// src/btree/mutations.ts
|
|
1297
|
+
var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
|
|
1298
|
+
if (state.duplicateKeys === "allow") return null;
|
|
1299
|
+
if (insertAt > 0) {
|
|
1300
|
+
const candidate = leafEntryAt(targetLeaf, insertAt - 1);
|
|
1301
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1302
|
+
return {
|
|
1303
|
+
leaf: targetLeaf,
|
|
1304
|
+
physIndex: targetLeaf.entryOffset + insertAt - 1,
|
|
1305
|
+
entry: candidate
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
} else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
|
|
1309
|
+
const prevLeaf = targetLeaf.prev;
|
|
1310
|
+
const prevCount = leafEntryCount(prevLeaf);
|
|
1311
|
+
const candidate = leafEntryAt(prevLeaf, prevCount - 1);
|
|
1312
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1313
|
+
return {
|
|
1314
|
+
leaf: prevLeaf,
|
|
1315
|
+
physIndex: prevLeaf.entryOffset + prevCount - 1,
|
|
1316
|
+
entry: candidate
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
return null;
|
|
1321
|
+
};
|
|
1048
1322
|
var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
|
|
1049
1323
|
const sequence = state.nextSequence;
|
|
1050
1324
|
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
|
-
}
|
|
1325
|
+
const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
|
|
1326
|
+
if (dup !== null) {
|
|
1327
|
+
if (state.duplicateKeys === "reject") {
|
|
1328
|
+
throw new BTreeValidationError("Duplicate key rejected.");
|
|
1329
|
+
}
|
|
1330
|
+
dup.leaf.entries[dup.physIndex] = createEntry(
|
|
1331
|
+
dup.entry.key,
|
|
1332
|
+
dup.entry.entryId,
|
|
1333
|
+
value
|
|
1334
|
+
);
|
|
1335
|
+
return dup.entry.entryId;
|
|
1072
1336
|
}
|
|
1073
1337
|
if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
|
|
1074
1338
|
throw new BTreeValidationError("Sequence overflow.");
|
|
1075
1339
|
}
|
|
1076
1340
|
state.nextSequence += 1;
|
|
1077
|
-
leafInsertAt(
|
|
1341
|
+
leafInsertAt(
|
|
1342
|
+
targetLeaf,
|
|
1343
|
+
insertAt,
|
|
1344
|
+
createEntry(key, sequence, value)
|
|
1345
|
+
);
|
|
1078
1346
|
state.entryCount += 1;
|
|
1079
1347
|
if (state.entryKeys !== null) {
|
|
1080
1348
|
state.entryKeys.set(sequence, key);
|
|
1081
1349
|
}
|
|
1082
|
-
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1083
|
-
|
|
1350
|
+
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1351
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1352
|
+
if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
|
|
1353
|
+
splitLeaf(state, targetLeaf);
|
|
1084
1354
|
maybeAutoScale(state);
|
|
1085
1355
|
return sequence;
|
|
1086
1356
|
};
|
|
@@ -1091,7 +1361,8 @@ var putEntry = (state, key, value) => {
|
|
|
1091
1361
|
var popFirstEntry = (state) => {
|
|
1092
1362
|
if (state.entryCount === 0) return null;
|
|
1093
1363
|
const firstEntry = leafShiftEntry(state.leftmostLeaf);
|
|
1094
|
-
if (firstEntry === void 0)
|
|
1364
|
+
if (firstEntry === void 0)
|
|
1365
|
+
throw new BTreeInvariantError("leftmost leaf empty but count > 0");
|
|
1095
1366
|
state.entryCount -= 1;
|
|
1096
1367
|
if (state.entryKeys !== null) {
|
|
1097
1368
|
state.entryKeys.delete(firstEntry.entryId);
|
|
@@ -1107,7 +1378,8 @@ var popFirstEntry = (state) => {
|
|
|
1107
1378
|
var popLastEntry = (state) => {
|
|
1108
1379
|
if (state.entryCount === 0) return null;
|
|
1109
1380
|
const lastEntry = leafPopEntry(state.rightmostLeaf);
|
|
1110
|
-
if (lastEntry === void 0)
|
|
1381
|
+
if (lastEntry === void 0)
|
|
1382
|
+
throw new BTreeInvariantError("rightmost leaf empty but count > 0");
|
|
1111
1383
|
state.entryCount -= 1;
|
|
1112
1384
|
if (state.entryKeys !== null) {
|
|
1113
1385
|
state.entryKeys.delete(lastEntry.entryId);
|
|
@@ -1128,70 +1400,22 @@ var removeFirstMatchingEntry = (state, key) => {
|
|
|
1128
1400
|
if (state.entryKeys !== null) {
|
|
1129
1401
|
state.entryKeys.delete(targetEntry.entryId);
|
|
1130
1402
|
}
|
|
1131
|
-
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1403
|
+
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1404
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1132
1405
|
if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
|
|
1133
1406
|
rebalanceAfterLeafRemoval(state, targetLeaf);
|
|
1134
1407
|
}
|
|
1135
1408
|
return targetEntry;
|
|
1136
1409
|
};
|
|
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
1410
|
var putManyEntries = (state, entries) => {
|
|
1189
1411
|
if (entries.length === 0) return [];
|
|
1190
1412
|
const strictlyAscending = state.duplicateKeys !== "allow";
|
|
1191
1413
|
for (let i = 1; i < entries.length; i += 1) {
|
|
1192
1414
|
const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
|
|
1193
1415
|
if (cmp > 0) {
|
|
1194
|
-
throw new BTreeValidationError(
|
|
1416
|
+
throw new BTreeValidationError(
|
|
1417
|
+
"putMany: entries not in ascending order."
|
|
1418
|
+
);
|
|
1195
1419
|
}
|
|
1196
1420
|
if (strictlyAscending && cmp === 0) {
|
|
1197
1421
|
throw new BTreeValidationError(
|
|
@@ -1204,7 +1428,12 @@ var putManyEntries = (state, entries) => {
|
|
|
1204
1428
|
let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
|
|
1205
1429
|
for (let i = 0; i < entries.length; i += 1) {
|
|
1206
1430
|
const entry = entries[i];
|
|
1207
|
-
const targetLeaf = findLeafFromHint(
|
|
1431
|
+
const targetLeaf = findLeafFromHint(
|
|
1432
|
+
state,
|
|
1433
|
+
hintLeaf,
|
|
1434
|
+
entry.key,
|
|
1435
|
+
state.nextSequence
|
|
1436
|
+
);
|
|
1208
1437
|
ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
|
|
1209
1438
|
hintLeaf = targetLeaf;
|
|
1210
1439
|
}
|
|
@@ -1219,7 +1448,8 @@ var buildConfigFromState = (state) => {
|
|
|
1219
1448
|
compareKeys: state.compareKeys,
|
|
1220
1449
|
duplicateKeys: state.duplicateKeys,
|
|
1221
1450
|
enableEntryIdLookup: state.entryKeys !== null,
|
|
1222
|
-
autoScale: state.autoScale
|
|
1451
|
+
autoScale: state.autoScale,
|
|
1452
|
+
deleteRebalancePolicy: state.deleteRebalancePolicy
|
|
1223
1453
|
};
|
|
1224
1454
|
if (!state.autoScale) {
|
|
1225
1455
|
config.maxLeafEntries = state.maxLeafEntries;
|
|
@@ -1239,17 +1469,17 @@ var serializeToJSON = (state) => {
|
|
|
1239
1469
|
}
|
|
1240
1470
|
leaf = leaf.next;
|
|
1241
1471
|
}
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
enableEntryIdLookup: state.entryKeys !== null,
|
|
1249
|
-
autoScale: state.autoScale
|
|
1250
|
-
},
|
|
1251
|
-
entries
|
|
1472
|
+
const config = {
|
|
1473
|
+
maxLeafEntries: state.maxLeafEntries,
|
|
1474
|
+
maxBranchChildren: state.maxBranchChildren,
|
|
1475
|
+
duplicateKeys: state.duplicateKeys,
|
|
1476
|
+
enableEntryIdLookup: state.entryKeys !== null,
|
|
1477
|
+
autoScale: state.autoScale
|
|
1252
1478
|
};
|
|
1479
|
+
if (state.deleteRebalancePolicy !== "standard") {
|
|
1480
|
+
config.deleteRebalancePolicy = state.deleteRebalancePolicy;
|
|
1481
|
+
}
|
|
1482
|
+
return { version: 1, config, entries };
|
|
1253
1483
|
};
|
|
1254
1484
|
var MAX_SERIALIZED_ENTRIES = 1e6;
|
|
1255
1485
|
var validateStructure = (json) => {
|
|
@@ -1312,13 +1542,28 @@ var validateBTreeJSON = (json) => {
|
|
|
1312
1542
|
validateStructure(json);
|
|
1313
1543
|
validateConfig(json.config);
|
|
1314
1544
|
};
|
|
1545
|
+
var validateBTreeJSONSortOrder = (json, compareKeys) => {
|
|
1546
|
+
const strict = json.config.duplicateKeys !== "allow";
|
|
1547
|
+
for (let i = 1; i < json.entries.length; i += 1) {
|
|
1548
|
+
const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
|
|
1549
|
+
if (cmp > 0) {
|
|
1550
|
+
throw new BTreeValidationError("fromJSON: entries not sorted.");
|
|
1551
|
+
}
|
|
1552
|
+
if (strict && cmp === 0) {
|
|
1553
|
+
throw new BTreeValidationError(
|
|
1554
|
+
'fromJSON: duplicate keys require duplicateKeys "allow".'
|
|
1555
|
+
);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
};
|
|
1315
1559
|
var buildConfigFromJSON = (json, compareKeys) => {
|
|
1316
1560
|
const cfg = json.config;
|
|
1317
1561
|
const config = {
|
|
1318
1562
|
compareKeys,
|
|
1319
1563
|
duplicateKeys: cfg.duplicateKeys,
|
|
1320
1564
|
enableEntryIdLookup: cfg.enableEntryIdLookup,
|
|
1321
|
-
autoScale: cfg.autoScale
|
|
1565
|
+
autoScale: cfg.autoScale,
|
|
1566
|
+
deleteRebalancePolicy: cfg.deleteRebalancePolicy
|
|
1322
1567
|
};
|
|
1323
1568
|
if (!cfg.autoScale) {
|
|
1324
1569
|
config.maxLeafEntries = cfg.maxLeafEntries;
|
|
@@ -1327,6 +1572,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
|
|
|
1327
1572
|
return config;
|
|
1328
1573
|
};
|
|
1329
1574
|
|
|
1575
|
+
// src/btree/traversal.ts
|
|
1576
|
+
var snapshotEntries = (state) => {
|
|
1577
|
+
const result = new Array(state.entryCount);
|
|
1578
|
+
let leaf = state.leftmostLeaf;
|
|
1579
|
+
let writeIdx = 0;
|
|
1580
|
+
while (leaf !== null) {
|
|
1581
|
+
const count = leafEntryCount(leaf);
|
|
1582
|
+
for (let i = 0; i < count; i += 1) {
|
|
1583
|
+
result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
|
|
1584
|
+
}
|
|
1585
|
+
leaf = leaf.next;
|
|
1586
|
+
}
|
|
1587
|
+
return result;
|
|
1588
|
+
};
|
|
1589
|
+
var collectInternalEntries = (state) => {
|
|
1590
|
+
const result = new Array(state.entryCount);
|
|
1591
|
+
let leaf = state.leftmostLeaf;
|
|
1592
|
+
let writeIdx = 0;
|
|
1593
|
+
while (leaf !== null) {
|
|
1594
|
+
const count = leafEntryCount(leaf);
|
|
1595
|
+
for (let i = 0; i < count; i += 1) {
|
|
1596
|
+
result[writeIdx++] = leafEntryAt(leaf, i);
|
|
1597
|
+
}
|
|
1598
|
+
leaf = leaf.next;
|
|
1599
|
+
}
|
|
1600
|
+
return result;
|
|
1601
|
+
};
|
|
1602
|
+
var forEachEntry = (state, callback, thisArg) => {
|
|
1603
|
+
let leaf = state.leftmostLeaf;
|
|
1604
|
+
while (leaf !== null) {
|
|
1605
|
+
const count = leafEntryCount(leaf);
|
|
1606
|
+
for (let i = 0; i < count; i += 1) {
|
|
1607
|
+
callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
|
|
1608
|
+
}
|
|
1609
|
+
leaf = leaf.next;
|
|
1610
|
+
}
|
|
1611
|
+
};
|
|
1612
|
+
|
|
1330
1613
|
// src/btree/integrity-helpers.ts
|
|
1331
1614
|
var nodeMinKey = (node) => {
|
|
1332
1615
|
if (isLeafNode(node)) {
|
|
@@ -1335,7 +1618,10 @@ var nodeMinKey = (node) => {
|
|
|
1335
1618
|
return { key: e.key, sequence: e.entryId };
|
|
1336
1619
|
}
|
|
1337
1620
|
if (node.childOffset >= node.keys.length) return null;
|
|
1338
|
-
return {
|
|
1621
|
+
return {
|
|
1622
|
+
key: node.keys[node.childOffset].key,
|
|
1623
|
+
sequence: node.keys[node.childOffset].sequence
|
|
1624
|
+
};
|
|
1339
1625
|
};
|
|
1340
1626
|
var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
|
|
1341
1627
|
const cmp = comparator(leftKey, rightKey);
|
|
@@ -1357,9 +1643,7 @@ var getNodeMaxKey = (node) => {
|
|
|
1357
1643
|
};
|
|
1358
1644
|
var validateComparatorResult = (result) => {
|
|
1359
1645
|
if (!Number.isFinite(result)) {
|
|
1360
|
-
throw new BTreeValidationError(
|
|
1361
|
-
"compareKeys must return a finite number."
|
|
1362
|
-
);
|
|
1646
|
+
throw new BTreeValidationError("compareKeys must return a finite number.");
|
|
1363
1647
|
}
|
|
1364
1648
|
return result;
|
|
1365
1649
|
};
|
|
@@ -1419,6 +1703,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
|
|
|
1419
1703
|
throw error;
|
|
1420
1704
|
}
|
|
1421
1705
|
};
|
|
1706
|
+
var validateAdjacentLeafOrdering = (state, previous, cursor) => {
|
|
1707
|
+
const prevMax = getNodeMaxKey(previous);
|
|
1708
|
+
const currentMin = nodeMinKey(cursor);
|
|
1709
|
+
if (prevMax === null || currentMin === null) {
|
|
1710
|
+
throw new BTreeInvariantError(
|
|
1711
|
+
"Non-empty tree leaf chain contains empty leaf node."
|
|
1712
|
+
);
|
|
1713
|
+
}
|
|
1714
|
+
if (compareNodeKeys(
|
|
1715
|
+
state.compareKeys,
|
|
1716
|
+
prevMax.key,
|
|
1717
|
+
prevMax.sequence,
|
|
1718
|
+
currentMin.key,
|
|
1719
|
+
currentMin.sequence
|
|
1720
|
+
) > 0) {
|
|
1721
|
+
throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
|
|
1722
|
+
}
|
|
1723
|
+
const prevCount = leafEntryCount(previous);
|
|
1724
|
+
const curCount = leafEntryCount(cursor);
|
|
1725
|
+
if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
|
|
1726
|
+
leafEntryAt(previous, prevCount - 1).key,
|
|
1727
|
+
leafEntryAt(cursor, 0).key
|
|
1728
|
+
) === 0) {
|
|
1729
|
+
throw new BTreeInvariantError(
|
|
1730
|
+
"Duplicate user key detected across adjacent leaves with uniqueness policy."
|
|
1731
|
+
);
|
|
1732
|
+
}
|
|
1733
|
+
};
|
|
1422
1734
|
var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
1423
1735
|
if (!isLeafNode(cursor)) {
|
|
1424
1736
|
throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
|
|
@@ -1430,22 +1742,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
|
1430
1742
|
throw new BTreeInvariantError("Leaf prev pointer mismatch.");
|
|
1431
1743
|
}
|
|
1432
1744
|
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
|
-
}
|
|
1745
|
+
validateAdjacentLeafOrdering(state, previous, cursor);
|
|
1449
1746
|
}
|
|
1450
1747
|
};
|
|
1451
1748
|
var validateLeafLinks = (state, expectedLeafCount) => {
|
|
@@ -1454,7 +1751,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1454
1751
|
throw new BTreeInvariantError("Empty tree root must be a leaf node.");
|
|
1455
1752
|
}
|
|
1456
1753
|
if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
|
|
1457
|
-
throw new BTreeInvariantError(
|
|
1754
|
+
throw new BTreeInvariantError(
|
|
1755
|
+
"Empty tree leaf pointers must reference root leaf."
|
|
1756
|
+
);
|
|
1458
1757
|
}
|
|
1459
1758
|
return;
|
|
1460
1759
|
}
|
|
@@ -1479,7 +1778,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1479
1778
|
throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
|
|
1480
1779
|
}
|
|
1481
1780
|
if (leafCount !== expectedLeafCount) {
|
|
1482
|
-
throw new BTreeInvariantError(
|
|
1781
|
+
throw new BTreeInvariantError(
|
|
1782
|
+
"Leaf chain count mismatch with tree traversal count."
|
|
1783
|
+
);
|
|
1483
1784
|
}
|
|
1484
1785
|
};
|
|
1485
1786
|
|
|
@@ -1506,7 +1807,9 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1506
1807
|
leafEntryAt(node, index - 1).key,
|
|
1507
1808
|
leafEntryAt(node, index).key
|
|
1508
1809
|
) === 0) {
|
|
1509
|
-
throw new BTreeInvariantError(
|
|
1810
|
+
throw new BTreeInvariantError(
|
|
1811
|
+
"Duplicate user key detected in tree with uniqueness policy."
|
|
1812
|
+
);
|
|
1510
1813
|
}
|
|
1511
1814
|
}
|
|
1512
1815
|
}
|
|
@@ -1514,12 +1817,7 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1514
1817
|
const first = leafEntryAt(node, index - 2);
|
|
1515
1818
|
const second = leafEntryAt(node, index - 1);
|
|
1516
1819
|
const third = leafEntryAt(node, index);
|
|
1517
|
-
assertTransitivityAsInvariant(
|
|
1518
|
-
state,
|
|
1519
|
-
first.key,
|
|
1520
|
-
second.key,
|
|
1521
|
-
third.key
|
|
1522
|
-
);
|
|
1820
|
+
assertTransitivityAsInvariant(state, first.key, second.key, third.key);
|
|
1523
1821
|
}
|
|
1524
1822
|
if (count > state.maxLeafEntries) {
|
|
1525
1823
|
throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
|
|
@@ -1528,9 +1826,14 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1528
1826
|
var validateLeafNode = (state, node, depth) => {
|
|
1529
1827
|
validateLeafNodeOrdering(state, node);
|
|
1530
1828
|
const count = leafEntryCount(node);
|
|
1531
|
-
|
|
1829
|
+
let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
|
|
1830
|
+
if (state.deleteRebalancePolicy === "lazy") {
|
|
1831
|
+
baseMinLeaf = applyLazyThreshold(baseMinLeaf);
|
|
1832
|
+
}
|
|
1532
1833
|
if (node !== state.root && count < baseMinLeaf) {
|
|
1533
|
-
throw new BTreeInvariantError(
|
|
1834
|
+
throw new BTreeInvariantError(
|
|
1835
|
+
"Non-root leaf node violates minimum occupancy."
|
|
1836
|
+
);
|
|
1534
1837
|
}
|
|
1535
1838
|
const first = count === 0 ? null : leafEntryAt(node, 0);
|
|
1536
1839
|
const last = count === 0 ? null : leafEntryAt(node, count - 1);
|
|
@@ -1552,76 +1855,110 @@ var validateBranchStructure = (state, node) => {
|
|
|
1552
1855
|
}
|
|
1553
1856
|
const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
|
|
1554
1857
|
if (node !== state.root && liveCount < baseMinBranch) {
|
|
1555
|
-
throw new BTreeInvariantError(
|
|
1858
|
+
throw new BTreeInvariantError(
|
|
1859
|
+
"Non-root branch node violates minimum occupancy."
|
|
1860
|
+
);
|
|
1556
1861
|
}
|
|
1557
1862
|
if (liveCount > state.maxBranchChildren) {
|
|
1558
1863
|
throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
|
|
1559
1864
|
}
|
|
1560
1865
|
if (node.keys.length !== node.children.length) {
|
|
1561
|
-
throw new BTreeInvariantError(
|
|
1866
|
+
throw new BTreeInvariantError(
|
|
1867
|
+
"Branch keys array length does not match children array length."
|
|
1868
|
+
);
|
|
1562
1869
|
}
|
|
1563
1870
|
};
|
|
1564
1871
|
var validateBranchChild = (state, node, childIndex, depth) => {
|
|
1565
1872
|
const child = node.children[childIndex];
|
|
1566
1873
|
if (child.parent !== node) {
|
|
1567
|
-
throw new BTreeInvariantError(
|
|
1874
|
+
throw new BTreeInvariantError(
|
|
1875
|
+
"Child-parent pointer mismatch in branch node."
|
|
1876
|
+
);
|
|
1568
1877
|
}
|
|
1569
1878
|
if (child.indexInParent !== childIndex) {
|
|
1570
|
-
throw new BTreeInvariantError(
|
|
1879
|
+
throw new BTreeInvariantError(
|
|
1880
|
+
"Child indexInParent does not match actual position in parent."
|
|
1881
|
+
);
|
|
1571
1882
|
}
|
|
1572
1883
|
const childValidation = validateNode(state, child, depth + 1);
|
|
1573
1884
|
if (childValidation.minKey === null || childValidation.maxKey === null) {
|
|
1574
|
-
throw new BTreeInvariantError(
|
|
1885
|
+
throw new BTreeInvariantError(
|
|
1886
|
+
"Branch child must not be empty in non-root branch tree."
|
|
1887
|
+
);
|
|
1575
1888
|
}
|
|
1576
1889
|
const cachedMinKey = node.keys[childIndex];
|
|
1577
1890
|
const actualMinKey = nodeMinKey(child);
|
|
1578
|
-
if (actualMinKey === null || compareNodeKeys(
|
|
1579
|
-
|
|
1891
|
+
if (actualMinKey === null || compareNodeKeys(
|
|
1892
|
+
state.compareKeys,
|
|
1893
|
+
cachedMinKey.key,
|
|
1894
|
+
cachedMinKey.sequence,
|
|
1895
|
+
actualMinKey.key,
|
|
1896
|
+
actualMinKey.sequence
|
|
1897
|
+
) !== 0) {
|
|
1898
|
+
throw new BTreeInvariantError(
|
|
1899
|
+
"Branch cached key does not match actual child minimum key."
|
|
1900
|
+
);
|
|
1580
1901
|
}
|
|
1581
1902
|
return childValidation;
|
|
1582
1903
|
};
|
|
1904
|
+
var mergeChildValidation = (state, accumulated, childValidation) => {
|
|
1905
|
+
if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
|
|
1906
|
+
throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
|
|
1907
|
+
}
|
|
1908
|
+
if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
|
|
1909
|
+
accumulated.leafDepth = childValidation.leafDepth;
|
|
1910
|
+
}
|
|
1911
|
+
if (accumulated.previousChildMax !== null && compareNodeKeys(
|
|
1912
|
+
state.compareKeys,
|
|
1913
|
+
accumulated.previousChildMax.key,
|
|
1914
|
+
accumulated.previousChildMax.sequence,
|
|
1915
|
+
childValidation.minKey.key,
|
|
1916
|
+
childValidation.minKey.sequence
|
|
1917
|
+
) >= 0) {
|
|
1918
|
+
throw new BTreeInvariantError(
|
|
1919
|
+
"Branch child key ranges are not strictly ordered."
|
|
1920
|
+
);
|
|
1921
|
+
}
|
|
1922
|
+
if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
|
|
1923
|
+
accumulated.maxKey = childValidation.maxKey;
|
|
1924
|
+
accumulated.previousChildMax = childValidation.maxKey;
|
|
1925
|
+
accumulated.leafCount += childValidation.leafCount;
|
|
1926
|
+
accumulated.branchCount += childValidation.branchCount;
|
|
1927
|
+
accumulated.entryCount += childValidation.entryCount;
|
|
1928
|
+
};
|
|
1583
1929
|
var validateNode = (state, node, depth) => {
|
|
1584
1930
|
if (isLeafNode(node)) {
|
|
1585
1931
|
return validateLeafNode(state, node, depth);
|
|
1586
1932
|
}
|
|
1587
1933
|
validateBranchStructure(state, node);
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1934
|
+
const acc = {
|
|
1935
|
+
leafDepth: null,
|
|
1936
|
+
leafCount: 0,
|
|
1937
|
+
branchCount: 1,
|
|
1938
|
+
entryCount: 0,
|
|
1939
|
+
minKey: null,
|
|
1940
|
+
maxKey: null,
|
|
1941
|
+
previousChildMax: null
|
|
1942
|
+
};
|
|
1595
1943
|
for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
|
|
1596
1944
|
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;
|
|
1945
|
+
mergeChildValidation(state, acc, childValidation);
|
|
1618
1946
|
}
|
|
1619
|
-
return {
|
|
1947
|
+
return {
|
|
1948
|
+
minKey: acc.minKey,
|
|
1949
|
+
maxKey: acc.maxKey,
|
|
1950
|
+
leafDepth: acc.leafDepth,
|
|
1951
|
+
leafCount: acc.leafCount,
|
|
1952
|
+
branchCount: acc.branchCount,
|
|
1953
|
+
entryCount: acc.entryCount
|
|
1954
|
+
};
|
|
1620
1955
|
};
|
|
1621
1956
|
var assertInvariants = (state) => {
|
|
1622
1957
|
const validation = validateNode(state, state.root, 0);
|
|
1623
1958
|
if (validation.entryCount !== state.entryCount) {
|
|
1624
|
-
throw new BTreeInvariantError(
|
|
1959
|
+
throw new BTreeInvariantError(
|
|
1960
|
+
"Index entry count mismatch between tree traversal and tracked state."
|
|
1961
|
+
);
|
|
1625
1962
|
}
|
|
1626
1963
|
validateLeafLinks(state, validation.leafCount);
|
|
1627
1964
|
};
|
|
@@ -1677,7 +2014,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1677
2014
|
remove(key) {
|
|
1678
2015
|
const entry = removeFirstMatchingEntry(this.state, key);
|
|
1679
2016
|
if (entry === null) return null;
|
|
1680
|
-
return
|
|
2017
|
+
return freezeEntry(entry);
|
|
1681
2018
|
}
|
|
1682
2019
|
removeById(entryId) {
|
|
1683
2020
|
if (this.state.entryKeys === null) {
|
|
@@ -1685,7 +2022,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1685
2022
|
}
|
|
1686
2023
|
const entry = removeEntryById(this.state, entryId);
|
|
1687
2024
|
if (entry === null) return null;
|
|
1688
|
-
return
|
|
2025
|
+
return freezeEntry(entry);
|
|
1689
2026
|
}
|
|
1690
2027
|
peekById(entryId) {
|
|
1691
2028
|
if (this.state.entryKeys === null) {
|
|
@@ -1693,7 +2030,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1693
2030
|
}
|
|
1694
2031
|
const entry = peekEntryById(this.state, entryId);
|
|
1695
2032
|
if (entry === null) return null;
|
|
1696
|
-
return
|
|
2033
|
+
return freezeEntry(entry);
|
|
1697
2034
|
}
|
|
1698
2035
|
updateById(entryId, value) {
|
|
1699
2036
|
if (this.state.entryKeys === null) {
|
|
@@ -1701,30 +2038,30 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1701
2038
|
}
|
|
1702
2039
|
const entry = updateEntryById(this.state, entryId, value);
|
|
1703
2040
|
if (entry === null) return null;
|
|
1704
|
-
return
|
|
2041
|
+
return freezeEntry(entry);
|
|
1705
2042
|
}
|
|
1706
2043
|
popFirst() {
|
|
1707
2044
|
const entry = popFirstEntry(this.state);
|
|
1708
2045
|
if (entry === null) return null;
|
|
1709
|
-
return
|
|
2046
|
+
return freezeEntry(entry);
|
|
1710
2047
|
}
|
|
1711
2048
|
peekFirst() {
|
|
1712
2049
|
if (this.state.entryCount === 0) {
|
|
1713
2050
|
return null;
|
|
1714
2051
|
}
|
|
1715
|
-
return
|
|
2052
|
+
return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
|
|
1716
2053
|
}
|
|
1717
2054
|
peekLast() {
|
|
1718
2055
|
if (this.state.entryCount === 0) {
|
|
1719
2056
|
return null;
|
|
1720
2057
|
}
|
|
1721
2058
|
const leaf = this.state.rightmostLeaf;
|
|
1722
|
-
return
|
|
2059
|
+
return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
|
|
1723
2060
|
}
|
|
1724
2061
|
popLast() {
|
|
1725
2062
|
const entry = popLastEntry(this.state);
|
|
1726
2063
|
if (entry === null) return null;
|
|
1727
|
-
return
|
|
2064
|
+
return freezeEntry(entry);
|
|
1728
2065
|
}
|
|
1729
2066
|
clear() {
|
|
1730
2067
|
const emptyLeaf = createLeafNode([], null);
|
|
@@ -1734,16 +2071,9 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1734
2071
|
this.state.entryCount = 0;
|
|
1735
2072
|
this.state._cursor.leaf = emptyLeaf;
|
|
1736
2073
|
this.state._cursor.index = 0;
|
|
1737
|
-
|
|
1738
|
-
this.state.entryKeys.clear();
|
|
1739
|
-
}
|
|
2074
|
+
this.state.entryKeys?.clear();
|
|
1740
2075
|
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);
|
|
2076
|
+
resetAutoScaleToTier0(this.state);
|
|
1747
2077
|
}
|
|
1748
2078
|
}
|
|
1749
2079
|
get(key) {
|
|
@@ -1757,65 +2087,39 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1757
2087
|
findFirst(key) {
|
|
1758
2088
|
const found = findFirstMatchingUserKey(this.state, key);
|
|
1759
2089
|
if (found === null) return null;
|
|
1760
|
-
return
|
|
2090
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1761
2091
|
}
|
|
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
2092
|
findLast(key) {
|
|
1767
2093
|
const found = findLastMatchingUserKey(this.state, key);
|
|
1768
2094
|
if (found === null) return null;
|
|
1769
|
-
return
|
|
2095
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1770
2096
|
}
|
|
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
2097
|
nextHigherKey(key) {
|
|
1776
2098
|
return findNextHigherKey(this.state, key);
|
|
1777
2099
|
}
|
|
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
2100
|
nextLowerKey(key) {
|
|
1783
2101
|
return findNextLowerKey(this.state, key);
|
|
1784
2102
|
}
|
|
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
2103
|
getPairOrNextLower(key) {
|
|
1791
2104
|
const found = findPairOrNextLower(this.state, key);
|
|
1792
2105
|
if (found === null) return null;
|
|
1793
|
-
return
|
|
2106
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1794
2107
|
}
|
|
1795
|
-
/**
|
|
1796
|
-
* Returns the number of entries whose keys fall within [`startKey`, `endKey`].
|
|
1797
|
-
* Pass `options` to make either bound exclusive.
|
|
1798
|
-
*/
|
|
1799
2108
|
count(startKey, endKey, options) {
|
|
1800
2109
|
return countRangeEntries(this.state, startKey, endKey, options);
|
|
1801
2110
|
}
|
|
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
2111
|
deleteRange(startKey, endKey, options) {
|
|
1808
2112
|
return deleteRangeEntries(this.state, startKey, endKey, options);
|
|
1809
2113
|
}
|
|
1810
2114
|
range(startKey, endKey, options) {
|
|
1811
|
-
return
|
|
2115
|
+
return rangeQueryPublicEntries(this.state, startKey, endKey, options);
|
|
1812
2116
|
}
|
|
1813
2117
|
*entries() {
|
|
1814
2118
|
let leaf = this.state.leftmostLeaf;
|
|
1815
2119
|
while (leaf !== null) {
|
|
1816
2120
|
const count = leafEntryCount(leaf);
|
|
1817
2121
|
for (let i = 0; i < count; i += 1) {
|
|
1818
|
-
yield
|
|
2122
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1819
2123
|
}
|
|
1820
2124
|
leaf = leaf.next;
|
|
1821
2125
|
}
|
|
@@ -1825,7 +2129,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1825
2129
|
while (leaf !== null) {
|
|
1826
2130
|
const count = leafEntryCount(leaf);
|
|
1827
2131
|
for (let i = count - 1; i >= 0; i -= 1) {
|
|
1828
|
-
yield
|
|
2132
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1829
2133
|
}
|
|
1830
2134
|
leaf = leaf.prev;
|
|
1831
2135
|
}
|
|
@@ -1843,55 +2147,26 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1843
2147
|
[Symbol.iterator]() {
|
|
1844
2148
|
return this.entries();
|
|
1845
2149
|
}
|
|
2150
|
+
forEachRange(startKey, endKey, callback, options) {
|
|
2151
|
+
forEachRangeEntries(this.state, startKey, endKey, callback, options);
|
|
2152
|
+
}
|
|
1846
2153
|
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
|
-
}
|
|
2154
|
+
forEachEntry(this.state, callback, thisArg);
|
|
1855
2155
|
}
|
|
1856
2156
|
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;
|
|
2157
|
+
return snapshotEntries(this.state);
|
|
1868
2158
|
}
|
|
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
2159
|
clone() {
|
|
1877
|
-
const cloned = new _InMemoryBTree(
|
|
2160
|
+
const cloned = new _InMemoryBTree(
|
|
2161
|
+
buildConfigFromState(this.state)
|
|
2162
|
+
);
|
|
1878
2163
|
applyAutoScaleCapacitySnapshot(
|
|
1879
2164
|
cloned.state,
|
|
1880
2165
|
this.state.maxLeafEntries,
|
|
1881
2166
|
this.state.maxBranchChildren
|
|
1882
2167
|
);
|
|
1883
2168
|
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);
|
|
2169
|
+
cloned.putMany(collectInternalEntries(this.state));
|
|
1895
2170
|
}
|
|
1896
2171
|
return cloned;
|
|
1897
2172
|
}
|
|
@@ -1900,26 +2175,19 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1900
2175
|
}
|
|
1901
2176
|
static fromJSON(json, compareKeys) {
|
|
1902
2177
|
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));
|
|
2178
|
+
validateBTreeJSONSortOrder(json, compareKeys);
|
|
2179
|
+
const tree = new _InMemoryBTree(
|
|
2180
|
+
buildConfigFromJSON(json, compareKeys)
|
|
2181
|
+
);
|
|
1916
2182
|
applyAutoScaleCapacitySnapshot(
|
|
1917
2183
|
tree.state,
|
|
1918
2184
|
json.config.maxLeafEntries,
|
|
1919
2185
|
json.config.maxBranchChildren
|
|
1920
2186
|
);
|
|
1921
2187
|
if (json.entries.length > 0) {
|
|
1922
|
-
const pairs = new Array(
|
|
2188
|
+
const pairs = new Array(
|
|
2189
|
+
json.entries.length
|
|
2190
|
+
);
|
|
1923
2191
|
for (let i = 0; i < json.entries.length; i += 1) {
|
|
1924
2192
|
pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
|
|
1925
2193
|
}
|