@frostpillar/frostpillar-btree 0.2.6 → 0.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README-JA.md +95 -73
- package/README.md +96 -74
- package/dist/InMemoryBTree.d.ts +3 -36
- package/dist/btree/autoScale.d.ts +1 -0
- package/dist/btree/entry-lookup.d.ts +8 -0
- package/dist/btree/mutations.d.ts +1 -3
- package/dist/btree/rangeQuery.d.ts +4 -1
- package/dist/btree/rebalance-branch.d.ts +4 -0
- package/dist/btree/rebalance.d.ts +5 -2
- package/dist/btree/serialization.d.ts +3 -1
- package/dist/btree/split.d.ts +3 -0
- package/dist/btree/traversal.d.ts +7 -0
- package/dist/btree/types.d.ts +14 -3
- package/dist/{chunk-OWHENPGJ.js → chunk-UGGWGP4E.js} +659 -392
- package/dist/concurrency/ConcurrentInMemoryBTree.d.ts +3 -14
- package/dist/concurrency/coordinator.d.ts +21 -0
- package/dist/concurrency/syncLogValidation.d.ts +2 -0
- package/dist/core.cjs +659 -392
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1 -1
- package/dist/frostpillar-btree-core.min.js +1 -1
- package/dist/frostpillar-btree.min.js +1 -1
- package/dist/index.cjs +852 -542
- package/dist/index.d.ts +1 -1
- package/dist/index.js +190 -147
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -77,11 +77,12 @@ var createBranchNode = (children, parent) => {
|
|
|
77
77
|
const child = children[i];
|
|
78
78
|
child.parent = branch;
|
|
79
79
|
child.indexInParent = i;
|
|
80
|
-
const target = {
|
|
80
|
+
const target = {
|
|
81
|
+
key: void 0,
|
|
82
|
+
sequence: 0
|
|
83
|
+
};
|
|
81
84
|
if (!writeMinKeyTo(child, target)) {
|
|
82
|
-
throw new BTreeInvariantError(
|
|
83
|
-
"branch child has no min key"
|
|
84
|
-
);
|
|
85
|
+
throw new BTreeInvariantError("branch child has no min key");
|
|
85
86
|
}
|
|
86
87
|
keys.push(target);
|
|
87
88
|
}
|
|
@@ -135,7 +136,14 @@ var leafInsertAt = (leaf, logicalIndex, entry) => {
|
|
|
135
136
|
leaf.entryOffset -= 1;
|
|
136
137
|
leaf.entries[phys - 1] = entry;
|
|
137
138
|
} else {
|
|
138
|
-
leaf.entries.
|
|
139
|
+
const len = leaf.entries.length;
|
|
140
|
+
if (phys >= len) {
|
|
141
|
+
leaf.entries.push(entry);
|
|
142
|
+
} else {
|
|
143
|
+
leaf.entries.push(leaf.entries[len - 1]);
|
|
144
|
+
leaf.entries.copyWithin(phys + 1, phys, len);
|
|
145
|
+
leaf.entries[phys] = entry;
|
|
146
|
+
}
|
|
139
147
|
}
|
|
140
148
|
};
|
|
141
149
|
var leafCompact = (leaf) => {
|
|
@@ -164,7 +172,11 @@ var branchInsertAt = (branch, logicalIndex, child, key) => {
|
|
|
164
172
|
const phys = branch.childOffset + logicalIndex;
|
|
165
173
|
const count = branch.children.length - branch.childOffset;
|
|
166
174
|
if (branch.childOffset > 0 && logicalIndex < count >>> 1) {
|
|
167
|
-
branch.children.copyWithin(
|
|
175
|
+
branch.children.copyWithin(
|
|
176
|
+
branch.childOffset - 1,
|
|
177
|
+
branch.childOffset,
|
|
178
|
+
phys
|
|
179
|
+
);
|
|
168
180
|
branch.keys.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
|
|
169
181
|
branch.childOffset -= 1;
|
|
170
182
|
branch.children[phys - 1] = child;
|
|
@@ -185,8 +197,16 @@ var branchRemoveAt = (branch, physIndex) => {
|
|
|
185
197
|
const logicalIndex = physIndex - branch.childOffset;
|
|
186
198
|
const count = branch.children.length - branch.childOffset;
|
|
187
199
|
if (logicalIndex < count - 1 - logicalIndex) {
|
|
188
|
-
branch.children.copyWithin(
|
|
189
|
-
|
|
200
|
+
branch.children.copyWithin(
|
|
201
|
+
branch.childOffset + 1,
|
|
202
|
+
branch.childOffset,
|
|
203
|
+
physIndex
|
|
204
|
+
);
|
|
205
|
+
branch.keys.copyWithin(
|
|
206
|
+
branch.childOffset + 1,
|
|
207
|
+
branch.childOffset,
|
|
208
|
+
physIndex
|
|
209
|
+
);
|
|
190
210
|
branch.childOffset += 1;
|
|
191
211
|
for (let i = branch.childOffset; i <= physIndex; i += 1) {
|
|
192
212
|
branch.children[i].indexInParent = i;
|
|
@@ -216,17 +236,21 @@ var normalizeDuplicateKeyPolicy = (value) => {
|
|
|
216
236
|
return "replace";
|
|
217
237
|
}
|
|
218
238
|
if (value !== "allow" && value !== "reject" && value !== "replace") {
|
|
219
|
-
throw new BTreeValidationError(
|
|
220
|
-
`Invalid duplicateKeys option.`
|
|
221
|
-
);
|
|
239
|
+
throw new BTreeValidationError(`Invalid duplicateKeys option.`);
|
|
222
240
|
}
|
|
223
241
|
return value;
|
|
224
242
|
};
|
|
225
|
-
var
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
243
|
+
var normalizeDeleteRebalancePolicy = (value) => {
|
|
244
|
+
if (value === void 0) {
|
|
245
|
+
return "standard";
|
|
246
|
+
}
|
|
247
|
+
if (value !== "standard" && value !== "lazy") {
|
|
248
|
+
throw new BTreeValidationError(`Invalid deleteRebalancePolicy option.`);
|
|
249
|
+
}
|
|
250
|
+
return value;
|
|
251
|
+
};
|
|
252
|
+
var freezeEntry = (entry) => Object.freeze(entry);
|
|
253
|
+
var createEntry = (key, entryId, value) => Object.freeze({ key, entryId, value });
|
|
230
254
|
var isLeafNode = (node) => {
|
|
231
255
|
return node.kind === NODE_LEAF;
|
|
232
256
|
};
|
|
@@ -376,7 +400,11 @@ var hasKeyEntry = (state, key) => {
|
|
|
376
400
|
var findNextHigherKey = (state, key) => {
|
|
377
401
|
if (state.entryCount === 0) return null;
|
|
378
402
|
const compare = state.compareKeys;
|
|
379
|
-
let leaf = findLeafForKey(
|
|
403
|
+
let leaf = findLeafForKey(
|
|
404
|
+
state,
|
|
405
|
+
key,
|
|
406
|
+
Number.MAX_SAFE_INTEGER
|
|
407
|
+
);
|
|
380
408
|
let idx = upperBoundInLeaf(state, leaf, key, Number.MAX_SAFE_INTEGER);
|
|
381
409
|
while (leaf !== null) {
|
|
382
410
|
if (idx < leafEntryCount(leaf)) {
|
|
@@ -479,55 +507,57 @@ var countRangeEntries = (state, startKey, endKey, options) => {
|
|
|
479
507
|
cursorIndex = 0;
|
|
480
508
|
continue;
|
|
481
509
|
}
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
510
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
511
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
485
512
|
count += leafCount - cursorIndex;
|
|
486
513
|
cursorLeaf = cursorLeaf.next;
|
|
487
514
|
cursorIndex = 0;
|
|
488
515
|
continue;
|
|
489
516
|
}
|
|
490
|
-
|
|
491
|
-
const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
|
|
492
|
-
const limit = endBound < leafCount ? endBound : leafCount;
|
|
493
|
-
count += limit - cursorIndex;
|
|
517
|
+
count += findBoundaryEnd(state, cursorLeaf, endKey, upperExclusive, leafCount) - cursorIndex;
|
|
494
518
|
return count;
|
|
495
519
|
}
|
|
496
520
|
return count;
|
|
497
521
|
};
|
|
522
|
+
var isLastEntryInRange = (lastKey, endKey, compare, upperExclusive) => {
|
|
523
|
+
const cmp = compare(lastKey, endKey);
|
|
524
|
+
return upperExclusive ? cmp < 0 : cmp <= 0;
|
|
525
|
+
};
|
|
526
|
+
var findBoundaryEnd = (state, leaf, endKey, upperExclusive, leafCount) => {
|
|
527
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
528
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
|
|
529
|
+
return endBound < leafCount ? endBound : leafCount;
|
|
530
|
+
};
|
|
498
531
|
var RANGE_PREALLOC_THRESHOLD = 200;
|
|
499
|
-
var allocateRangeOutput = (state,
|
|
500
|
-
const firstLeafCount = leafEntryCount(
|
|
501
|
-
const firstLeafRemainder = firstLeafCount -
|
|
502
|
-
if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD &&
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
532
|
+
var allocateRangeOutput = (state, cursor, startKey, endKey, options) => {
|
|
533
|
+
const firstLeafCount = leafEntryCount(cursor.leaf);
|
|
534
|
+
const firstLeafRemainder = firstLeafCount - cursor.index;
|
|
535
|
+
if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursor.leaf.next !== null) {
|
|
536
|
+
const lastKey = leafEntryAt(cursor.leaf, firstLeafCount - 1).key;
|
|
537
|
+
if (isLastEntryInRange(lastKey, endKey, cursor.compare, cursor.upperExclusive)) {
|
|
538
|
+
return new Array(
|
|
539
|
+
countRangeEntries(state, startKey, endKey, options)
|
|
540
|
+
);
|
|
508
541
|
}
|
|
509
542
|
}
|
|
510
543
|
return [];
|
|
511
544
|
};
|
|
512
|
-
var
|
|
545
|
+
var appendLeafSlicePublic = (leaf, from, to, output, useIndexed, writeIdx) => {
|
|
513
546
|
if (useIndexed) {
|
|
514
547
|
for (let i = from; i < to; i += 1) {
|
|
515
|
-
output[writeIdx++] = leafEntryAt(leaf, i);
|
|
548
|
+
output[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
|
|
516
549
|
}
|
|
517
550
|
} else {
|
|
518
551
|
for (let i = from; i < to; i += 1) {
|
|
519
|
-
output.push(leafEntryAt(leaf, i));
|
|
552
|
+
output.push(freezeEntry(leafEntryAt(leaf, i)));
|
|
520
553
|
}
|
|
521
554
|
}
|
|
522
555
|
return writeIdx;
|
|
523
556
|
};
|
|
524
|
-
var
|
|
525
|
-
const
|
|
526
|
-
if (cursor === null) return [];
|
|
557
|
+
var collectPublicEntries = (state, cursor, endKey, output) => {
|
|
558
|
+
const { compare, upperExclusive } = cursor;
|
|
527
559
|
let cursorLeaf = cursor.leaf;
|
|
528
560
|
let cursorIndex = cursor.index;
|
|
529
|
-
const { compare, upperExclusive } = cursor;
|
|
530
|
-
const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
|
|
531
561
|
let writeIdx = 0;
|
|
532
562
|
const useIndexed = output.length > 0;
|
|
533
563
|
while (cursorLeaf !== null) {
|
|
@@ -537,24 +567,82 @@ var rangeQueryEntries = (state, startKey, endKey, options) => {
|
|
|
537
567
|
cursorIndex = 0;
|
|
538
568
|
continue;
|
|
539
569
|
}
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
570
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
571
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
572
|
+
writeIdx = appendLeafSlicePublic(
|
|
573
|
+
cursorLeaf,
|
|
574
|
+
cursorIndex,
|
|
575
|
+
leafCount,
|
|
576
|
+
output,
|
|
577
|
+
useIndexed,
|
|
578
|
+
writeIdx
|
|
579
|
+
);
|
|
544
580
|
cursorLeaf = cursorLeaf.next;
|
|
545
581
|
cursorIndex = 0;
|
|
546
582
|
continue;
|
|
547
583
|
}
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
584
|
+
const limit = findBoundaryEnd(
|
|
585
|
+
state,
|
|
586
|
+
cursorLeaf,
|
|
587
|
+
endKey,
|
|
588
|
+
upperExclusive,
|
|
589
|
+
leafCount
|
|
590
|
+
);
|
|
591
|
+
appendLeafSlicePublic(
|
|
592
|
+
cursorLeaf,
|
|
593
|
+
cursorIndex,
|
|
594
|
+
limit,
|
|
595
|
+
output,
|
|
596
|
+
useIndexed,
|
|
597
|
+
writeIdx
|
|
598
|
+
);
|
|
599
|
+
return;
|
|
553
600
|
}
|
|
601
|
+
};
|
|
602
|
+
var rangeQueryPublicEntries = (state, startKey, endKey, options) => {
|
|
603
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
604
|
+
if (cursor === null) return [];
|
|
605
|
+
const output = allocateRangeOutput(state, cursor, startKey, endKey, options);
|
|
606
|
+
collectPublicEntries(state, cursor, endKey, output);
|
|
554
607
|
return output;
|
|
555
608
|
};
|
|
609
|
+
var forEachRangeEntries = (state, startKey, endKey, callback, options) => {
|
|
610
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
611
|
+
if (cursor === null) return;
|
|
612
|
+
let cursorLeaf = cursor.leaf;
|
|
613
|
+
let cursorIndex = cursor.index;
|
|
614
|
+
const { compare, upperExclusive } = cursor;
|
|
615
|
+
while (cursorLeaf !== null) {
|
|
616
|
+
const leafCount = leafEntryCount(cursorLeaf);
|
|
617
|
+
if (cursorIndex >= leafCount) {
|
|
618
|
+
cursorLeaf = cursorLeaf.next;
|
|
619
|
+
cursorIndex = 0;
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
623
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
624
|
+
for (let i = cursorIndex; i < leafCount; i += 1) {
|
|
625
|
+
callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
|
|
626
|
+
}
|
|
627
|
+
cursorLeaf = cursorLeaf.next;
|
|
628
|
+
cursorIndex = 0;
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
631
|
+
const limit = findBoundaryEnd(
|
|
632
|
+
state,
|
|
633
|
+
cursorLeaf,
|
|
634
|
+
endKey,
|
|
635
|
+
upperExclusive,
|
|
636
|
+
leafCount
|
|
637
|
+
);
|
|
638
|
+
for (let i = cursorIndex; i < limit; i += 1) {
|
|
639
|
+
callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
|
|
640
|
+
}
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
};
|
|
556
644
|
|
|
557
|
-
// src/btree/rebalance.ts
|
|
645
|
+
// src/btree/rebalance-branch.ts
|
|
558
646
|
var updateMinKeyInAncestors = (node) => {
|
|
559
647
|
let current = node;
|
|
560
648
|
while (current.parent !== null) {
|
|
@@ -570,12 +658,9 @@ var requireParent = (node) => {
|
|
|
570
658
|
}
|
|
571
659
|
return node.parent;
|
|
572
660
|
};
|
|
573
|
-
var requireLeafNode = (node) => {
|
|
574
|
-
if (!isLeafNode(node)) throw new BTreeInvariantError("expected leaf, got branch");
|
|
575
|
-
return node;
|
|
576
|
-
};
|
|
577
661
|
var requireBranchNode = (node) => {
|
|
578
|
-
if (isLeafNode(node))
|
|
662
|
+
if (isLeafNode(node))
|
|
663
|
+
throw new BTreeInvariantError("expected branch, got leaf");
|
|
579
664
|
return node;
|
|
580
665
|
};
|
|
581
666
|
var removeChildFromBranch = (branch, childIndex) => {
|
|
@@ -584,27 +669,18 @@ var removeChildFromBranch = (branch, childIndex) => {
|
|
|
584
669
|
}
|
|
585
670
|
branchRemoveAt(branch, childIndex);
|
|
586
671
|
};
|
|
587
|
-
var detachLeafFromChain = (state, leaf) => {
|
|
588
|
-
if (leaf.prev !== null) {
|
|
589
|
-
leaf.prev.next = leaf.next;
|
|
590
|
-
} else if (leaf.next !== null) {
|
|
591
|
-
state.leftmostLeaf = leaf.next;
|
|
592
|
-
}
|
|
593
|
-
if (leaf.next !== null) {
|
|
594
|
-
leaf.next.prev = leaf.prev;
|
|
595
|
-
} else if (leaf.prev !== null) {
|
|
596
|
-
state.rightmostLeaf = leaf.prev;
|
|
597
|
-
}
|
|
598
|
-
leaf.prev = null;
|
|
599
|
-
leaf.next = null;
|
|
600
|
-
};
|
|
601
672
|
var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
|
|
602
673
|
const borrowedChild = leftSibling.children.pop();
|
|
603
|
-
if (borrowedChild === void 0)
|
|
674
|
+
if (borrowedChild === void 0)
|
|
675
|
+
throw new BTreeInvariantError("left branch borrow failed");
|
|
604
676
|
leftSibling.keys.pop();
|
|
605
677
|
borrowedChild.parent = branch;
|
|
606
|
-
const borrowedMinKey = {
|
|
607
|
-
|
|
678
|
+
const borrowedMinKey = {
|
|
679
|
+
key: void 0,
|
|
680
|
+
sequence: 0
|
|
681
|
+
};
|
|
682
|
+
if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
|
|
683
|
+
throw new BTreeInvariantError("borrowed child has no min key");
|
|
608
684
|
if (branch.childOffset > 0) {
|
|
609
685
|
branch.childOffset -= 1;
|
|
610
686
|
branch.children[branch.childOffset] = borrowedChild;
|
|
@@ -613,15 +689,20 @@ var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
|
|
|
613
689
|
} else {
|
|
614
690
|
branch.children.unshift(borrowedChild);
|
|
615
691
|
branch.keys.unshift(borrowedMinKey);
|
|
616
|
-
for (let i = 0; i < branch.children.length; i += 1)
|
|
692
|
+
for (let i = 0; i < branch.children.length; i += 1)
|
|
693
|
+
branch.children[i].indexInParent = i;
|
|
617
694
|
}
|
|
618
695
|
const parent = requireParent(branch);
|
|
619
|
-
parent.keys[branchIndex] = {
|
|
696
|
+
parent.keys[branchIndex] = {
|
|
697
|
+
key: borrowedMinKey.key,
|
|
698
|
+
sequence: borrowedMinKey.sequence
|
|
699
|
+
};
|
|
620
700
|
updateMinKeyInAncestors(branch);
|
|
621
701
|
};
|
|
622
702
|
var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
|
|
623
703
|
const shiftIdx = rightSibling.childOffset;
|
|
624
|
-
if (shiftIdx >= rightSibling.children.length)
|
|
704
|
+
if (shiftIdx >= rightSibling.children.length)
|
|
705
|
+
throw new BTreeInvariantError("right branch borrow failed");
|
|
625
706
|
const borrowedChild = rightSibling.children[shiftIdx];
|
|
626
707
|
rightSibling.childOffset += 1;
|
|
627
708
|
if (rightSibling.childOffset >= rightSibling.children.length >>> 1) {
|
|
@@ -629,8 +710,12 @@ var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
|
|
|
629
710
|
}
|
|
630
711
|
branch.children.push(borrowedChild);
|
|
631
712
|
borrowedChild.parent = branch;
|
|
632
|
-
const borrowedMinKey = {
|
|
633
|
-
|
|
713
|
+
const borrowedMinKey = {
|
|
714
|
+
key: void 0,
|
|
715
|
+
sequence: 0
|
|
716
|
+
};
|
|
717
|
+
if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
|
|
718
|
+
throw new BTreeInvariantError("borrowed child has no min key");
|
|
634
719
|
branch.keys.push(borrowedMinKey);
|
|
635
720
|
borrowedChild.indexInParent = branch.children.length - 1;
|
|
636
721
|
const parent = requireParent(branch);
|
|
@@ -683,7 +768,10 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
|
|
|
683
768
|
const parent = branch.parent;
|
|
684
769
|
if (parent === null) throw new BTreeInvariantError("branch has no parent");
|
|
685
770
|
const branchIndex = branch.indexInParent;
|
|
686
|
-
const { left: leftSibling, right: rightSibling } = findBranchSiblings(
|
|
771
|
+
const { left: leftSibling, right: rightSibling } = findBranchSiblings(
|
|
772
|
+
parent,
|
|
773
|
+
branchIndex
|
|
774
|
+
);
|
|
687
775
|
if (rightSibling !== null && branchChildCount(rightSibling) > state.minBranchChildren) {
|
|
688
776
|
borrowFromRightBranch(branch, rightSibling, branchIndex);
|
|
689
777
|
return;
|
|
@@ -702,44 +790,68 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
|
|
|
702
790
|
}
|
|
703
791
|
throw new BTreeInvariantError("no branch siblings to rebalance");
|
|
704
792
|
};
|
|
793
|
+
|
|
794
|
+
// src/btree/rebalance.ts
|
|
795
|
+
var requireLeafNode = (node) => {
|
|
796
|
+
if (!isLeafNode(node))
|
|
797
|
+
throw new BTreeInvariantError("expected leaf, got branch");
|
|
798
|
+
return node;
|
|
799
|
+
};
|
|
800
|
+
var detachLeafFromChain = (state, leaf) => {
|
|
801
|
+
if (leaf.prev !== null) {
|
|
802
|
+
leaf.prev.next = leaf.next;
|
|
803
|
+
} else if (leaf.next !== null) {
|
|
804
|
+
state.leftmostLeaf = leaf.next;
|
|
805
|
+
}
|
|
806
|
+
if (leaf.next !== null) {
|
|
807
|
+
leaf.next.prev = leaf.prev;
|
|
808
|
+
} else if (leaf.prev !== null) {
|
|
809
|
+
state.rightmostLeaf = leaf.prev;
|
|
810
|
+
}
|
|
811
|
+
leaf.prev = null;
|
|
812
|
+
leaf.next = null;
|
|
813
|
+
};
|
|
705
814
|
var mergeLeafEntries = (target, source) => {
|
|
706
815
|
if (target.entryOffset > 0) {
|
|
707
816
|
target.entries.copyWithin(0, target.entryOffset);
|
|
708
817
|
target.entries.length = target.entries.length - target.entryOffset;
|
|
709
818
|
target.entryOffset = 0;
|
|
710
819
|
}
|
|
711
|
-
|
|
712
|
-
for (let i = source.entryOffset; i < src.length; i += 1) target.entries.push(src[i]);
|
|
820
|
+
target.entries.push(...source.entries.slice(source.entryOffset));
|
|
713
821
|
};
|
|
714
|
-
var
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
state.rightmostLeaf = leaf;
|
|
719
|
-
}
|
|
720
|
-
return;
|
|
822
|
+
var applyLazyThreshold = (min) => Math.max(1, Math.ceil(min / 4));
|
|
823
|
+
var leafRebalanceThreshold = (state) => {
|
|
824
|
+
if (state.deleteRebalancePolicy === "lazy") {
|
|
825
|
+
return applyLazyThreshold(state.minLeafEntries);
|
|
721
826
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
const
|
|
726
|
-
const
|
|
727
|
-
|
|
827
|
+
return state.minLeafEntries;
|
|
828
|
+
};
|
|
829
|
+
var findLeafSiblings = (parent, leafIndex) => {
|
|
830
|
+
const left = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
|
|
831
|
+
const right = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
|
|
832
|
+
return { left, right };
|
|
833
|
+
};
|
|
834
|
+
var tryBorrowFromLeafSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
|
|
728
835
|
if (rightSibling !== null && leafEntryCount(rightSibling) > state.minLeafEntries) {
|
|
729
836
|
const borrowed = leafShiftEntry(rightSibling);
|
|
730
|
-
if (borrowed === void 0)
|
|
837
|
+
if (borrowed === void 0)
|
|
838
|
+
throw new BTreeInvariantError("right leaf borrow failed");
|
|
731
839
|
leaf.entries.push(borrowed);
|
|
732
840
|
writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
|
|
733
|
-
return;
|
|
841
|
+
return true;
|
|
734
842
|
}
|
|
735
843
|
if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
|
|
736
844
|
const borrowed = leftSibling.entries.pop();
|
|
737
|
-
if (borrowed === void 0)
|
|
845
|
+
if (borrowed === void 0)
|
|
846
|
+
throw new BTreeInvariantError("left leaf borrow failed");
|
|
738
847
|
leafUnshiftEntry(leaf, borrowed);
|
|
739
848
|
parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
|
|
740
849
|
updateMinKeyInAncestors(leaf);
|
|
741
|
-
return;
|
|
850
|
+
return true;
|
|
742
851
|
}
|
|
852
|
+
return false;
|
|
853
|
+
};
|
|
854
|
+
var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
|
|
743
855
|
if (leftSibling !== null) {
|
|
744
856
|
mergeLeafEntries(leftSibling, leaf);
|
|
745
857
|
detachLeafFromChain(state, leaf);
|
|
@@ -756,6 +868,24 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
|
756
868
|
}
|
|
757
869
|
throw new BTreeInvariantError("no leaf siblings to rebalance");
|
|
758
870
|
};
|
|
871
|
+
var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
872
|
+
if (leaf === state.root) {
|
|
873
|
+
if (state.entryCount === 0) {
|
|
874
|
+
state.leftmostLeaf = leaf;
|
|
875
|
+
state.rightmostLeaf = leaf;
|
|
876
|
+
}
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
|
|
880
|
+
const parent = leaf.parent;
|
|
881
|
+
if (parent === null)
|
|
882
|
+
throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
|
|
883
|
+
const leafIndex = leaf.indexInParent;
|
|
884
|
+
const { left, right } = findLeafSiblings(parent, leafIndex);
|
|
885
|
+
if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
|
|
886
|
+
return;
|
|
887
|
+
mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
|
|
888
|
+
};
|
|
759
889
|
|
|
760
890
|
// src/btree/deleteRange.ts
|
|
761
891
|
var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
@@ -768,16 +898,16 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
|
768
898
|
}
|
|
769
899
|
return { leaf, idx };
|
|
770
900
|
};
|
|
771
|
-
var findRemoveEnd = (state, leaf,
|
|
901
|
+
var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
|
|
772
902
|
const count = leafEntryCount(leaf);
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
|
|
778
|
-
removeEnd += 1;
|
|
903
|
+
const lastEntry = leafEntryAt(leaf, count - 1);
|
|
904
|
+
const cmpLast = state.compareKeys(lastEntry.key, endKey);
|
|
905
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
906
|
+
return count;
|
|
779
907
|
}
|
|
780
|
-
|
|
908
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
909
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
|
|
910
|
+
return endBound < count ? endBound : count;
|
|
781
911
|
};
|
|
782
912
|
var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
783
913
|
if (state.entryKeys !== null) {
|
|
@@ -794,10 +924,12 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
|
794
924
|
updateMinKeyInAncestors(leaf);
|
|
795
925
|
}
|
|
796
926
|
const countAfterSplice = leafEntryCount(leaf);
|
|
927
|
+
const rebalThreshold = leafRebalanceThreshold(state);
|
|
797
928
|
let safetyGuard = state.minLeafEntries + 4;
|
|
798
|
-
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) <
|
|
929
|
+
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
|
|
799
930
|
rebalanceAfterLeafRemoval(state, leaf);
|
|
800
|
-
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
931
|
+
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
932
|
+
break;
|
|
801
933
|
safetyGuard -= 1;
|
|
802
934
|
}
|
|
803
935
|
if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
|
|
@@ -825,10 +957,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
825
957
|
}
|
|
826
958
|
if (idx >= leafEntryCount(leaf)) break;
|
|
827
959
|
const count = leafEntryCount(leaf);
|
|
828
|
-
const removeEnd = findRemoveEnd(state, leaf,
|
|
960
|
+
const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
|
|
829
961
|
const removeCount = removeEnd - idx;
|
|
830
962
|
if (removeCount === 0) break;
|
|
831
|
-
const countAfterSplice = spliceLeafAndRebalance(
|
|
963
|
+
const countAfterSplice = spliceLeafAndRebalance(
|
|
964
|
+
state,
|
|
965
|
+
leaf,
|
|
966
|
+
idx,
|
|
967
|
+
removeCount
|
|
968
|
+
);
|
|
832
969
|
deleted += removeCount;
|
|
833
970
|
if (removeEnd < count) break;
|
|
834
971
|
if (!isLeafStillValid(state, leaf)) {
|
|
@@ -874,25 +1011,42 @@ var computeNextAutoScaleThreshold = (entryCount) => {
|
|
|
874
1011
|
}
|
|
875
1012
|
return Number.MAX_SAFE_INTEGER;
|
|
876
1013
|
};
|
|
877
|
-
var
|
|
878
|
-
if (typeof config.compareKeys !== "function") {
|
|
879
|
-
throw new BTreeValidationError("compareKeys must be a function.");
|
|
880
|
-
}
|
|
881
|
-
const autoScale = config.autoScale === true;
|
|
1014
|
+
var resolveInitialCapacity = (config, autoScale) => {
|
|
882
1015
|
if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
|
|
883
|
-
throw new BTreeValidationError(
|
|
1016
|
+
throw new BTreeValidationError(
|
|
1017
|
+
"autoScale conflicts with explicit capacity."
|
|
1018
|
+
);
|
|
884
1019
|
}
|
|
885
|
-
let maxLeafEntries;
|
|
886
|
-
let maxBranchChildren;
|
|
887
1020
|
if (autoScale) {
|
|
888
1021
|
const tier = computeAutoScaleTier(0);
|
|
889
|
-
maxLeafEntries
|
|
890
|
-
maxBranchChildren = tier.maxBranch;
|
|
891
|
-
} else {
|
|
892
|
-
maxLeafEntries = normalizeNodeCapacity(config.maxLeafEntries, "maxLeafEntries", DEFAULT_MAX_LEAF_ENTRIES);
|
|
893
|
-
maxBranchChildren = normalizeNodeCapacity(config.maxBranchChildren, "maxBranchChildren", DEFAULT_MAX_BRANCH_CHILDREN);
|
|
1022
|
+
return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
|
|
894
1023
|
}
|
|
1024
|
+
return {
|
|
1025
|
+
maxLeafEntries: normalizeNodeCapacity(
|
|
1026
|
+
config.maxLeafEntries,
|
|
1027
|
+
"maxLeafEntries",
|
|
1028
|
+
DEFAULT_MAX_LEAF_ENTRIES
|
|
1029
|
+
),
|
|
1030
|
+
maxBranchChildren: normalizeNodeCapacity(
|
|
1031
|
+
config.maxBranchChildren,
|
|
1032
|
+
"maxBranchChildren",
|
|
1033
|
+
DEFAULT_MAX_BRANCH_CHILDREN
|
|
1034
|
+
)
|
|
1035
|
+
};
|
|
1036
|
+
};
|
|
1037
|
+
var createInitialState = (config) => {
|
|
1038
|
+
if (typeof config.compareKeys !== "function") {
|
|
1039
|
+
throw new BTreeValidationError("compareKeys must be a function.");
|
|
1040
|
+
}
|
|
1041
|
+
const autoScale = config.autoScale === true;
|
|
1042
|
+
const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
|
|
1043
|
+
config,
|
|
1044
|
+
autoScale
|
|
1045
|
+
);
|
|
895
1046
|
const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
|
|
1047
|
+
const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
|
|
1048
|
+
config.deleteRebalancePolicy
|
|
1049
|
+
);
|
|
896
1050
|
const emptyLeaf = createLeafNode([], null);
|
|
897
1051
|
return {
|
|
898
1052
|
compareKeys: config.compareKeys,
|
|
@@ -908,6 +1062,7 @@ var createInitialState = (config) => {
|
|
|
908
1062
|
nextSequence: 0,
|
|
909
1063
|
entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
|
|
910
1064
|
autoScale,
|
|
1065
|
+
deleteRebalancePolicy,
|
|
911
1066
|
_nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
|
|
912
1067
|
_cursor: { leaf: emptyLeaf, index: 0 }
|
|
913
1068
|
};
|
|
@@ -923,7 +1078,9 @@ var maybeAutoScale = (state) => {
|
|
|
923
1078
|
state.maxBranchChildren = maxBranch;
|
|
924
1079
|
state.minBranchChildren = minOccupancy(maxBranch);
|
|
925
1080
|
}
|
|
926
|
-
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1081
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1082
|
+
state.entryCount
|
|
1083
|
+
);
|
|
927
1084
|
};
|
|
928
1085
|
var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
|
|
929
1086
|
if (!state.autoScale) {
|
|
@@ -950,6 +1107,14 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
|
|
|
950
1107
|
state.minLeafEntries = minOccupancy(normalizedLeaf);
|
|
951
1108
|
state.minBranchChildren = minOccupancy(normalizedBranch);
|
|
952
1109
|
};
|
|
1110
|
+
var resetAutoScaleToTier0 = (state) => {
|
|
1111
|
+
const tier = computeAutoScaleTier(0);
|
|
1112
|
+
state.maxLeafEntries = tier.maxLeaf;
|
|
1113
|
+
state.maxBranchChildren = tier.maxBranch;
|
|
1114
|
+
state.minLeafEntries = minOccupancy(tier.maxLeaf);
|
|
1115
|
+
state.minBranchChildren = minOccupancy(tier.maxBranch);
|
|
1116
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
1117
|
+
};
|
|
953
1118
|
|
|
954
1119
|
// src/btree/bulkLoad.ts
|
|
955
1120
|
var computeChunkBoundaries = (total, max, min) => {
|
|
@@ -970,7 +1135,11 @@ var computeChunkBoundaries = (total, max, min) => {
|
|
|
970
1135
|
return boundaries;
|
|
971
1136
|
};
|
|
972
1137
|
var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
973
|
-
const boundaries = computeChunkBoundaries(
|
|
1138
|
+
const boundaries = computeChunkBoundaries(
|
|
1139
|
+
entries.length,
|
|
1140
|
+
state.maxLeafEntries,
|
|
1141
|
+
state.minLeafEntries
|
|
1142
|
+
);
|
|
974
1143
|
const leaves = new Array(boundaries.length);
|
|
975
1144
|
let chunkStart = 0;
|
|
976
1145
|
for (let c = 0; c < boundaries.length; c += 1) {
|
|
@@ -978,7 +1147,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
|
978
1147
|
const chunk = new Array(chunkEnd - chunkStart);
|
|
979
1148
|
for (let i = chunkStart; i < chunkEnd; i += 1) {
|
|
980
1149
|
const seq = baseSequence + i;
|
|
981
|
-
chunk[i - chunkStart] =
|
|
1150
|
+
chunk[i - chunkStart] = createEntry(
|
|
1151
|
+
entries[i].key,
|
|
1152
|
+
seq,
|
|
1153
|
+
entries[i].value
|
|
1154
|
+
);
|
|
982
1155
|
ids[i] = seq;
|
|
983
1156
|
if (state.entryKeys !== null) {
|
|
984
1157
|
state.entryKeys.set(seq, entries[i].key);
|
|
@@ -1012,11 +1185,18 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
1012
1185
|
} else {
|
|
1013
1186
|
let currentLevel = leaves;
|
|
1014
1187
|
while (currentLevel.length > 1) {
|
|
1015
|
-
const bounds = computeChunkBoundaries(
|
|
1188
|
+
const bounds = computeChunkBoundaries(
|
|
1189
|
+
currentLevel.length,
|
|
1190
|
+
state.maxBranchChildren,
|
|
1191
|
+
state.minBranchChildren
|
|
1192
|
+
);
|
|
1016
1193
|
const nextLevel = new Array(bounds.length);
|
|
1017
1194
|
let start = 0;
|
|
1018
1195
|
for (let b = 0; b < bounds.length; b += 1) {
|
|
1019
|
-
nextLevel[b] = createBranchNode(
|
|
1196
|
+
nextLevel[b] = createBranchNode(
|
|
1197
|
+
currentLevel.slice(start, bounds[b]),
|
|
1198
|
+
null
|
|
1199
|
+
);
|
|
1020
1200
|
start = bounds[b];
|
|
1021
1201
|
}
|
|
1022
1202
|
currentLevel = nextLevel;
|
|
@@ -1027,9 +1207,12 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
1027
1207
|
return ids;
|
|
1028
1208
|
};
|
|
1029
1209
|
|
|
1030
|
-
// src/btree/
|
|
1210
|
+
// src/btree/split.ts
|
|
1031
1211
|
var insertChildAfter = (state, parent, existingChild, childToInsert) => {
|
|
1032
|
-
const newChildMinKey = {
|
|
1212
|
+
const newChildMinKey = {
|
|
1213
|
+
key: void 0,
|
|
1214
|
+
sequence: 0
|
|
1215
|
+
};
|
|
1033
1216
|
if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
|
|
1034
1217
|
throw new BTreeInvariantError("inserted child has no min key");
|
|
1035
1218
|
}
|
|
@@ -1067,7 +1250,10 @@ var splitLeaf = (state, leaf) => {
|
|
|
1067
1250
|
var splitBranch = (state, branch) => {
|
|
1068
1251
|
branchCompact(branch);
|
|
1069
1252
|
const splitAt = Math.ceil(branch.children.length / 2);
|
|
1070
|
-
const sibling = createBranchNode(
|
|
1253
|
+
const sibling = createBranchNode(
|
|
1254
|
+
branch.children.splice(splitAt),
|
|
1255
|
+
branch.parent
|
|
1256
|
+
);
|
|
1071
1257
|
branch.keys.splice(splitAt);
|
|
1072
1258
|
if (branch.parent === null) {
|
|
1073
1259
|
state.root = createBranchNode([branch, sibling], null);
|
|
@@ -1075,42 +1261,125 @@ var splitBranch = (state, branch) => {
|
|
|
1075
1261
|
}
|
|
1076
1262
|
insertChildAfter(state, branch.parent, branch, sibling);
|
|
1077
1263
|
};
|
|
1264
|
+
|
|
1265
|
+
// src/btree/entry-lookup.ts
|
|
1266
|
+
var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
1267
|
+
const targetLeaf = findLeafForKey(state, userKey, sequence);
|
|
1268
|
+
const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
|
|
1269
|
+
if (index >= leafEntryCount(targetLeaf)) return null;
|
|
1270
|
+
const entry = leafEntryAt(targetLeaf, index);
|
|
1271
|
+
if (entry.entryId !== sequence) return null;
|
|
1272
|
+
return { leaf: targetLeaf, index };
|
|
1273
|
+
};
|
|
1274
|
+
var peekEntryById = (state, entryId) => {
|
|
1275
|
+
if (state.entryKeys === null) {
|
|
1276
|
+
throw new BTreeInvariantError(
|
|
1277
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1280
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1281
|
+
if (userKey === void 0) return null;
|
|
1282
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1283
|
+
if (found === null) return null;
|
|
1284
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1285
|
+
return entry;
|
|
1286
|
+
};
|
|
1287
|
+
var updateEntryById = (state, entryId, newValue) => {
|
|
1288
|
+
if (state.entryKeys === null) {
|
|
1289
|
+
throw new BTreeInvariantError(
|
|
1290
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1294
|
+
if (userKey === void 0) return null;
|
|
1295
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1296
|
+
if (found === null) return null;
|
|
1297
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1298
|
+
const updated = createEntry(entry.key, entry.entryId, newValue);
|
|
1299
|
+
found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
|
|
1300
|
+
return updated;
|
|
1301
|
+
};
|
|
1302
|
+
var removeEntryById = (state, entryId) => {
|
|
1303
|
+
if (state.entryKeys === null) {
|
|
1304
|
+
throw new BTreeInvariantError(
|
|
1305
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1306
|
+
);
|
|
1307
|
+
}
|
|
1308
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1309
|
+
if (userKey === void 0) return null;
|
|
1310
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1311
|
+
if (found === null) return null;
|
|
1312
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1313
|
+
leafRemoveAt(found.leaf, found.index);
|
|
1314
|
+
state.entryCount -= 1;
|
|
1315
|
+
state.entryKeys.delete(entryId);
|
|
1316
|
+
if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
|
|
1317
|
+
updateMinKeyInAncestors(found.leaf);
|
|
1318
|
+
}
|
|
1319
|
+
if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
|
|
1320
|
+
rebalanceAfterLeafRemoval(state, found.leaf);
|
|
1321
|
+
}
|
|
1322
|
+
return entry;
|
|
1323
|
+
};
|
|
1324
|
+
|
|
1325
|
+
// src/btree/mutations.ts
|
|
1326
|
+
var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
|
|
1327
|
+
if (state.duplicateKeys === "allow") return null;
|
|
1328
|
+
if (insertAt > 0) {
|
|
1329
|
+
const candidate = leafEntryAt(targetLeaf, insertAt - 1);
|
|
1330
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1331
|
+
return {
|
|
1332
|
+
leaf: targetLeaf,
|
|
1333
|
+
physIndex: targetLeaf.entryOffset + insertAt - 1,
|
|
1334
|
+
entry: candidate
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
} else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
|
|
1338
|
+
const prevLeaf = targetLeaf.prev;
|
|
1339
|
+
const prevCount = leafEntryCount(prevLeaf);
|
|
1340
|
+
const candidate = leafEntryAt(prevLeaf, prevCount - 1);
|
|
1341
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1342
|
+
return {
|
|
1343
|
+
leaf: prevLeaf,
|
|
1344
|
+
physIndex: prevLeaf.entryOffset + prevCount - 1,
|
|
1345
|
+
entry: candidate
|
|
1346
|
+
};
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
return null;
|
|
1350
|
+
};
|
|
1078
1351
|
var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
|
|
1079
1352
|
const sequence = state.nextSequence;
|
|
1080
1353
|
const insertAt = upperBoundInLeaf(state, targetLeaf, key, sequence);
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
if (
|
|
1084
|
-
|
|
1085
|
-
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1086
|
-
existingEntry = candidate;
|
|
1087
|
-
}
|
|
1088
|
-
} else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
|
|
1089
|
-
const prevLeaf = targetLeaf.prev;
|
|
1090
|
-
const candidate = leafEntryAt(prevLeaf, leafEntryCount(prevLeaf) - 1);
|
|
1091
|
-
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1092
|
-
existingEntry = candidate;
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
if (existingEntry !== null) {
|
|
1096
|
-
if (state.duplicateKeys === "reject") {
|
|
1097
|
-
throw new BTreeValidationError("Duplicate key rejected.");
|
|
1098
|
-
}
|
|
1099
|
-
existingEntry.value = value;
|
|
1100
|
-
return existingEntry.entryId;
|
|
1354
|
+
const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
|
|
1355
|
+
if (dup !== null) {
|
|
1356
|
+
if (state.duplicateKeys === "reject") {
|
|
1357
|
+
throw new BTreeValidationError("Duplicate key rejected.");
|
|
1101
1358
|
}
|
|
1359
|
+
dup.leaf.entries[dup.physIndex] = createEntry(
|
|
1360
|
+
dup.entry.key,
|
|
1361
|
+
dup.entry.entryId,
|
|
1362
|
+
value
|
|
1363
|
+
);
|
|
1364
|
+
return dup.entry.entryId;
|
|
1102
1365
|
}
|
|
1103
1366
|
if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
|
|
1104
1367
|
throw new BTreeValidationError("Sequence overflow.");
|
|
1105
1368
|
}
|
|
1106
1369
|
state.nextSequence += 1;
|
|
1107
|
-
leafInsertAt(
|
|
1370
|
+
leafInsertAt(
|
|
1371
|
+
targetLeaf,
|
|
1372
|
+
insertAt,
|
|
1373
|
+
createEntry(key, sequence, value)
|
|
1374
|
+
);
|
|
1108
1375
|
state.entryCount += 1;
|
|
1109
1376
|
if (state.entryKeys !== null) {
|
|
1110
1377
|
state.entryKeys.set(sequence, key);
|
|
1111
1378
|
}
|
|
1112
|
-
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1113
|
-
|
|
1379
|
+
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1380
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1381
|
+
if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
|
|
1382
|
+
splitLeaf(state, targetLeaf);
|
|
1114
1383
|
maybeAutoScale(state);
|
|
1115
1384
|
return sequence;
|
|
1116
1385
|
};
|
|
@@ -1121,7 +1390,8 @@ var putEntry = (state, key, value) => {
|
|
|
1121
1390
|
var popFirstEntry = (state) => {
|
|
1122
1391
|
if (state.entryCount === 0) return null;
|
|
1123
1392
|
const firstEntry = leafShiftEntry(state.leftmostLeaf);
|
|
1124
|
-
if (firstEntry === void 0)
|
|
1393
|
+
if (firstEntry === void 0)
|
|
1394
|
+
throw new BTreeInvariantError("leftmost leaf empty but count > 0");
|
|
1125
1395
|
state.entryCount -= 1;
|
|
1126
1396
|
if (state.entryKeys !== null) {
|
|
1127
1397
|
state.entryKeys.delete(firstEntry.entryId);
|
|
@@ -1137,7 +1407,8 @@ var popFirstEntry = (state) => {
|
|
|
1137
1407
|
var popLastEntry = (state) => {
|
|
1138
1408
|
if (state.entryCount === 0) return null;
|
|
1139
1409
|
const lastEntry = leafPopEntry(state.rightmostLeaf);
|
|
1140
|
-
if (lastEntry === void 0)
|
|
1410
|
+
if (lastEntry === void 0)
|
|
1411
|
+
throw new BTreeInvariantError("rightmost leaf empty but count > 0");
|
|
1141
1412
|
state.entryCount -= 1;
|
|
1142
1413
|
if (state.entryKeys !== null) {
|
|
1143
1414
|
state.entryKeys.delete(lastEntry.entryId);
|
|
@@ -1156,64 +1427,14 @@ var removeFirstMatchingEntry = (state, key) => {
|
|
|
1156
1427
|
leafRemoveAt(targetLeaf, removeAt);
|
|
1157
1428
|
state.entryCount -= 1;
|
|
1158
1429
|
if (state.entryKeys !== null) {
|
|
1159
|
-
state.entryKeys.delete(targetEntry.entryId);
|
|
1160
|
-
}
|
|
1161
|
-
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
|
|
1162
|
-
if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
|
|
1163
|
-
rebalanceAfterLeafRemoval(state, targetLeaf);
|
|
1164
|
-
}
|
|
1165
|
-
return targetEntry;
|
|
1166
|
-
};
|
|
1167
|
-
var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
1168
|
-
const targetLeaf = findLeafForKey(state, userKey, sequence);
|
|
1169
|
-
const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
|
|
1170
|
-
if (index >= leafEntryCount(targetLeaf)) return null;
|
|
1171
|
-
const entry = leafEntryAt(targetLeaf, index);
|
|
1172
|
-
if (entry.entryId !== sequence) return null;
|
|
1173
|
-
return { leaf: targetLeaf, index };
|
|
1174
|
-
};
|
|
1175
|
-
var removeEntryById = (state, entryId) => {
|
|
1176
|
-
if (state.entryKeys === null) {
|
|
1177
|
-
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1178
|
-
}
|
|
1179
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1180
|
-
if (userKey === void 0) return null;
|
|
1181
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1182
|
-
if (found === null) return null;
|
|
1183
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1184
|
-
leafRemoveAt(found.leaf, found.index);
|
|
1185
|
-
state.entryCount -= 1;
|
|
1186
|
-
state.entryKeys.delete(entryId);
|
|
1187
|
-
if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
|
|
1188
|
-
updateMinKeyInAncestors(found.leaf);
|
|
1189
|
-
}
|
|
1190
|
-
if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
|
|
1191
|
-
rebalanceAfterLeafRemoval(state, found.leaf);
|
|
1192
|
-
}
|
|
1193
|
-
return entry;
|
|
1194
|
-
};
|
|
1195
|
-
var peekEntryById = (state, entryId) => {
|
|
1196
|
-
if (state.entryKeys === null) {
|
|
1197
|
-
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1198
|
-
}
|
|
1199
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1200
|
-
if (userKey === void 0) return null;
|
|
1201
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1202
|
-
if (found === null) return null;
|
|
1203
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1204
|
-
return entry;
|
|
1205
|
-
};
|
|
1206
|
-
var updateEntryById = (state, entryId, newValue) => {
|
|
1207
|
-
if (state.entryKeys === null) {
|
|
1208
|
-
throw new BTreeInvariantError("entryKeys lookup map is not enabled on this tree.");
|
|
1430
|
+
state.entryKeys.delete(targetEntry.entryId);
|
|
1209
1431
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
return entry;
|
|
1432
|
+
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1433
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1434
|
+
if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
|
|
1435
|
+
rebalanceAfterLeafRemoval(state, targetLeaf);
|
|
1436
|
+
}
|
|
1437
|
+
return targetEntry;
|
|
1217
1438
|
};
|
|
1218
1439
|
var putManyEntries = (state, entries) => {
|
|
1219
1440
|
if (entries.length === 0) return [];
|
|
@@ -1221,7 +1442,9 @@ var putManyEntries = (state, entries) => {
|
|
|
1221
1442
|
for (let i = 1; i < entries.length; i += 1) {
|
|
1222
1443
|
const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
|
|
1223
1444
|
if (cmp > 0) {
|
|
1224
|
-
throw new BTreeValidationError(
|
|
1445
|
+
throw new BTreeValidationError(
|
|
1446
|
+
"putMany: entries not in ascending order."
|
|
1447
|
+
);
|
|
1225
1448
|
}
|
|
1226
1449
|
if (strictlyAscending && cmp === 0) {
|
|
1227
1450
|
throw new BTreeValidationError(
|
|
@@ -1234,7 +1457,12 @@ var putManyEntries = (state, entries) => {
|
|
|
1234
1457
|
let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
|
|
1235
1458
|
for (let i = 0; i < entries.length; i += 1) {
|
|
1236
1459
|
const entry = entries[i];
|
|
1237
|
-
const targetLeaf = findLeafFromHint(
|
|
1460
|
+
const targetLeaf = findLeafFromHint(
|
|
1461
|
+
state,
|
|
1462
|
+
hintLeaf,
|
|
1463
|
+
entry.key,
|
|
1464
|
+
state.nextSequence
|
|
1465
|
+
);
|
|
1238
1466
|
ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
|
|
1239
1467
|
hintLeaf = targetLeaf;
|
|
1240
1468
|
}
|
|
@@ -1249,7 +1477,8 @@ var buildConfigFromState = (state) => {
|
|
|
1249
1477
|
compareKeys: state.compareKeys,
|
|
1250
1478
|
duplicateKeys: state.duplicateKeys,
|
|
1251
1479
|
enableEntryIdLookup: state.entryKeys !== null,
|
|
1252
|
-
autoScale: state.autoScale
|
|
1480
|
+
autoScale: state.autoScale,
|
|
1481
|
+
deleteRebalancePolicy: state.deleteRebalancePolicy
|
|
1253
1482
|
};
|
|
1254
1483
|
if (!state.autoScale) {
|
|
1255
1484
|
config.maxLeafEntries = state.maxLeafEntries;
|
|
@@ -1269,17 +1498,17 @@ var serializeToJSON = (state) => {
|
|
|
1269
1498
|
}
|
|
1270
1499
|
leaf = leaf.next;
|
|
1271
1500
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
enableEntryIdLookup: state.entryKeys !== null,
|
|
1279
|
-
autoScale: state.autoScale
|
|
1280
|
-
},
|
|
1281
|
-
entries
|
|
1501
|
+
const config = {
|
|
1502
|
+
maxLeafEntries: state.maxLeafEntries,
|
|
1503
|
+
maxBranchChildren: state.maxBranchChildren,
|
|
1504
|
+
duplicateKeys: state.duplicateKeys,
|
|
1505
|
+
enableEntryIdLookup: state.entryKeys !== null,
|
|
1506
|
+
autoScale: state.autoScale
|
|
1282
1507
|
};
|
|
1508
|
+
if (state.deleteRebalancePolicy !== "standard") {
|
|
1509
|
+
config.deleteRebalancePolicy = state.deleteRebalancePolicy;
|
|
1510
|
+
}
|
|
1511
|
+
return { version: 1, config, entries };
|
|
1283
1512
|
};
|
|
1284
1513
|
var MAX_SERIALIZED_ENTRIES = 1e6;
|
|
1285
1514
|
var validateStructure = (json) => {
|
|
@@ -1342,13 +1571,28 @@ var validateBTreeJSON = (json) => {
|
|
|
1342
1571
|
validateStructure(json);
|
|
1343
1572
|
validateConfig(json.config);
|
|
1344
1573
|
};
|
|
1574
|
+
var validateBTreeJSONSortOrder = (json, compareKeys) => {
|
|
1575
|
+
const strict = json.config.duplicateKeys !== "allow";
|
|
1576
|
+
for (let i = 1; i < json.entries.length; i += 1) {
|
|
1577
|
+
const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
|
|
1578
|
+
if (cmp > 0) {
|
|
1579
|
+
throw new BTreeValidationError("fromJSON: entries not sorted.");
|
|
1580
|
+
}
|
|
1581
|
+
if (strict && cmp === 0) {
|
|
1582
|
+
throw new BTreeValidationError(
|
|
1583
|
+
'fromJSON: duplicate keys require duplicateKeys "allow".'
|
|
1584
|
+
);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
};
|
|
1345
1588
|
var buildConfigFromJSON = (json, compareKeys) => {
|
|
1346
1589
|
const cfg = json.config;
|
|
1347
1590
|
const config = {
|
|
1348
1591
|
compareKeys,
|
|
1349
1592
|
duplicateKeys: cfg.duplicateKeys,
|
|
1350
1593
|
enableEntryIdLookup: cfg.enableEntryIdLookup,
|
|
1351
|
-
autoScale: cfg.autoScale
|
|
1594
|
+
autoScale: cfg.autoScale,
|
|
1595
|
+
deleteRebalancePolicy: cfg.deleteRebalancePolicy
|
|
1352
1596
|
};
|
|
1353
1597
|
if (!cfg.autoScale) {
|
|
1354
1598
|
config.maxLeafEntries = cfg.maxLeafEntries;
|
|
@@ -1357,6 +1601,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
|
|
|
1357
1601
|
return config;
|
|
1358
1602
|
};
|
|
1359
1603
|
|
|
1604
|
+
// src/btree/traversal.ts
|
|
1605
|
+
var snapshotEntries = (state) => {
|
|
1606
|
+
const result = new Array(state.entryCount);
|
|
1607
|
+
let leaf = state.leftmostLeaf;
|
|
1608
|
+
let writeIdx = 0;
|
|
1609
|
+
while (leaf !== null) {
|
|
1610
|
+
const count = leafEntryCount(leaf);
|
|
1611
|
+
for (let i = 0; i < count; i += 1) {
|
|
1612
|
+
result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
|
|
1613
|
+
}
|
|
1614
|
+
leaf = leaf.next;
|
|
1615
|
+
}
|
|
1616
|
+
return result;
|
|
1617
|
+
};
|
|
1618
|
+
var collectInternalEntries = (state) => {
|
|
1619
|
+
const result = new Array(state.entryCount);
|
|
1620
|
+
let leaf = state.leftmostLeaf;
|
|
1621
|
+
let writeIdx = 0;
|
|
1622
|
+
while (leaf !== null) {
|
|
1623
|
+
const count = leafEntryCount(leaf);
|
|
1624
|
+
for (let i = 0; i < count; i += 1) {
|
|
1625
|
+
result[writeIdx++] = leafEntryAt(leaf, i);
|
|
1626
|
+
}
|
|
1627
|
+
leaf = leaf.next;
|
|
1628
|
+
}
|
|
1629
|
+
return result;
|
|
1630
|
+
};
|
|
1631
|
+
var forEachEntry = (state, callback, thisArg) => {
|
|
1632
|
+
let leaf = state.leftmostLeaf;
|
|
1633
|
+
while (leaf !== null) {
|
|
1634
|
+
const count = leafEntryCount(leaf);
|
|
1635
|
+
for (let i = 0; i < count; i += 1) {
|
|
1636
|
+
callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
|
|
1637
|
+
}
|
|
1638
|
+
leaf = leaf.next;
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1641
|
+
|
|
1360
1642
|
// src/btree/integrity-helpers.ts
|
|
1361
1643
|
var nodeMinKey = (node) => {
|
|
1362
1644
|
if (isLeafNode(node)) {
|
|
@@ -1365,7 +1647,10 @@ var nodeMinKey = (node) => {
|
|
|
1365
1647
|
return { key: e.key, sequence: e.entryId };
|
|
1366
1648
|
}
|
|
1367
1649
|
if (node.childOffset >= node.keys.length) return null;
|
|
1368
|
-
return {
|
|
1650
|
+
return {
|
|
1651
|
+
key: node.keys[node.childOffset].key,
|
|
1652
|
+
sequence: node.keys[node.childOffset].sequence
|
|
1653
|
+
};
|
|
1369
1654
|
};
|
|
1370
1655
|
var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
|
|
1371
1656
|
const cmp = comparator(leftKey, rightKey);
|
|
@@ -1387,9 +1672,7 @@ var getNodeMaxKey = (node) => {
|
|
|
1387
1672
|
};
|
|
1388
1673
|
var validateComparatorResult = (result) => {
|
|
1389
1674
|
if (!Number.isFinite(result)) {
|
|
1390
|
-
throw new BTreeValidationError(
|
|
1391
|
-
"compareKeys must return a finite number."
|
|
1392
|
-
);
|
|
1675
|
+
throw new BTreeValidationError("compareKeys must return a finite number.");
|
|
1393
1676
|
}
|
|
1394
1677
|
return result;
|
|
1395
1678
|
};
|
|
@@ -1449,6 +1732,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
|
|
|
1449
1732
|
throw error;
|
|
1450
1733
|
}
|
|
1451
1734
|
};
|
|
1735
|
+
var validateAdjacentLeafOrdering = (state, previous, cursor) => {
|
|
1736
|
+
const prevMax = getNodeMaxKey(previous);
|
|
1737
|
+
const currentMin = nodeMinKey(cursor);
|
|
1738
|
+
if (prevMax === null || currentMin === null) {
|
|
1739
|
+
throw new BTreeInvariantError(
|
|
1740
|
+
"Non-empty tree leaf chain contains empty leaf node."
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
if (compareNodeKeys(
|
|
1744
|
+
state.compareKeys,
|
|
1745
|
+
prevMax.key,
|
|
1746
|
+
prevMax.sequence,
|
|
1747
|
+
currentMin.key,
|
|
1748
|
+
currentMin.sequence
|
|
1749
|
+
) > 0) {
|
|
1750
|
+
throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
|
|
1751
|
+
}
|
|
1752
|
+
const prevCount = leafEntryCount(previous);
|
|
1753
|
+
const curCount = leafEntryCount(cursor);
|
|
1754
|
+
if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
|
|
1755
|
+
leafEntryAt(previous, prevCount - 1).key,
|
|
1756
|
+
leafEntryAt(cursor, 0).key
|
|
1757
|
+
) === 0) {
|
|
1758
|
+
throw new BTreeInvariantError(
|
|
1759
|
+
"Duplicate user key detected across adjacent leaves with uniqueness policy."
|
|
1760
|
+
);
|
|
1761
|
+
}
|
|
1762
|
+
};
|
|
1452
1763
|
var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
1453
1764
|
if (!isLeafNode(cursor)) {
|
|
1454
1765
|
throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
|
|
@@ -1460,22 +1771,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
|
1460
1771
|
throw new BTreeInvariantError("Leaf prev pointer mismatch.");
|
|
1461
1772
|
}
|
|
1462
1773
|
if (previous !== null && isLeafNode(previous)) {
|
|
1463
|
-
|
|
1464
|
-
const currentMin = nodeMinKey(cursor);
|
|
1465
|
-
if (prevMax === null || currentMin === null) {
|
|
1466
|
-
throw new BTreeInvariantError("Non-empty tree leaf chain contains empty leaf node.");
|
|
1467
|
-
}
|
|
1468
|
-
if (compareNodeKeys(state.compareKeys, prevMax.key, prevMax.sequence, currentMin.key, currentMin.sequence) > 0) {
|
|
1469
|
-
throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
|
|
1470
|
-
}
|
|
1471
|
-
const prevCount = leafEntryCount(previous);
|
|
1472
|
-
const curCount = leafEntryCount(cursor);
|
|
1473
|
-
if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
|
|
1474
|
-
leafEntryAt(previous, prevCount - 1).key,
|
|
1475
|
-
leafEntryAt(cursor, 0).key
|
|
1476
|
-
) === 0) {
|
|
1477
|
-
throw new BTreeInvariantError("Duplicate user key detected across adjacent leaves with uniqueness policy.");
|
|
1478
|
-
}
|
|
1774
|
+
validateAdjacentLeafOrdering(state, previous, cursor);
|
|
1479
1775
|
}
|
|
1480
1776
|
};
|
|
1481
1777
|
var validateLeafLinks = (state, expectedLeafCount) => {
|
|
@@ -1484,7 +1780,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1484
1780
|
throw new BTreeInvariantError("Empty tree root must be a leaf node.");
|
|
1485
1781
|
}
|
|
1486
1782
|
if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
|
|
1487
|
-
throw new BTreeInvariantError(
|
|
1783
|
+
throw new BTreeInvariantError(
|
|
1784
|
+
"Empty tree leaf pointers must reference root leaf."
|
|
1785
|
+
);
|
|
1488
1786
|
}
|
|
1489
1787
|
return;
|
|
1490
1788
|
}
|
|
@@ -1509,7 +1807,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1509
1807
|
throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
|
|
1510
1808
|
}
|
|
1511
1809
|
if (leafCount !== expectedLeafCount) {
|
|
1512
|
-
throw new BTreeInvariantError(
|
|
1810
|
+
throw new BTreeInvariantError(
|
|
1811
|
+
"Leaf chain count mismatch with tree traversal count."
|
|
1812
|
+
);
|
|
1513
1813
|
}
|
|
1514
1814
|
};
|
|
1515
1815
|
|
|
@@ -1536,7 +1836,9 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1536
1836
|
leafEntryAt(node, index - 1).key,
|
|
1537
1837
|
leafEntryAt(node, index).key
|
|
1538
1838
|
) === 0) {
|
|
1539
|
-
throw new BTreeInvariantError(
|
|
1839
|
+
throw new BTreeInvariantError(
|
|
1840
|
+
"Duplicate user key detected in tree with uniqueness policy."
|
|
1841
|
+
);
|
|
1540
1842
|
}
|
|
1541
1843
|
}
|
|
1542
1844
|
}
|
|
@@ -1544,12 +1846,7 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1544
1846
|
const first = leafEntryAt(node, index - 2);
|
|
1545
1847
|
const second = leafEntryAt(node, index - 1);
|
|
1546
1848
|
const third = leafEntryAt(node, index);
|
|
1547
|
-
assertTransitivityAsInvariant(
|
|
1548
|
-
state,
|
|
1549
|
-
first.key,
|
|
1550
|
-
second.key,
|
|
1551
|
-
third.key
|
|
1552
|
-
);
|
|
1849
|
+
assertTransitivityAsInvariant(state, first.key, second.key, third.key);
|
|
1553
1850
|
}
|
|
1554
1851
|
if (count > state.maxLeafEntries) {
|
|
1555
1852
|
throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
|
|
@@ -1558,9 +1855,14 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1558
1855
|
var validateLeafNode = (state, node, depth) => {
|
|
1559
1856
|
validateLeafNodeOrdering(state, node);
|
|
1560
1857
|
const count = leafEntryCount(node);
|
|
1561
|
-
|
|
1858
|
+
let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
|
|
1859
|
+
if (state.deleteRebalancePolicy === "lazy") {
|
|
1860
|
+
baseMinLeaf = applyLazyThreshold(baseMinLeaf);
|
|
1861
|
+
}
|
|
1562
1862
|
if (node !== state.root && count < baseMinLeaf) {
|
|
1563
|
-
throw new BTreeInvariantError(
|
|
1863
|
+
throw new BTreeInvariantError(
|
|
1864
|
+
"Non-root leaf node violates minimum occupancy."
|
|
1865
|
+
);
|
|
1564
1866
|
}
|
|
1565
1867
|
const first = count === 0 ? null : leafEntryAt(node, 0);
|
|
1566
1868
|
const last = count === 0 ? null : leafEntryAt(node, count - 1);
|
|
@@ -1582,76 +1884,110 @@ var validateBranchStructure = (state, node) => {
|
|
|
1582
1884
|
}
|
|
1583
1885
|
const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
|
|
1584
1886
|
if (node !== state.root && liveCount < baseMinBranch) {
|
|
1585
|
-
throw new BTreeInvariantError(
|
|
1887
|
+
throw new BTreeInvariantError(
|
|
1888
|
+
"Non-root branch node violates minimum occupancy."
|
|
1889
|
+
);
|
|
1586
1890
|
}
|
|
1587
1891
|
if (liveCount > state.maxBranchChildren) {
|
|
1588
1892
|
throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
|
|
1589
1893
|
}
|
|
1590
1894
|
if (node.keys.length !== node.children.length) {
|
|
1591
|
-
throw new BTreeInvariantError(
|
|
1895
|
+
throw new BTreeInvariantError(
|
|
1896
|
+
"Branch keys array length does not match children array length."
|
|
1897
|
+
);
|
|
1592
1898
|
}
|
|
1593
1899
|
};
|
|
1594
1900
|
var validateBranchChild = (state, node, childIndex, depth) => {
|
|
1595
1901
|
const child = node.children[childIndex];
|
|
1596
1902
|
if (child.parent !== node) {
|
|
1597
|
-
throw new BTreeInvariantError(
|
|
1903
|
+
throw new BTreeInvariantError(
|
|
1904
|
+
"Child-parent pointer mismatch in branch node."
|
|
1905
|
+
);
|
|
1598
1906
|
}
|
|
1599
1907
|
if (child.indexInParent !== childIndex) {
|
|
1600
|
-
throw new BTreeInvariantError(
|
|
1908
|
+
throw new BTreeInvariantError(
|
|
1909
|
+
"Child indexInParent does not match actual position in parent."
|
|
1910
|
+
);
|
|
1601
1911
|
}
|
|
1602
1912
|
const childValidation = validateNode(state, child, depth + 1);
|
|
1603
1913
|
if (childValidation.minKey === null || childValidation.maxKey === null) {
|
|
1604
|
-
throw new BTreeInvariantError(
|
|
1914
|
+
throw new BTreeInvariantError(
|
|
1915
|
+
"Branch child must not be empty in non-root branch tree."
|
|
1916
|
+
);
|
|
1605
1917
|
}
|
|
1606
1918
|
const cachedMinKey = node.keys[childIndex];
|
|
1607
1919
|
const actualMinKey = nodeMinKey(child);
|
|
1608
|
-
if (actualMinKey === null || compareNodeKeys(
|
|
1609
|
-
|
|
1920
|
+
if (actualMinKey === null || compareNodeKeys(
|
|
1921
|
+
state.compareKeys,
|
|
1922
|
+
cachedMinKey.key,
|
|
1923
|
+
cachedMinKey.sequence,
|
|
1924
|
+
actualMinKey.key,
|
|
1925
|
+
actualMinKey.sequence
|
|
1926
|
+
) !== 0) {
|
|
1927
|
+
throw new BTreeInvariantError(
|
|
1928
|
+
"Branch cached key does not match actual child minimum key."
|
|
1929
|
+
);
|
|
1610
1930
|
}
|
|
1611
1931
|
return childValidation;
|
|
1612
1932
|
};
|
|
1933
|
+
var mergeChildValidation = (state, accumulated, childValidation) => {
|
|
1934
|
+
if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
|
|
1935
|
+
throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
|
|
1936
|
+
}
|
|
1937
|
+
if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
|
|
1938
|
+
accumulated.leafDepth = childValidation.leafDepth;
|
|
1939
|
+
}
|
|
1940
|
+
if (accumulated.previousChildMax !== null && compareNodeKeys(
|
|
1941
|
+
state.compareKeys,
|
|
1942
|
+
accumulated.previousChildMax.key,
|
|
1943
|
+
accumulated.previousChildMax.sequence,
|
|
1944
|
+
childValidation.minKey.key,
|
|
1945
|
+
childValidation.minKey.sequence
|
|
1946
|
+
) >= 0) {
|
|
1947
|
+
throw new BTreeInvariantError(
|
|
1948
|
+
"Branch child key ranges are not strictly ordered."
|
|
1949
|
+
);
|
|
1950
|
+
}
|
|
1951
|
+
if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
|
|
1952
|
+
accumulated.maxKey = childValidation.maxKey;
|
|
1953
|
+
accumulated.previousChildMax = childValidation.maxKey;
|
|
1954
|
+
accumulated.leafCount += childValidation.leafCount;
|
|
1955
|
+
accumulated.branchCount += childValidation.branchCount;
|
|
1956
|
+
accumulated.entryCount += childValidation.entryCount;
|
|
1957
|
+
};
|
|
1613
1958
|
var validateNode = (state, node, depth) => {
|
|
1614
1959
|
if (isLeafNode(node)) {
|
|
1615
1960
|
return validateLeafNode(state, node, depth);
|
|
1616
1961
|
}
|
|
1617
1962
|
validateBranchStructure(state, node);
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1963
|
+
const acc = {
|
|
1964
|
+
leafDepth: null,
|
|
1965
|
+
leafCount: 0,
|
|
1966
|
+
branchCount: 1,
|
|
1967
|
+
entryCount: 0,
|
|
1968
|
+
minKey: null,
|
|
1969
|
+
maxKey: null,
|
|
1970
|
+
previousChildMax: null
|
|
1971
|
+
};
|
|
1625
1972
|
for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
|
|
1626
1973
|
const childValidation = validateBranchChild(state, node, childIndex, depth);
|
|
1627
|
-
|
|
1628
|
-
throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
|
|
1629
|
-
}
|
|
1630
|
-
if (leafDepth === null && childValidation.leafDepth !== null) {
|
|
1631
|
-
leafDepth = childValidation.leafDepth;
|
|
1632
|
-
}
|
|
1633
|
-
if (previousChildMax !== null && compareNodeKeys(
|
|
1634
|
-
state.compareKeys,
|
|
1635
|
-
previousChildMax.key,
|
|
1636
|
-
previousChildMax.sequence,
|
|
1637
|
-
childValidation.minKey.key,
|
|
1638
|
-
childValidation.minKey.sequence
|
|
1639
|
-
) >= 0) {
|
|
1640
|
-
throw new BTreeInvariantError("Branch child key ranges are not strictly ordered.");
|
|
1641
|
-
}
|
|
1642
|
-
if (minKey === null) minKey = childValidation.minKey;
|
|
1643
|
-
maxKey = childValidation.maxKey;
|
|
1644
|
-
previousChildMax = childValidation.maxKey;
|
|
1645
|
-
leafCount += childValidation.leafCount;
|
|
1646
|
-
branchCount += childValidation.branchCount;
|
|
1647
|
-
entryCount += childValidation.entryCount;
|
|
1974
|
+
mergeChildValidation(state, acc, childValidation);
|
|
1648
1975
|
}
|
|
1649
|
-
return {
|
|
1976
|
+
return {
|
|
1977
|
+
minKey: acc.minKey,
|
|
1978
|
+
maxKey: acc.maxKey,
|
|
1979
|
+
leafDepth: acc.leafDepth,
|
|
1980
|
+
leafCount: acc.leafCount,
|
|
1981
|
+
branchCount: acc.branchCount,
|
|
1982
|
+
entryCount: acc.entryCount
|
|
1983
|
+
};
|
|
1650
1984
|
};
|
|
1651
1985
|
var assertInvariants = (state) => {
|
|
1652
1986
|
const validation = validateNode(state, state.root, 0);
|
|
1653
1987
|
if (validation.entryCount !== state.entryCount) {
|
|
1654
|
-
throw new BTreeInvariantError(
|
|
1988
|
+
throw new BTreeInvariantError(
|
|
1989
|
+
"Index entry count mismatch between tree traversal and tracked state."
|
|
1990
|
+
);
|
|
1655
1991
|
}
|
|
1656
1992
|
validateLeafLinks(state, validation.leafCount);
|
|
1657
1993
|
};
|
|
@@ -1707,7 +2043,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1707
2043
|
remove(key) {
|
|
1708
2044
|
const entry = removeFirstMatchingEntry(this.state, key);
|
|
1709
2045
|
if (entry === null) return null;
|
|
1710
|
-
return
|
|
2046
|
+
return freezeEntry(entry);
|
|
1711
2047
|
}
|
|
1712
2048
|
removeById(entryId) {
|
|
1713
2049
|
if (this.state.entryKeys === null) {
|
|
@@ -1715,7 +2051,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1715
2051
|
}
|
|
1716
2052
|
const entry = removeEntryById(this.state, entryId);
|
|
1717
2053
|
if (entry === null) return null;
|
|
1718
|
-
return
|
|
2054
|
+
return freezeEntry(entry);
|
|
1719
2055
|
}
|
|
1720
2056
|
peekById(entryId) {
|
|
1721
2057
|
if (this.state.entryKeys === null) {
|
|
@@ -1723,7 +2059,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1723
2059
|
}
|
|
1724
2060
|
const entry = peekEntryById(this.state, entryId);
|
|
1725
2061
|
if (entry === null) return null;
|
|
1726
|
-
return
|
|
2062
|
+
return freezeEntry(entry);
|
|
1727
2063
|
}
|
|
1728
2064
|
updateById(entryId, value) {
|
|
1729
2065
|
if (this.state.entryKeys === null) {
|
|
@@ -1731,30 +2067,30 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1731
2067
|
}
|
|
1732
2068
|
const entry = updateEntryById(this.state, entryId, value);
|
|
1733
2069
|
if (entry === null) return null;
|
|
1734
|
-
return
|
|
2070
|
+
return freezeEntry(entry);
|
|
1735
2071
|
}
|
|
1736
2072
|
popFirst() {
|
|
1737
2073
|
const entry = popFirstEntry(this.state);
|
|
1738
2074
|
if (entry === null) return null;
|
|
1739
|
-
return
|
|
2075
|
+
return freezeEntry(entry);
|
|
1740
2076
|
}
|
|
1741
2077
|
peekFirst() {
|
|
1742
2078
|
if (this.state.entryCount === 0) {
|
|
1743
2079
|
return null;
|
|
1744
2080
|
}
|
|
1745
|
-
return
|
|
2081
|
+
return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
|
|
1746
2082
|
}
|
|
1747
2083
|
peekLast() {
|
|
1748
2084
|
if (this.state.entryCount === 0) {
|
|
1749
2085
|
return null;
|
|
1750
2086
|
}
|
|
1751
2087
|
const leaf = this.state.rightmostLeaf;
|
|
1752
|
-
return
|
|
2088
|
+
return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
|
|
1753
2089
|
}
|
|
1754
2090
|
popLast() {
|
|
1755
2091
|
const entry = popLastEntry(this.state);
|
|
1756
2092
|
if (entry === null) return null;
|
|
1757
|
-
return
|
|
2093
|
+
return freezeEntry(entry);
|
|
1758
2094
|
}
|
|
1759
2095
|
clear() {
|
|
1760
2096
|
const emptyLeaf = createLeafNode([], null);
|
|
@@ -1764,16 +2100,9 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1764
2100
|
this.state.entryCount = 0;
|
|
1765
2101
|
this.state._cursor.leaf = emptyLeaf;
|
|
1766
2102
|
this.state._cursor.index = 0;
|
|
1767
|
-
|
|
1768
|
-
this.state.entryKeys.clear();
|
|
1769
|
-
}
|
|
2103
|
+
this.state.entryKeys?.clear();
|
|
1770
2104
|
if (this.state.autoScale) {
|
|
1771
|
-
|
|
1772
|
-
this.state.maxLeafEntries = tier.maxLeaf;
|
|
1773
|
-
this.state.maxBranchChildren = tier.maxBranch;
|
|
1774
|
-
this.state.minLeafEntries = minOccupancy(tier.maxLeaf);
|
|
1775
|
-
this.state.minBranchChildren = minOccupancy(tier.maxBranch);
|
|
1776
|
-
this.state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
2105
|
+
resetAutoScaleToTier0(this.state);
|
|
1777
2106
|
}
|
|
1778
2107
|
}
|
|
1779
2108
|
get(key) {
|
|
@@ -1787,65 +2116,39 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1787
2116
|
findFirst(key) {
|
|
1788
2117
|
const found = findFirstMatchingUserKey(this.state, key);
|
|
1789
2118
|
if (found === null) return null;
|
|
1790
|
-
return
|
|
2119
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1791
2120
|
}
|
|
1792
|
-
/**
|
|
1793
|
-
* Returns the last entry whose key matches `key`, or `null` if not found.
|
|
1794
|
-
* Useful when `duplicateKeys` is `'allow'` and multiple entries share the same key.
|
|
1795
|
-
*/
|
|
1796
2121
|
findLast(key) {
|
|
1797
2122
|
const found = findLastMatchingUserKey(this.state, key);
|
|
1798
2123
|
if (found === null) return null;
|
|
1799
|
-
return
|
|
2124
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1800
2125
|
}
|
|
1801
|
-
/**
|
|
1802
|
-
* Returns the smallest key in the tree that is strictly greater than `key`,
|
|
1803
|
-
* or `null` if no such key exists.
|
|
1804
|
-
*/
|
|
1805
2126
|
nextHigherKey(key) {
|
|
1806
2127
|
return findNextHigherKey(this.state, key);
|
|
1807
2128
|
}
|
|
1808
|
-
/**
|
|
1809
|
-
* Returns the largest key in the tree that is strictly less than `key`,
|
|
1810
|
-
* or `null` if no such key exists.
|
|
1811
|
-
*/
|
|
1812
2129
|
nextLowerKey(key) {
|
|
1813
2130
|
return findNextLowerKey(this.state, key);
|
|
1814
2131
|
}
|
|
1815
|
-
/**
|
|
1816
|
-
* Returns the entry for `key` if it exists; otherwise returns the entry with
|
|
1817
|
-
* the largest key strictly less than `key`. Returns `null` when the tree is
|
|
1818
|
-
* empty or every key is greater than `key`.
|
|
1819
|
-
*/
|
|
1820
2132
|
getPairOrNextLower(key) {
|
|
1821
2133
|
const found = findPairOrNextLower(this.state, key);
|
|
1822
2134
|
if (found === null) return null;
|
|
1823
|
-
return
|
|
2135
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1824
2136
|
}
|
|
1825
|
-
/**
|
|
1826
|
-
* Returns the number of entries whose keys fall within [`startKey`, `endKey`].
|
|
1827
|
-
* Pass `options` to make either bound exclusive.
|
|
1828
|
-
*/
|
|
1829
2137
|
count(startKey, endKey, options) {
|
|
1830
2138
|
return countRangeEntries(this.state, startKey, endKey, options);
|
|
1831
2139
|
}
|
|
1832
|
-
/**
|
|
1833
|
-
* Deletes all entries whose keys fall within [`startKey`, `endKey`].
|
|
1834
|
-
* Pass `options` to make either bound exclusive.
|
|
1835
|
-
* @returns The number of entries deleted.
|
|
1836
|
-
*/
|
|
1837
2140
|
deleteRange(startKey, endKey, options) {
|
|
1838
2141
|
return deleteRangeEntries(this.state, startKey, endKey, options);
|
|
1839
2142
|
}
|
|
1840
2143
|
range(startKey, endKey, options) {
|
|
1841
|
-
return
|
|
2144
|
+
return rangeQueryPublicEntries(this.state, startKey, endKey, options);
|
|
1842
2145
|
}
|
|
1843
2146
|
*entries() {
|
|
1844
2147
|
let leaf = this.state.leftmostLeaf;
|
|
1845
2148
|
while (leaf !== null) {
|
|
1846
2149
|
const count = leafEntryCount(leaf);
|
|
1847
2150
|
for (let i = 0; i < count; i += 1) {
|
|
1848
|
-
yield
|
|
2151
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1849
2152
|
}
|
|
1850
2153
|
leaf = leaf.next;
|
|
1851
2154
|
}
|
|
@@ -1855,7 +2158,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1855
2158
|
while (leaf !== null) {
|
|
1856
2159
|
const count = leafEntryCount(leaf);
|
|
1857
2160
|
for (let i = count - 1; i >= 0; i -= 1) {
|
|
1858
|
-
yield
|
|
2161
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1859
2162
|
}
|
|
1860
2163
|
leaf = leaf.prev;
|
|
1861
2164
|
}
|
|
@@ -1873,55 +2176,26 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1873
2176
|
[Symbol.iterator]() {
|
|
1874
2177
|
return this.entries();
|
|
1875
2178
|
}
|
|
2179
|
+
forEachRange(startKey, endKey, callback, options) {
|
|
2180
|
+
forEachRangeEntries(this.state, startKey, endKey, callback, options);
|
|
2181
|
+
}
|
|
1876
2182
|
forEach(callback, thisArg) {
|
|
1877
|
-
|
|
1878
|
-
while (leaf !== null) {
|
|
1879
|
-
const count = leafEntryCount(leaf);
|
|
1880
|
-
for (let i = 0; i < count; i += 1) {
|
|
1881
|
-
callback.call(thisArg, toPublicEntry(leafEntryAt(leaf, i)));
|
|
1882
|
-
}
|
|
1883
|
-
leaf = leaf.next;
|
|
1884
|
-
}
|
|
2183
|
+
forEachEntry(this.state, callback, thisArg);
|
|
1885
2184
|
}
|
|
1886
2185
|
snapshot() {
|
|
1887
|
-
|
|
1888
|
-
let leaf = this.state.leftmostLeaf;
|
|
1889
|
-
let writeIdx = 0;
|
|
1890
|
-
while (leaf !== null) {
|
|
1891
|
-
const count = leafEntryCount(leaf);
|
|
1892
|
-
for (let i = 0; i < count; i += 1) {
|
|
1893
|
-
result[writeIdx++] = toPublicEntry(leafEntryAt(leaf, i));
|
|
1894
|
-
}
|
|
1895
|
-
leaf = leaf.next;
|
|
1896
|
-
}
|
|
1897
|
-
return result;
|
|
2186
|
+
return snapshotEntries(this.state);
|
|
1898
2187
|
}
|
|
1899
|
-
/**
|
|
1900
|
-
* Returns a structurally independent `InMemoryBTree` with identical
|
|
1901
|
-
* configuration and entries. The tree structure (nodes, links, entry IDs)
|
|
1902
|
-
* is fully independent, but stored key and value references are shared
|
|
1903
|
-
* with the source tree.
|
|
1904
|
-
* Note: `EntryId` values are reassigned in the clone — IDs from the source tree are not valid for the clone.
|
|
1905
|
-
*/
|
|
1906
2188
|
clone() {
|
|
1907
|
-
const cloned = new _InMemoryBTree(
|
|
2189
|
+
const cloned = new _InMemoryBTree(
|
|
2190
|
+
buildConfigFromState(this.state)
|
|
2191
|
+
);
|
|
1908
2192
|
applyAutoScaleCapacitySnapshot(
|
|
1909
2193
|
cloned.state,
|
|
1910
2194
|
this.state.maxLeafEntries,
|
|
1911
2195
|
this.state.maxBranchChildren
|
|
1912
2196
|
);
|
|
1913
2197
|
if (this.state.entryCount > 0) {
|
|
1914
|
-
|
|
1915
|
-
let leaf = this.state.leftmostLeaf;
|
|
1916
|
-
let writeIdx = 0;
|
|
1917
|
-
while (leaf !== null) {
|
|
1918
|
-
const count = leafEntryCount(leaf);
|
|
1919
|
-
for (let i = 0; i < count; i += 1) {
|
|
1920
|
-
pairs[writeIdx++] = leafEntryAt(leaf, i);
|
|
1921
|
-
}
|
|
1922
|
-
leaf = leaf.next;
|
|
1923
|
-
}
|
|
1924
|
-
cloned.putMany(pairs);
|
|
2198
|
+
cloned.putMany(collectInternalEntries(this.state));
|
|
1925
2199
|
}
|
|
1926
2200
|
return cloned;
|
|
1927
2201
|
}
|
|
@@ -1930,26 +2204,19 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1930
2204
|
}
|
|
1931
2205
|
static fromJSON(json, compareKeys) {
|
|
1932
2206
|
validateBTreeJSON(json);
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
throw new BTreeValidationError("fromJSON: entries not sorted.");
|
|
1938
|
-
}
|
|
1939
|
-
if (strict && cmp === 0) {
|
|
1940
|
-
throw new BTreeValidationError(
|
|
1941
|
-
'fromJSON: duplicate keys require duplicateKeys "allow".'
|
|
1942
|
-
);
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
|
-
const tree = new _InMemoryBTree(buildConfigFromJSON(json, compareKeys));
|
|
2207
|
+
validateBTreeJSONSortOrder(json, compareKeys);
|
|
2208
|
+
const tree = new _InMemoryBTree(
|
|
2209
|
+
buildConfigFromJSON(json, compareKeys)
|
|
2210
|
+
);
|
|
1946
2211
|
applyAutoScaleCapacitySnapshot(
|
|
1947
2212
|
tree.state,
|
|
1948
2213
|
json.config.maxLeafEntries,
|
|
1949
2214
|
json.config.maxBranchChildren
|
|
1950
2215
|
);
|
|
1951
2216
|
if (json.entries.length > 0) {
|
|
1952
|
-
const pairs = new Array(
|
|
2217
|
+
const pairs = new Array(
|
|
2218
|
+
json.entries.length
|
|
2219
|
+
);
|
|
1953
2220
|
for (let i = 0; i < json.entries.length; i += 1) {
|
|
1954
2221
|
pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
|
|
1955
2222
|
}
|
|
@@ -1993,13 +2260,17 @@ var assertNeverMutation = (mutation) => {
|
|
|
1993
2260
|
var validatePutManyEntries = (entries) => {
|
|
1994
2261
|
for (const entry of entries) {
|
|
1995
2262
|
if (typeof entry !== "object" || entry === null || !("key" in entry) || !("value" in entry)) {
|
|
1996
|
-
throw new BTreeConcurrencyError(
|
|
2263
|
+
throw new BTreeConcurrencyError(
|
|
2264
|
+
"Malformed putMany mutation: each entry must have key and value."
|
|
2265
|
+
);
|
|
1997
2266
|
}
|
|
1998
2267
|
}
|
|
1999
2268
|
};
|
|
2000
2269
|
var validateInitMutation = (m, expectedConfigFingerprint) => {
|
|
2001
2270
|
if (typeof m.configFingerprint !== "string") {
|
|
2002
|
-
throw new BTreeConcurrencyError(
|
|
2271
|
+
throw new BTreeConcurrencyError(
|
|
2272
|
+
"Malformed init mutation: missing configFingerprint."
|
|
2273
|
+
);
|
|
2003
2274
|
}
|
|
2004
2275
|
if (expectedConfigFingerprint !== void 0 && m.configFingerprint !== expectedConfigFingerprint) {
|
|
2005
2276
|
throw new BTreeConcurrencyError(
|
|
@@ -2007,45 +2278,70 @@ var validateInitMutation = (m, expectedConfigFingerprint) => {
|
|
|
2007
2278
|
);
|
|
2008
2279
|
}
|
|
2009
2280
|
};
|
|
2010
|
-
var
|
|
2281
|
+
var validateMutationFields = (m) => {
|
|
2011
2282
|
switch (m.type) {
|
|
2012
|
-
case "init":
|
|
2013
|
-
validateInitMutation(m, expectedConfigFingerprint);
|
|
2014
|
-
break;
|
|
2015
2283
|
case "put":
|
|
2016
2284
|
if (!("key" in m) || !("value" in m)) {
|
|
2017
|
-
throw new BTreeConcurrencyError(
|
|
2285
|
+
throw new BTreeConcurrencyError(
|
|
2286
|
+
"Malformed put mutation: missing key or value."
|
|
2287
|
+
);
|
|
2018
2288
|
}
|
|
2019
2289
|
break;
|
|
2020
2290
|
case "remove":
|
|
2021
2291
|
if (!("key" in m)) {
|
|
2022
|
-
throw new BTreeConcurrencyError(
|
|
2292
|
+
throw new BTreeConcurrencyError(
|
|
2293
|
+
"Malformed remove mutation: missing key."
|
|
2294
|
+
);
|
|
2023
2295
|
}
|
|
2024
2296
|
break;
|
|
2025
2297
|
case "removeById":
|
|
2026
2298
|
if (!("entryId" in m)) {
|
|
2027
|
-
throw new BTreeConcurrencyError(
|
|
2299
|
+
throw new BTreeConcurrencyError(
|
|
2300
|
+
"Malformed removeById mutation: missing entryId."
|
|
2301
|
+
);
|
|
2028
2302
|
}
|
|
2029
2303
|
break;
|
|
2030
2304
|
case "updateById":
|
|
2031
2305
|
if (!("entryId" in m) || !("value" in m)) {
|
|
2032
|
-
throw new BTreeConcurrencyError(
|
|
2306
|
+
throw new BTreeConcurrencyError(
|
|
2307
|
+
"Malformed updateById mutation: missing entryId or value."
|
|
2308
|
+
);
|
|
2033
2309
|
}
|
|
2034
2310
|
break;
|
|
2035
|
-
case "popFirst":
|
|
2036
|
-
case "popLast":
|
|
2037
|
-
break;
|
|
2038
2311
|
case "putMany":
|
|
2039
2312
|
if (!("entries" in m) || !Array.isArray(m.entries)) {
|
|
2040
|
-
throw new BTreeConcurrencyError(
|
|
2313
|
+
throw new BTreeConcurrencyError(
|
|
2314
|
+
"Malformed putMany mutation: missing entries array."
|
|
2315
|
+
);
|
|
2041
2316
|
}
|
|
2042
2317
|
validatePutManyEntries(m.entries);
|
|
2043
2318
|
break;
|
|
2044
2319
|
case "deleteRange":
|
|
2045
2320
|
if (!("startKey" in m) || !("endKey" in m)) {
|
|
2046
|
-
throw new BTreeConcurrencyError(
|
|
2321
|
+
throw new BTreeConcurrencyError(
|
|
2322
|
+
"Malformed deleteRange mutation: missing startKey or endKey."
|
|
2323
|
+
);
|
|
2047
2324
|
}
|
|
2048
2325
|
break;
|
|
2326
|
+
default:
|
|
2327
|
+
break;
|
|
2328
|
+
}
|
|
2329
|
+
};
|
|
2330
|
+
var validateSingleMutation = (m, expectedConfigFingerprint) => {
|
|
2331
|
+
switch (m.type) {
|
|
2332
|
+
case "init":
|
|
2333
|
+
validateInitMutation(m, expectedConfigFingerprint);
|
|
2334
|
+
break;
|
|
2335
|
+
case "put":
|
|
2336
|
+
case "remove":
|
|
2337
|
+
case "removeById":
|
|
2338
|
+
case "updateById":
|
|
2339
|
+
case "putMany":
|
|
2340
|
+
case "deleteRange":
|
|
2341
|
+
validateMutationFields(m);
|
|
2342
|
+
break;
|
|
2343
|
+
case "popFirst":
|
|
2344
|
+
case "popLast":
|
|
2049
2345
|
case "clear":
|
|
2050
2346
|
break;
|
|
2051
2347
|
default:
|
|
@@ -2057,7 +2353,9 @@ var validateSingleMutation = (m, expectedConfigFingerprint) => {
|
|
|
2057
2353
|
var validateMutationBatch = (mutations, expectedConfigFingerprint) => {
|
|
2058
2354
|
for (const mutation of mutations) {
|
|
2059
2355
|
if (typeof mutation !== "object" || mutation === null) {
|
|
2060
|
-
throw new BTreeConcurrencyError(
|
|
2356
|
+
throw new BTreeConcurrencyError(
|
|
2357
|
+
"Malformed mutation: expected an object."
|
|
2358
|
+
);
|
|
2061
2359
|
}
|
|
2062
2360
|
validateSingleMutation(
|
|
2063
2361
|
mutation,
|
|
@@ -2092,9 +2390,7 @@ var normalizeReadMode = (value) => {
|
|
|
2092
2390
|
return "strong";
|
|
2093
2391
|
}
|
|
2094
2392
|
if (value !== "strong" && value !== "local") {
|
|
2095
|
-
throw new BTreeConcurrencyError(
|
|
2096
|
-
`readMode: must be 'strong' or 'local'.`
|
|
2097
|
-
);
|
|
2393
|
+
throw new BTreeConcurrencyError(`readMode: must be 'strong' or 'local'.`);
|
|
2098
2394
|
}
|
|
2099
2395
|
return value;
|
|
2100
2396
|
};
|
|
@@ -2144,7 +2440,11 @@ var applyMutationLocal = (tree, mutation, onInit) => {
|
|
|
2144
2440
|
case "popLast":
|
|
2145
2441
|
return tree.popLast();
|
|
2146
2442
|
case "deleteRange":
|
|
2147
|
-
return tree.deleteRange(
|
|
2443
|
+
return tree.deleteRange(
|
|
2444
|
+
mutation.startKey,
|
|
2445
|
+
mutation.endKey,
|
|
2446
|
+
mutation.options
|
|
2447
|
+
);
|
|
2148
2448
|
case "clear":
|
|
2149
2449
|
tree.clear();
|
|
2150
2450
|
return null;
|
|
@@ -2191,7 +2491,9 @@ var createPutManyEvaluator = (entries, duplicateKeys, compareKeys) => {
|
|
|
2191
2491
|
for (let i = 1; i < entries.length; i += 1) {
|
|
2192
2492
|
const cmp = compareKeys(entries[i - 1].key, entries[i].key);
|
|
2193
2493
|
if (cmp > 0) {
|
|
2194
|
-
throw new BTreeValidationError(
|
|
2494
|
+
throw new BTreeValidationError(
|
|
2495
|
+
"putMany: entries not in ascending order."
|
|
2496
|
+
);
|
|
2195
2497
|
}
|
|
2196
2498
|
if (strictlyAscending && cmp === 0) {
|
|
2197
2499
|
throw new BTreeValidationError(
|
|
@@ -2222,59 +2524,48 @@ var createClearEvaluator = () => {
|
|
|
2222
2524
|
return () => ({ type: "clear" });
|
|
2223
2525
|
};
|
|
2224
2526
|
|
|
2225
|
-
// src/concurrency/
|
|
2226
|
-
var
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2527
|
+
// src/concurrency/syncLogValidation.ts
|
|
2528
|
+
var validateSyncLog = (log, maxSyncMutationsPerBatch) => {
|
|
2529
|
+
if (typeof log.version !== "bigint") {
|
|
2530
|
+
throw new BTreeConcurrencyError("Store contract: version must be bigint.");
|
|
2531
|
+
}
|
|
2532
|
+
if (!Array.isArray(log.mutations)) {
|
|
2533
|
+
throw new BTreeConcurrencyError(
|
|
2534
|
+
"Store contract: mutations must be an array."
|
|
2233
2535
|
);
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2536
|
+
}
|
|
2537
|
+
if (log.mutations.length > maxSyncMutationsPerBatch) {
|
|
2538
|
+
throw new BTreeConcurrencyError(
|
|
2539
|
+
`Sync batch exceeded limit (${String(maxSyncMutationsPerBatch)}).`
|
|
2540
|
+
);
|
|
2541
|
+
}
|
|
2542
|
+
};
|
|
2543
|
+
|
|
2544
|
+
// src/concurrency/coordinator.ts
|
|
2545
|
+
var Coordinator = class {
|
|
2546
|
+
constructor(tree, store, maxRetries, maxSyncMutationsPerBatch, configFingerprint, readMode) {
|
|
2547
|
+
this.tree = tree;
|
|
2548
|
+
this.store = store;
|
|
2549
|
+
this.maxRetries = maxRetries;
|
|
2550
|
+
this.maxSyncMutationsPerBatch = maxSyncMutationsPerBatch;
|
|
2551
|
+
this.configFingerprint = configFingerprint;
|
|
2552
|
+
this.readMode = readMode;
|
|
2245
2553
|
this.currentVersion = 0n;
|
|
2246
2554
|
this.operationQueue = Promise.resolve();
|
|
2247
2555
|
this.initSeen = false;
|
|
2248
2556
|
this.corrupted = false;
|
|
2249
2557
|
}
|
|
2250
|
-
async sync() {
|
|
2251
|
-
await this.runExclusive(async () => {
|
|
2252
|
-
await this.syncUnlocked();
|
|
2253
|
-
});
|
|
2254
|
-
}
|
|
2255
2558
|
async syncUnlocked() {
|
|
2256
2559
|
const log = await this.store.getLogEntriesSince(this.currentVersion);
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
}
|
|
2260
|
-
if (!Array.isArray(log.mutations)) {
|
|
2261
|
-
throw new BTreeConcurrencyError("Store contract: mutations must be an array.");
|
|
2262
|
-
}
|
|
2263
|
-
if (log.mutations.length > this.maxSyncMutationsPerBatch) {
|
|
2264
|
-
throw new BTreeConcurrencyError(
|
|
2265
|
-
`Sync batch exceeded limit (${String(this.maxSyncMutationsPerBatch)}).`
|
|
2266
|
-
);
|
|
2267
|
-
}
|
|
2268
|
-
if (log.version <= this.currentVersion) {
|
|
2269
|
-
return;
|
|
2270
|
-
}
|
|
2560
|
+
validateSyncLog(log, this.maxSyncMutationsPerBatch);
|
|
2561
|
+
if (log.version <= this.currentVersion) return;
|
|
2271
2562
|
validateMutationBatch(log.mutations, this.configFingerprint);
|
|
2272
2563
|
try {
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2564
|
+
const markInit = () => {
|
|
2565
|
+
this.initSeen = true;
|
|
2566
|
+
};
|
|
2567
|
+
for (const mutation of log.mutations)
|
|
2568
|
+
applyMutationLocal(this.tree, mutation, markInit);
|
|
2278
2569
|
this.currentVersion = log.version;
|
|
2279
2570
|
} catch (error) {
|
|
2280
2571
|
this.corrupted = true;
|
|
@@ -2286,11 +2577,10 @@ var ConcurrentInMemoryBTree = class {
|
|
|
2286
2577
|
}
|
|
2287
2578
|
runExclusive(operation) {
|
|
2288
2579
|
const run = async () => {
|
|
2289
|
-
if (this.corrupted)
|
|
2580
|
+
if (this.corrupted)
|
|
2290
2581
|
throw new BTreeConcurrencyError(
|
|
2291
2582
|
"Instance is permanently corrupted. Discard and create a new instance."
|
|
2292
2583
|
);
|
|
2293
|
-
}
|
|
2294
2584
|
return operation();
|
|
2295
2585
|
};
|
|
2296
2586
|
const result = this.operationQueue.then(run, run);
|
|
@@ -2302,40 +2592,38 @@ var ConcurrentInMemoryBTree = class {
|
|
|
2302
2592
|
}
|
|
2303
2593
|
readOp(fn) {
|
|
2304
2594
|
return this.runExclusive(async () => {
|
|
2305
|
-
if (this.readMode === "strong")
|
|
2306
|
-
await this.syncUnlocked();
|
|
2307
|
-
}
|
|
2595
|
+
if (this.readMode === "strong") await this.syncUnlocked();
|
|
2308
2596
|
return fn(this.tree);
|
|
2309
2597
|
});
|
|
2310
2598
|
}
|
|
2311
|
-
async
|
|
2599
|
+
async appendAndApply(evaluate) {
|
|
2312
2600
|
for (let attempt = 0; attempt < this.maxRetries; attempt += 1) {
|
|
2313
2601
|
await this.syncUnlocked();
|
|
2314
2602
|
const mutation = evaluate(this.tree);
|
|
2315
|
-
if (mutation === null)
|
|
2316
|
-
return null;
|
|
2317
|
-
}
|
|
2603
|
+
if (mutation === null) return null;
|
|
2318
2604
|
const expectedVersion = this.currentVersion;
|
|
2319
|
-
const mutations = this.initSeen ? [mutation] : [
|
|
2605
|
+
const mutations = this.initSeen ? [mutation] : [
|
|
2606
|
+
{ type: "init", configFingerprint: this.configFingerprint },
|
|
2607
|
+
mutation
|
|
2608
|
+
];
|
|
2320
2609
|
const appendResult = await this.store.append(expectedVersion, mutations);
|
|
2321
2610
|
assertAppendVersionContract(expectedVersion, appendResult);
|
|
2322
2611
|
if (appendResult.applied) {
|
|
2323
2612
|
try {
|
|
2613
|
+
const markInit = () => {
|
|
2614
|
+
this.initSeen = true;
|
|
2615
|
+
};
|
|
2324
2616
|
for (const m of mutations) {
|
|
2325
2617
|
if (m === mutation) break;
|
|
2326
|
-
applyMutationLocal(this.tree, m,
|
|
2327
|
-
this.initSeen = true;
|
|
2328
|
-
});
|
|
2618
|
+
applyMutationLocal(this.tree, m, markInit);
|
|
2329
2619
|
}
|
|
2330
|
-
const
|
|
2620
|
+
const result = applyMutationLocal(
|
|
2331
2621
|
this.tree,
|
|
2332
2622
|
mutation,
|
|
2333
|
-
|
|
2334
|
-
this.initSeen = true;
|
|
2335
|
-
}
|
|
2623
|
+
markInit
|
|
2336
2624
|
);
|
|
2337
2625
|
this.currentVersion = appendResult.version;
|
|
2338
|
-
return
|
|
2626
|
+
return result;
|
|
2339
2627
|
} catch (error) {
|
|
2340
2628
|
this.corrupted = true;
|
|
2341
2629
|
const cause = error instanceof Error ? error.message : String(error);
|
|
@@ -2349,137 +2637,159 @@ var ConcurrentInMemoryBTree = class {
|
|
|
2349
2637
|
`Mutation failed after ${String(this.maxRetries)} retries.`
|
|
2350
2638
|
);
|
|
2351
2639
|
}
|
|
2352
|
-
|
|
2353
|
-
return this.runExclusive(async () =>
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2640
|
+
writeOp(evaluator) {
|
|
2641
|
+
return this.runExclusive(async () => this.appendAndApply(evaluator));
|
|
2642
|
+
}
|
|
2643
|
+
};
|
|
2644
|
+
|
|
2645
|
+
// src/concurrency/ConcurrentInMemoryBTree.ts
|
|
2646
|
+
var ConcurrentInMemoryBTree = class {
|
|
2647
|
+
constructor(config) {
|
|
2648
|
+
this.compareKeys = config.compareKeys;
|
|
2649
|
+
this.duplicateKeys = config.duplicateKeys ?? "replace";
|
|
2650
|
+
const tree = new InMemoryBTree({
|
|
2651
|
+
compareKeys: config.compareKeys,
|
|
2652
|
+
maxLeafEntries: config.maxLeafEntries,
|
|
2653
|
+
maxBranchChildren: config.maxBranchChildren,
|
|
2654
|
+
duplicateKeys: config.duplicateKeys,
|
|
2655
|
+
enableEntryIdLookup: config.enableEntryIdLookup,
|
|
2656
|
+
autoScale: config.autoScale,
|
|
2657
|
+
deleteRebalancePolicy: config.deleteRebalancePolicy
|
|
2357
2658
|
});
|
|
2659
|
+
this.coord = new Coordinator(
|
|
2660
|
+
tree,
|
|
2661
|
+
config.store,
|
|
2662
|
+
normalizeMaxRetries(config.maxRetries),
|
|
2663
|
+
normalizeMaxSyncMutationsPerBatch(config.maxSyncMutationsPerBatch),
|
|
2664
|
+
computeConfigFingerprint(config),
|
|
2665
|
+
normalizeReadMode(config.readMode)
|
|
2666
|
+
);
|
|
2358
2667
|
}
|
|
2359
|
-
async
|
|
2360
|
-
|
|
2361
|
-
|
|
2668
|
+
async sync() {
|
|
2669
|
+
await this.coord.runExclusive(async () => {
|
|
2670
|
+
await this.coord.syncUnlocked();
|
|
2362
2671
|
});
|
|
2363
2672
|
}
|
|
2364
|
-
async
|
|
2365
|
-
return this.runExclusive(async () => {
|
|
2366
|
-
|
|
2673
|
+
async syncThenRead(fn) {
|
|
2674
|
+
return this.coord.runExclusive(async () => {
|
|
2675
|
+
await this.coord.syncUnlocked();
|
|
2676
|
+
return fn(this.coord.tree);
|
|
2367
2677
|
});
|
|
2368
2678
|
}
|
|
2679
|
+
async put(key, value) {
|
|
2680
|
+
return this.coord.writeOp(
|
|
2681
|
+
createPutEvaluator(this.duplicateKeys, key, value)
|
|
2682
|
+
);
|
|
2683
|
+
}
|
|
2684
|
+
async remove(key) {
|
|
2685
|
+
return this.coord.writeOp(createRemoveEvaluator(key));
|
|
2686
|
+
}
|
|
2687
|
+
async removeById(entryId) {
|
|
2688
|
+
return this.coord.writeOp(createRemoveByIdEvaluator(entryId));
|
|
2689
|
+
}
|
|
2369
2690
|
async updateById(entryId, value) {
|
|
2370
|
-
return this.
|
|
2371
|
-
return this.appendMutationAndApplyUnlocked(createUpdateByIdEvaluator(entryId, value));
|
|
2372
|
-
});
|
|
2691
|
+
return this.coord.writeOp(createUpdateByIdEvaluator(entryId, value));
|
|
2373
2692
|
}
|
|
2374
2693
|
async popFirst() {
|
|
2375
|
-
return this.
|
|
2376
|
-
return this.appendMutationAndApplyUnlocked(createPopFirstEvaluator());
|
|
2377
|
-
});
|
|
2694
|
+
return this.coord.writeOp(createPopFirstEvaluator());
|
|
2378
2695
|
}
|
|
2379
2696
|
async popLast() {
|
|
2380
|
-
return this.
|
|
2381
|
-
return this.appendMutationAndApplyUnlocked(createPopLastEvaluator());
|
|
2382
|
-
});
|
|
2697
|
+
return this.coord.writeOp(createPopLastEvaluator());
|
|
2383
2698
|
}
|
|
2384
2699
|
async putMany(entries) {
|
|
2385
|
-
if (entries.length === 0)
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
return this.appendMutationAndApplyUnlocked(
|
|
2390
|
-
createPutManyEvaluator(entries, this.duplicateKeys, this.compareKeys)
|
|
2391
|
-
);
|
|
2392
|
-
});
|
|
2700
|
+
if (entries.length === 0) return [];
|
|
2701
|
+
return this.coord.writeOp(
|
|
2702
|
+
createPutManyEvaluator(entries, this.duplicateKeys, this.compareKeys)
|
|
2703
|
+
);
|
|
2393
2704
|
}
|
|
2394
2705
|
async deleteRange(startKey, endKey, options) {
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
return result ?? 0;
|
|
2400
|
-
});
|
|
2706
|
+
const result = await this.coord.writeOp(
|
|
2707
|
+
createDeleteRangeEvaluator(startKey, endKey, options)
|
|
2708
|
+
);
|
|
2709
|
+
return result ?? 0;
|
|
2401
2710
|
}
|
|
2402
2711
|
async clear() {
|
|
2403
|
-
await this.
|
|
2404
|
-
await this.appendMutationAndApplyUnlocked(createClearEvaluator());
|
|
2405
|
-
});
|
|
2712
|
+
await this.coord.writeOp(createClearEvaluator());
|
|
2406
2713
|
}
|
|
2407
2714
|
async get(key) {
|
|
2408
|
-
return this.readOp((
|
|
2715
|
+
return this.coord.readOp((t) => t.get(key));
|
|
2409
2716
|
}
|
|
2410
2717
|
async hasKey(key) {
|
|
2411
|
-
return this.readOp((
|
|
2718
|
+
return this.coord.readOp((t) => t.hasKey(key));
|
|
2412
2719
|
}
|
|
2413
2720
|
async findFirst(key) {
|
|
2414
|
-
return this.readOp((
|
|
2721
|
+
return this.coord.readOp((t) => t.findFirst(key));
|
|
2415
2722
|
}
|
|
2416
2723
|
async findLast(key) {
|
|
2417
|
-
return this.readOp((
|
|
2724
|
+
return this.coord.readOp((t) => t.findLast(key));
|
|
2418
2725
|
}
|
|
2419
2726
|
async range(startKey, endKey, options) {
|
|
2420
|
-
return this.readOp((
|
|
2727
|
+
return this.coord.readOp((t) => t.range(startKey, endKey, options));
|
|
2421
2728
|
}
|
|
2422
2729
|
async snapshot() {
|
|
2423
|
-
return this.readOp((
|
|
2730
|
+
return this.coord.readOp((t) => t.snapshot());
|
|
2424
2731
|
}
|
|
2425
2732
|
async size() {
|
|
2426
|
-
return this.readOp((
|
|
2733
|
+
return this.coord.readOp((t) => t.size());
|
|
2427
2734
|
}
|
|
2428
2735
|
async assertInvariants() {
|
|
2429
|
-
await this.readOp((
|
|
2736
|
+
await this.coord.readOp((t) => t.assertInvariants());
|
|
2430
2737
|
}
|
|
2431
2738
|
async getStats() {
|
|
2432
|
-
return this.readOp((
|
|
2739
|
+
return this.coord.readOp((t) => t.getStats());
|
|
2433
2740
|
}
|
|
2434
2741
|
async peekFirst() {
|
|
2435
|
-
return this.readOp((
|
|
2742
|
+
return this.coord.readOp((t) => t.peekFirst());
|
|
2436
2743
|
}
|
|
2437
2744
|
async peekLast() {
|
|
2438
|
-
return this.readOp((
|
|
2745
|
+
return this.coord.readOp((t) => t.peekLast());
|
|
2439
2746
|
}
|
|
2440
2747
|
async peekById(entryId) {
|
|
2441
|
-
return this.readOp((
|
|
2748
|
+
return this.coord.readOp((t) => t.peekById(entryId));
|
|
2442
2749
|
}
|
|
2443
2750
|
async count(startKey, endKey, options) {
|
|
2444
|
-
return this.readOp((
|
|
2751
|
+
return this.coord.readOp((t) => t.count(startKey, endKey, options));
|
|
2445
2752
|
}
|
|
2446
2753
|
async nextHigherKey(key) {
|
|
2447
|
-
return this.readOp((
|
|
2754
|
+
return this.coord.readOp((t) => t.nextHigherKey(key));
|
|
2448
2755
|
}
|
|
2449
2756
|
async nextLowerKey(key) {
|
|
2450
|
-
return this.readOp((
|
|
2757
|
+
return this.coord.readOp((t) => t.nextLowerKey(key));
|
|
2451
2758
|
}
|
|
2452
2759
|
async getPairOrNextLower(key) {
|
|
2453
|
-
return this.readOp((
|
|
2760
|
+
return this.coord.readOp((t) => t.getPairOrNextLower(key));
|
|
2454
2761
|
}
|
|
2455
2762
|
async entries() {
|
|
2456
|
-
return this.readOp((
|
|
2763
|
+
return this.coord.readOp((t) => Array.from(t.entries()));
|
|
2457
2764
|
}
|
|
2458
2765
|
async entriesReversed() {
|
|
2459
|
-
return this.readOp((
|
|
2766
|
+
return this.coord.readOp((t) => Array.from(t.entriesReversed()));
|
|
2460
2767
|
}
|
|
2461
2768
|
async keys() {
|
|
2462
|
-
return this.readOp((
|
|
2769
|
+
return this.coord.readOp((t) => Array.from(t.keys()));
|
|
2463
2770
|
}
|
|
2464
2771
|
async values() {
|
|
2465
|
-
return this.readOp((
|
|
2772
|
+
return this.coord.readOp((t) => Array.from(t.values()));
|
|
2466
2773
|
}
|
|
2467
2774
|
async forEach(callback) {
|
|
2468
|
-
await this.readOp((
|
|
2469
|
-
|
|
2775
|
+
await this.coord.readOp((t) => {
|
|
2776
|
+
t.forEach(callback);
|
|
2777
|
+
});
|
|
2778
|
+
}
|
|
2779
|
+
async forEachRange(startKey, endKey, callback, options) {
|
|
2780
|
+
await this.coord.readOp((t) => {
|
|
2781
|
+
t.forEachRange(startKey, endKey, callback, options);
|
|
2470
2782
|
});
|
|
2471
2783
|
}
|
|
2472
2784
|
async *[Symbol.asyncIterator]() {
|
|
2473
2785
|
const all = await this.entries();
|
|
2474
|
-
for (const entry of all)
|
|
2475
|
-
yield entry;
|
|
2476
|
-
}
|
|
2786
|
+
for (const entry of all) yield entry;
|
|
2477
2787
|
}
|
|
2478
2788
|
async clone() {
|
|
2479
|
-
return this.readOp((
|
|
2789
|
+
return this.coord.readOp((t) => t.clone());
|
|
2480
2790
|
}
|
|
2481
2791
|
async toJSON() {
|
|
2482
|
-
return this.readOp((
|
|
2792
|
+
return this.coord.readOp((t) => t.toJSON());
|
|
2483
2793
|
}
|
|
2484
2794
|
static fromJSON(json, compareKeys) {
|
|
2485
2795
|
return InMemoryBTree.fromJSON(json, compareKeys);
|