@frostpillar/frostpillar-btree 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README-JA.md +95 -73
- package/README.md +96 -74
- package/dist/InMemoryBTree.d.ts +3 -36
- package/dist/btree/autoScale.d.ts +1 -0
- package/dist/btree/entry-lookup.d.ts +8 -0
- package/dist/btree/mutations.d.ts +1 -3
- package/dist/btree/rangeQuery.d.ts +4 -1
- package/dist/btree/rebalance-branch.d.ts +4 -0
- package/dist/btree/rebalance.d.ts +5 -2
- package/dist/btree/serialization.d.ts +3 -1
- package/dist/btree/split.d.ts +3 -0
- package/dist/btree/traversal.d.ts +7 -0
- package/dist/btree/types.d.ts +14 -3
- package/dist/{chunk-OWHENPGJ.js → chunk-OFXDCKLC.js} +663 -395
- package/dist/concurrency/ConcurrentInMemoryBTree.d.ts +3 -14
- package/dist/concurrency/coordinator.d.ts +21 -0
- package/dist/concurrency/syncLogValidation.d.ts +2 -0
- package/dist/core.cjs +663 -395
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1 -1
- package/dist/frostpillar-btree-core.min.js +1 -1
- package/dist/frostpillar-btree.min.js +1 -1
- package/dist/index.cjs +856 -545
- package/dist/index.d.ts +1 -1
- package/dist/index.js +190 -147
- package/package.json +2 -2
package/dist/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,70 @@ 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");
|
|
839
|
+
const leafWasEmpty = leafEntryCount(leaf) === 0;
|
|
731
840
|
leaf.entries.push(borrowed);
|
|
732
841
|
writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
|
|
733
|
-
|
|
842
|
+
if (leafWasEmpty) updateMinKeyInAncestors(leaf);
|
|
843
|
+
return true;
|
|
734
844
|
}
|
|
735
845
|
if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
|
|
736
846
|
const borrowed = leftSibling.entries.pop();
|
|
737
|
-
if (borrowed === void 0)
|
|
847
|
+
if (borrowed === void 0)
|
|
848
|
+
throw new BTreeInvariantError("left leaf borrow failed");
|
|
738
849
|
leafUnshiftEntry(leaf, borrowed);
|
|
739
850
|
parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
|
|
740
851
|
updateMinKeyInAncestors(leaf);
|
|
741
|
-
return;
|
|
852
|
+
return true;
|
|
742
853
|
}
|
|
854
|
+
return false;
|
|
855
|
+
};
|
|
856
|
+
var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
|
|
743
857
|
if (leftSibling !== null) {
|
|
744
858
|
mergeLeafEntries(leftSibling, leaf);
|
|
745
859
|
detachLeafFromChain(state, leaf);
|
|
@@ -748,14 +862,34 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
|
748
862
|
return;
|
|
749
863
|
}
|
|
750
864
|
if (rightSibling !== null) {
|
|
865
|
+
const leafWasEmpty = leafEntryCount(leaf) === 0;
|
|
751
866
|
mergeLeafEntries(leaf, rightSibling);
|
|
752
867
|
detachLeafFromChain(state, rightSibling);
|
|
868
|
+
if (leafWasEmpty) updateMinKeyInAncestors(leaf);
|
|
753
869
|
removeChildFromBranch(parent, leafIndex + 1);
|
|
754
870
|
rebalanceAfterBranchRemoval(state, parent);
|
|
755
871
|
return;
|
|
756
872
|
}
|
|
757
873
|
throw new BTreeInvariantError("no leaf siblings to rebalance");
|
|
758
874
|
};
|
|
875
|
+
var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
876
|
+
if (leaf === state.root) {
|
|
877
|
+
if (state.entryCount === 0) {
|
|
878
|
+
state.leftmostLeaf = leaf;
|
|
879
|
+
state.rightmostLeaf = leaf;
|
|
880
|
+
}
|
|
881
|
+
return;
|
|
882
|
+
}
|
|
883
|
+
if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
|
|
884
|
+
const parent = leaf.parent;
|
|
885
|
+
if (parent === null)
|
|
886
|
+
throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
|
|
887
|
+
const leafIndex = leaf.indexInParent;
|
|
888
|
+
const { left, right } = findLeafSiblings(parent, leafIndex);
|
|
889
|
+
if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
|
|
890
|
+
return;
|
|
891
|
+
mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
|
|
892
|
+
};
|
|
759
893
|
|
|
760
894
|
// src/btree/deleteRange.ts
|
|
761
895
|
var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
@@ -768,16 +902,16 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
|
768
902
|
}
|
|
769
903
|
return { leaf, idx };
|
|
770
904
|
};
|
|
771
|
-
var findRemoveEnd = (state, leaf,
|
|
905
|
+
var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
|
|
772
906
|
const count = leafEntryCount(leaf);
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
|
|
778
|
-
removeEnd += 1;
|
|
907
|
+
const lastEntry = leafEntryAt(leaf, count - 1);
|
|
908
|
+
const cmpLast = state.compareKeys(lastEntry.key, endKey);
|
|
909
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
910
|
+
return count;
|
|
779
911
|
}
|
|
780
|
-
|
|
912
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
913
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
|
|
914
|
+
return endBound < count ? endBound : count;
|
|
781
915
|
};
|
|
782
916
|
var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
783
917
|
if (state.entryKeys !== null) {
|
|
@@ -794,15 +928,14 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
|
794
928
|
updateMinKeyInAncestors(leaf);
|
|
795
929
|
}
|
|
796
930
|
const countAfterSplice = leafEntryCount(leaf);
|
|
931
|
+
const rebalThreshold = leafRebalanceThreshold(state);
|
|
797
932
|
let safetyGuard = state.minLeafEntries + 4;
|
|
798
|
-
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) <
|
|
933
|
+
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
|
|
799
934
|
rebalanceAfterLeafRemoval(state, leaf);
|
|
800
|
-
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
935
|
+
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
936
|
+
break;
|
|
801
937
|
safetyGuard -= 1;
|
|
802
938
|
}
|
|
803
|
-
if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
|
|
804
|
-
updateMinKeyInAncestors(leaf);
|
|
805
|
-
}
|
|
806
939
|
return countAfterSplice;
|
|
807
940
|
};
|
|
808
941
|
var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
|
|
@@ -825,10 +958,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
825
958
|
}
|
|
826
959
|
if (idx >= leafEntryCount(leaf)) break;
|
|
827
960
|
const count = leafEntryCount(leaf);
|
|
828
|
-
const removeEnd = findRemoveEnd(state, leaf,
|
|
961
|
+
const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
|
|
829
962
|
const removeCount = removeEnd - idx;
|
|
830
963
|
if (removeCount === 0) break;
|
|
831
|
-
const countAfterSplice = spliceLeafAndRebalance(
|
|
964
|
+
const countAfterSplice = spliceLeafAndRebalance(
|
|
965
|
+
state,
|
|
966
|
+
leaf,
|
|
967
|
+
idx,
|
|
968
|
+
removeCount
|
|
969
|
+
);
|
|
832
970
|
deleted += removeCount;
|
|
833
971
|
if (removeEnd < count) break;
|
|
834
972
|
if (!isLeafStillValid(state, leaf)) {
|
|
@@ -874,25 +1012,42 @@ var computeNextAutoScaleThreshold = (entryCount) => {
|
|
|
874
1012
|
}
|
|
875
1013
|
return Number.MAX_SAFE_INTEGER;
|
|
876
1014
|
};
|
|
877
|
-
var
|
|
878
|
-
if (typeof config.compareKeys !== "function") {
|
|
879
|
-
throw new BTreeValidationError("compareKeys must be a function.");
|
|
880
|
-
}
|
|
881
|
-
const autoScale = config.autoScale === true;
|
|
1015
|
+
var resolveInitialCapacity = (config, autoScale) => {
|
|
882
1016
|
if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
|
|
883
|
-
throw new BTreeValidationError(
|
|
1017
|
+
throw new BTreeValidationError(
|
|
1018
|
+
"autoScale conflicts with explicit capacity."
|
|
1019
|
+
);
|
|
884
1020
|
}
|
|
885
|
-
let maxLeafEntries;
|
|
886
|
-
let maxBranchChildren;
|
|
887
1021
|
if (autoScale) {
|
|
888
1022
|
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);
|
|
1023
|
+
return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
|
|
894
1024
|
}
|
|
1025
|
+
return {
|
|
1026
|
+
maxLeafEntries: normalizeNodeCapacity(
|
|
1027
|
+
config.maxLeafEntries,
|
|
1028
|
+
"maxLeafEntries",
|
|
1029
|
+
DEFAULT_MAX_LEAF_ENTRIES
|
|
1030
|
+
),
|
|
1031
|
+
maxBranchChildren: normalizeNodeCapacity(
|
|
1032
|
+
config.maxBranchChildren,
|
|
1033
|
+
"maxBranchChildren",
|
|
1034
|
+
DEFAULT_MAX_BRANCH_CHILDREN
|
|
1035
|
+
)
|
|
1036
|
+
};
|
|
1037
|
+
};
|
|
1038
|
+
var createInitialState = (config) => {
|
|
1039
|
+
if (typeof config.compareKeys !== "function") {
|
|
1040
|
+
throw new BTreeValidationError("compareKeys must be a function.");
|
|
1041
|
+
}
|
|
1042
|
+
const autoScale = config.autoScale === true;
|
|
1043
|
+
const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
|
|
1044
|
+
config,
|
|
1045
|
+
autoScale
|
|
1046
|
+
);
|
|
895
1047
|
const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
|
|
1048
|
+
const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
|
|
1049
|
+
config.deleteRebalancePolicy
|
|
1050
|
+
);
|
|
896
1051
|
const emptyLeaf = createLeafNode([], null);
|
|
897
1052
|
return {
|
|
898
1053
|
compareKeys: config.compareKeys,
|
|
@@ -908,6 +1063,7 @@ var createInitialState = (config) => {
|
|
|
908
1063
|
nextSequence: 0,
|
|
909
1064
|
entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
|
|
910
1065
|
autoScale,
|
|
1066
|
+
deleteRebalancePolicy,
|
|
911
1067
|
_nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
|
|
912
1068
|
_cursor: { leaf: emptyLeaf, index: 0 }
|
|
913
1069
|
};
|
|
@@ -923,7 +1079,9 @@ var maybeAutoScale = (state) => {
|
|
|
923
1079
|
state.maxBranchChildren = maxBranch;
|
|
924
1080
|
state.minBranchChildren = minOccupancy(maxBranch);
|
|
925
1081
|
}
|
|
926
|
-
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1082
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1083
|
+
state.entryCount
|
|
1084
|
+
);
|
|
927
1085
|
};
|
|
928
1086
|
var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
|
|
929
1087
|
if (!state.autoScale) {
|
|
@@ -950,6 +1108,14 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
|
|
|
950
1108
|
state.minLeafEntries = minOccupancy(normalizedLeaf);
|
|
951
1109
|
state.minBranchChildren = minOccupancy(normalizedBranch);
|
|
952
1110
|
};
|
|
1111
|
+
var resetAutoScaleToTier0 = (state) => {
|
|
1112
|
+
const tier = computeAutoScaleTier(0);
|
|
1113
|
+
state.maxLeafEntries = tier.maxLeaf;
|
|
1114
|
+
state.maxBranchChildren = tier.maxBranch;
|
|
1115
|
+
state.minLeafEntries = minOccupancy(tier.maxLeaf);
|
|
1116
|
+
state.minBranchChildren = minOccupancy(tier.maxBranch);
|
|
1117
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
1118
|
+
};
|
|
953
1119
|
|
|
954
1120
|
// src/btree/bulkLoad.ts
|
|
955
1121
|
var computeChunkBoundaries = (total, max, min) => {
|
|
@@ -970,7 +1136,11 @@ var computeChunkBoundaries = (total, max, min) => {
|
|
|
970
1136
|
return boundaries;
|
|
971
1137
|
};
|
|
972
1138
|
var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
973
|
-
const boundaries = computeChunkBoundaries(
|
|
1139
|
+
const boundaries = computeChunkBoundaries(
|
|
1140
|
+
entries.length,
|
|
1141
|
+
state.maxLeafEntries,
|
|
1142
|
+
state.minLeafEntries
|
|
1143
|
+
);
|
|
974
1144
|
const leaves = new Array(boundaries.length);
|
|
975
1145
|
let chunkStart = 0;
|
|
976
1146
|
for (let c = 0; c < boundaries.length; c += 1) {
|
|
@@ -978,7 +1148,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
|
978
1148
|
const chunk = new Array(chunkEnd - chunkStart);
|
|
979
1149
|
for (let i = chunkStart; i < chunkEnd; i += 1) {
|
|
980
1150
|
const seq = baseSequence + i;
|
|
981
|
-
chunk[i - chunkStart] =
|
|
1151
|
+
chunk[i - chunkStart] = createEntry(
|
|
1152
|
+
entries[i].key,
|
|
1153
|
+
seq,
|
|
1154
|
+
entries[i].value
|
|
1155
|
+
);
|
|
982
1156
|
ids[i] = seq;
|
|
983
1157
|
if (state.entryKeys !== null) {
|
|
984
1158
|
state.entryKeys.set(seq, entries[i].key);
|
|
@@ -1012,11 +1186,18 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
1012
1186
|
} else {
|
|
1013
1187
|
let currentLevel = leaves;
|
|
1014
1188
|
while (currentLevel.length > 1) {
|
|
1015
|
-
const bounds = computeChunkBoundaries(
|
|
1189
|
+
const bounds = computeChunkBoundaries(
|
|
1190
|
+
currentLevel.length,
|
|
1191
|
+
state.maxBranchChildren,
|
|
1192
|
+
state.minBranchChildren
|
|
1193
|
+
);
|
|
1016
1194
|
const nextLevel = new Array(bounds.length);
|
|
1017
1195
|
let start = 0;
|
|
1018
1196
|
for (let b = 0; b < bounds.length; b += 1) {
|
|
1019
|
-
nextLevel[b] = createBranchNode(
|
|
1197
|
+
nextLevel[b] = createBranchNode(
|
|
1198
|
+
currentLevel.slice(start, bounds[b]),
|
|
1199
|
+
null
|
|
1200
|
+
);
|
|
1020
1201
|
start = bounds[b];
|
|
1021
1202
|
}
|
|
1022
1203
|
currentLevel = nextLevel;
|
|
@@ -1027,9 +1208,12 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
1027
1208
|
return ids;
|
|
1028
1209
|
};
|
|
1029
1210
|
|
|
1030
|
-
// src/btree/
|
|
1211
|
+
// src/btree/split.ts
|
|
1031
1212
|
var insertChildAfter = (state, parent, existingChild, childToInsert) => {
|
|
1032
|
-
const newChildMinKey = {
|
|
1213
|
+
const newChildMinKey = {
|
|
1214
|
+
key: void 0,
|
|
1215
|
+
sequence: 0
|
|
1216
|
+
};
|
|
1033
1217
|
if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
|
|
1034
1218
|
throw new BTreeInvariantError("inserted child has no min key");
|
|
1035
1219
|
}
|
|
@@ -1067,7 +1251,10 @@ var splitLeaf = (state, leaf) => {
|
|
|
1067
1251
|
var splitBranch = (state, branch) => {
|
|
1068
1252
|
branchCompact(branch);
|
|
1069
1253
|
const splitAt = Math.ceil(branch.children.length / 2);
|
|
1070
|
-
const sibling = createBranchNode(
|
|
1254
|
+
const sibling = createBranchNode(
|
|
1255
|
+
branch.children.splice(splitAt),
|
|
1256
|
+
branch.parent
|
|
1257
|
+
);
|
|
1071
1258
|
branch.keys.splice(splitAt);
|
|
1072
1259
|
if (branch.parent === null) {
|
|
1073
1260
|
state.root = createBranchNode([branch, sibling], null);
|
|
@@ -1075,42 +1262,125 @@ var splitBranch = (state, branch) => {
|
|
|
1075
1262
|
}
|
|
1076
1263
|
insertChildAfter(state, branch.parent, branch, sibling);
|
|
1077
1264
|
};
|
|
1265
|
+
|
|
1266
|
+
// src/btree/entry-lookup.ts
|
|
1267
|
+
var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
1268
|
+
const targetLeaf = findLeafForKey(state, userKey, sequence);
|
|
1269
|
+
const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
|
|
1270
|
+
if (index >= leafEntryCount(targetLeaf)) return null;
|
|
1271
|
+
const entry = leafEntryAt(targetLeaf, index);
|
|
1272
|
+
if (entry.entryId !== sequence) return null;
|
|
1273
|
+
return { leaf: targetLeaf, index };
|
|
1274
|
+
};
|
|
1275
|
+
var peekEntryById = (state, entryId) => {
|
|
1276
|
+
if (state.entryKeys === null) {
|
|
1277
|
+
throw new BTreeInvariantError(
|
|
1278
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1279
|
+
);
|
|
1280
|
+
}
|
|
1281
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1282
|
+
if (userKey === void 0) return null;
|
|
1283
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1284
|
+
if (found === null) return null;
|
|
1285
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1286
|
+
return entry;
|
|
1287
|
+
};
|
|
1288
|
+
var updateEntryById = (state, entryId, newValue) => {
|
|
1289
|
+
if (state.entryKeys === null) {
|
|
1290
|
+
throw new BTreeInvariantError(
|
|
1291
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1295
|
+
if (userKey === void 0) return null;
|
|
1296
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1297
|
+
if (found === null) return null;
|
|
1298
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1299
|
+
const updated = createEntry(entry.key, entry.entryId, newValue);
|
|
1300
|
+
found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
|
|
1301
|
+
return updated;
|
|
1302
|
+
};
|
|
1303
|
+
var removeEntryById = (state, entryId) => {
|
|
1304
|
+
if (state.entryKeys === null) {
|
|
1305
|
+
throw new BTreeInvariantError(
|
|
1306
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1310
|
+
if (userKey === void 0) return null;
|
|
1311
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1312
|
+
if (found === null) return null;
|
|
1313
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1314
|
+
leafRemoveAt(found.leaf, found.index);
|
|
1315
|
+
state.entryCount -= 1;
|
|
1316
|
+
state.entryKeys.delete(entryId);
|
|
1317
|
+
if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
|
|
1318
|
+
updateMinKeyInAncestors(found.leaf);
|
|
1319
|
+
}
|
|
1320
|
+
if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
|
|
1321
|
+
rebalanceAfterLeafRemoval(state, found.leaf);
|
|
1322
|
+
}
|
|
1323
|
+
return entry;
|
|
1324
|
+
};
|
|
1325
|
+
|
|
1326
|
+
// src/btree/mutations.ts
|
|
1327
|
+
var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
|
|
1328
|
+
if (state.duplicateKeys === "allow") return null;
|
|
1329
|
+
if (insertAt > 0) {
|
|
1330
|
+
const candidate = leafEntryAt(targetLeaf, insertAt - 1);
|
|
1331
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1332
|
+
return {
|
|
1333
|
+
leaf: targetLeaf,
|
|
1334
|
+
physIndex: targetLeaf.entryOffset + insertAt - 1,
|
|
1335
|
+
entry: candidate
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
} else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
|
|
1339
|
+
const prevLeaf = targetLeaf.prev;
|
|
1340
|
+
const prevCount = leafEntryCount(prevLeaf);
|
|
1341
|
+
const candidate = leafEntryAt(prevLeaf, prevCount - 1);
|
|
1342
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1343
|
+
return {
|
|
1344
|
+
leaf: prevLeaf,
|
|
1345
|
+
physIndex: prevLeaf.entryOffset + prevCount - 1,
|
|
1346
|
+
entry: candidate
|
|
1347
|
+
};
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return null;
|
|
1351
|
+
};
|
|
1078
1352
|
var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
|
|
1079
1353
|
const sequence = state.nextSequence;
|
|
1080
1354
|
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;
|
|
1355
|
+
const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
|
|
1356
|
+
if (dup !== null) {
|
|
1357
|
+
if (state.duplicateKeys === "reject") {
|
|
1358
|
+
throw new BTreeValidationError("Duplicate key rejected.");
|
|
1101
1359
|
}
|
|
1360
|
+
dup.leaf.entries[dup.physIndex] = createEntry(
|
|
1361
|
+
dup.entry.key,
|
|
1362
|
+
dup.entry.entryId,
|
|
1363
|
+
value
|
|
1364
|
+
);
|
|
1365
|
+
return dup.entry.entryId;
|
|
1102
1366
|
}
|
|
1103
1367
|
if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
|
|
1104
1368
|
throw new BTreeValidationError("Sequence overflow.");
|
|
1105
1369
|
}
|
|
1106
1370
|
state.nextSequence += 1;
|
|
1107
|
-
leafInsertAt(
|
|
1371
|
+
leafInsertAt(
|
|
1372
|
+
targetLeaf,
|
|
1373
|
+
insertAt,
|
|
1374
|
+
createEntry(key, sequence, value)
|
|
1375
|
+
);
|
|
1108
1376
|
state.entryCount += 1;
|
|
1109
1377
|
if (state.entryKeys !== null) {
|
|
1110
1378
|
state.entryKeys.set(sequence, key);
|
|
1111
1379
|
}
|
|
1112
|
-
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1113
|
-
|
|
1380
|
+
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1381
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1382
|
+
if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
|
|
1383
|
+
splitLeaf(state, targetLeaf);
|
|
1114
1384
|
maybeAutoScale(state);
|
|
1115
1385
|
return sequence;
|
|
1116
1386
|
};
|
|
@@ -1121,7 +1391,8 @@ var putEntry = (state, key, value) => {
|
|
|
1121
1391
|
var popFirstEntry = (state) => {
|
|
1122
1392
|
if (state.entryCount === 0) return null;
|
|
1123
1393
|
const firstEntry = leafShiftEntry(state.leftmostLeaf);
|
|
1124
|
-
if (firstEntry === void 0)
|
|
1394
|
+
if (firstEntry === void 0)
|
|
1395
|
+
throw new BTreeInvariantError("leftmost leaf empty but count > 0");
|
|
1125
1396
|
state.entryCount -= 1;
|
|
1126
1397
|
if (state.entryKeys !== null) {
|
|
1127
1398
|
state.entryKeys.delete(firstEntry.entryId);
|
|
@@ -1137,7 +1408,8 @@ var popFirstEntry = (state) => {
|
|
|
1137
1408
|
var popLastEntry = (state) => {
|
|
1138
1409
|
if (state.entryCount === 0) return null;
|
|
1139
1410
|
const lastEntry = leafPopEntry(state.rightmostLeaf);
|
|
1140
|
-
if (lastEntry === void 0)
|
|
1411
|
+
if (lastEntry === void 0)
|
|
1412
|
+
throw new BTreeInvariantError("rightmost leaf empty but count > 0");
|
|
1141
1413
|
state.entryCount -= 1;
|
|
1142
1414
|
if (state.entryKeys !== null) {
|
|
1143
1415
|
state.entryKeys.delete(lastEntry.entryId);
|
|
@@ -1156,64 +1428,14 @@ var removeFirstMatchingEntry = (state, key) => {
|
|
|
1156
1428
|
leafRemoveAt(targetLeaf, removeAt);
|
|
1157
1429
|
state.entryCount -= 1;
|
|
1158
1430
|
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.");
|
|
1431
|
+
state.entryKeys.delete(targetEntry.entryId);
|
|
1209
1432
|
}
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
return entry;
|
|
1433
|
+
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1434
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1435
|
+
if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
|
|
1436
|
+
rebalanceAfterLeafRemoval(state, targetLeaf);
|
|
1437
|
+
}
|
|
1438
|
+
return targetEntry;
|
|
1217
1439
|
};
|
|
1218
1440
|
var putManyEntries = (state, entries) => {
|
|
1219
1441
|
if (entries.length === 0) return [];
|
|
@@ -1221,7 +1443,9 @@ var putManyEntries = (state, entries) => {
|
|
|
1221
1443
|
for (let i = 1; i < entries.length; i += 1) {
|
|
1222
1444
|
const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
|
|
1223
1445
|
if (cmp > 0) {
|
|
1224
|
-
throw new BTreeValidationError(
|
|
1446
|
+
throw new BTreeValidationError(
|
|
1447
|
+
"putMany: entries not in ascending order."
|
|
1448
|
+
);
|
|
1225
1449
|
}
|
|
1226
1450
|
if (strictlyAscending && cmp === 0) {
|
|
1227
1451
|
throw new BTreeValidationError(
|
|
@@ -1234,7 +1458,12 @@ var putManyEntries = (state, entries) => {
|
|
|
1234
1458
|
let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
|
|
1235
1459
|
for (let i = 0; i < entries.length; i += 1) {
|
|
1236
1460
|
const entry = entries[i];
|
|
1237
|
-
const targetLeaf = findLeafFromHint(
|
|
1461
|
+
const targetLeaf = findLeafFromHint(
|
|
1462
|
+
state,
|
|
1463
|
+
hintLeaf,
|
|
1464
|
+
entry.key,
|
|
1465
|
+
state.nextSequence
|
|
1466
|
+
);
|
|
1238
1467
|
ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
|
|
1239
1468
|
hintLeaf = targetLeaf;
|
|
1240
1469
|
}
|
|
@@ -1249,7 +1478,8 @@ var buildConfigFromState = (state) => {
|
|
|
1249
1478
|
compareKeys: state.compareKeys,
|
|
1250
1479
|
duplicateKeys: state.duplicateKeys,
|
|
1251
1480
|
enableEntryIdLookup: state.entryKeys !== null,
|
|
1252
|
-
autoScale: state.autoScale
|
|
1481
|
+
autoScale: state.autoScale,
|
|
1482
|
+
deleteRebalancePolicy: state.deleteRebalancePolicy
|
|
1253
1483
|
};
|
|
1254
1484
|
if (!state.autoScale) {
|
|
1255
1485
|
config.maxLeafEntries = state.maxLeafEntries;
|
|
@@ -1269,17 +1499,17 @@ var serializeToJSON = (state) => {
|
|
|
1269
1499
|
}
|
|
1270
1500
|
leaf = leaf.next;
|
|
1271
1501
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
enableEntryIdLookup: state.entryKeys !== null,
|
|
1279
|
-
autoScale: state.autoScale
|
|
1280
|
-
},
|
|
1281
|
-
entries
|
|
1502
|
+
const config = {
|
|
1503
|
+
maxLeafEntries: state.maxLeafEntries,
|
|
1504
|
+
maxBranchChildren: state.maxBranchChildren,
|
|
1505
|
+
duplicateKeys: state.duplicateKeys,
|
|
1506
|
+
enableEntryIdLookup: state.entryKeys !== null,
|
|
1507
|
+
autoScale: state.autoScale
|
|
1282
1508
|
};
|
|
1509
|
+
if (state.deleteRebalancePolicy !== "standard") {
|
|
1510
|
+
config.deleteRebalancePolicy = state.deleteRebalancePolicy;
|
|
1511
|
+
}
|
|
1512
|
+
return { version: 1, config, entries };
|
|
1283
1513
|
};
|
|
1284
1514
|
var MAX_SERIALIZED_ENTRIES = 1e6;
|
|
1285
1515
|
var validateStructure = (json) => {
|
|
@@ -1342,13 +1572,28 @@ var validateBTreeJSON = (json) => {
|
|
|
1342
1572
|
validateStructure(json);
|
|
1343
1573
|
validateConfig(json.config);
|
|
1344
1574
|
};
|
|
1575
|
+
var validateBTreeJSONSortOrder = (json, compareKeys) => {
|
|
1576
|
+
const strict = json.config.duplicateKeys !== "allow";
|
|
1577
|
+
for (let i = 1; i < json.entries.length; i += 1) {
|
|
1578
|
+
const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
|
|
1579
|
+
if (cmp > 0) {
|
|
1580
|
+
throw new BTreeValidationError("fromJSON: entries not sorted.");
|
|
1581
|
+
}
|
|
1582
|
+
if (strict && cmp === 0) {
|
|
1583
|
+
throw new BTreeValidationError(
|
|
1584
|
+
'fromJSON: duplicate keys require duplicateKeys "allow".'
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
};
|
|
1345
1589
|
var buildConfigFromJSON = (json, compareKeys) => {
|
|
1346
1590
|
const cfg = json.config;
|
|
1347
1591
|
const config = {
|
|
1348
1592
|
compareKeys,
|
|
1349
1593
|
duplicateKeys: cfg.duplicateKeys,
|
|
1350
1594
|
enableEntryIdLookup: cfg.enableEntryIdLookup,
|
|
1351
|
-
autoScale: cfg.autoScale
|
|
1595
|
+
autoScale: cfg.autoScale,
|
|
1596
|
+
deleteRebalancePolicy: cfg.deleteRebalancePolicy
|
|
1352
1597
|
};
|
|
1353
1598
|
if (!cfg.autoScale) {
|
|
1354
1599
|
config.maxLeafEntries = cfg.maxLeafEntries;
|
|
@@ -1357,6 +1602,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
|
|
|
1357
1602
|
return config;
|
|
1358
1603
|
};
|
|
1359
1604
|
|
|
1605
|
+
// src/btree/traversal.ts
|
|
1606
|
+
var snapshotEntries = (state) => {
|
|
1607
|
+
const result = new Array(state.entryCount);
|
|
1608
|
+
let leaf = state.leftmostLeaf;
|
|
1609
|
+
let writeIdx = 0;
|
|
1610
|
+
while (leaf !== null) {
|
|
1611
|
+
const count = leafEntryCount(leaf);
|
|
1612
|
+
for (let i = 0; i < count; i += 1) {
|
|
1613
|
+
result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
|
|
1614
|
+
}
|
|
1615
|
+
leaf = leaf.next;
|
|
1616
|
+
}
|
|
1617
|
+
return result;
|
|
1618
|
+
};
|
|
1619
|
+
var collectInternalEntries = (state) => {
|
|
1620
|
+
const result = new Array(state.entryCount);
|
|
1621
|
+
let leaf = state.leftmostLeaf;
|
|
1622
|
+
let writeIdx = 0;
|
|
1623
|
+
while (leaf !== null) {
|
|
1624
|
+
const count = leafEntryCount(leaf);
|
|
1625
|
+
for (let i = 0; i < count; i += 1) {
|
|
1626
|
+
result[writeIdx++] = leafEntryAt(leaf, i);
|
|
1627
|
+
}
|
|
1628
|
+
leaf = leaf.next;
|
|
1629
|
+
}
|
|
1630
|
+
return result;
|
|
1631
|
+
};
|
|
1632
|
+
var forEachEntry = (state, callback, thisArg) => {
|
|
1633
|
+
let leaf = state.leftmostLeaf;
|
|
1634
|
+
while (leaf !== null) {
|
|
1635
|
+
const count = leafEntryCount(leaf);
|
|
1636
|
+
for (let i = 0; i < count; i += 1) {
|
|
1637
|
+
callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
|
|
1638
|
+
}
|
|
1639
|
+
leaf = leaf.next;
|
|
1640
|
+
}
|
|
1641
|
+
};
|
|
1642
|
+
|
|
1360
1643
|
// src/btree/integrity-helpers.ts
|
|
1361
1644
|
var nodeMinKey = (node) => {
|
|
1362
1645
|
if (isLeafNode(node)) {
|
|
@@ -1365,7 +1648,10 @@ var nodeMinKey = (node) => {
|
|
|
1365
1648
|
return { key: e.key, sequence: e.entryId };
|
|
1366
1649
|
}
|
|
1367
1650
|
if (node.childOffset >= node.keys.length) return null;
|
|
1368
|
-
return {
|
|
1651
|
+
return {
|
|
1652
|
+
key: node.keys[node.childOffset].key,
|
|
1653
|
+
sequence: node.keys[node.childOffset].sequence
|
|
1654
|
+
};
|
|
1369
1655
|
};
|
|
1370
1656
|
var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
|
|
1371
1657
|
const cmp = comparator(leftKey, rightKey);
|
|
@@ -1387,9 +1673,7 @@ var getNodeMaxKey = (node) => {
|
|
|
1387
1673
|
};
|
|
1388
1674
|
var validateComparatorResult = (result) => {
|
|
1389
1675
|
if (!Number.isFinite(result)) {
|
|
1390
|
-
throw new BTreeValidationError(
|
|
1391
|
-
"compareKeys must return a finite number."
|
|
1392
|
-
);
|
|
1676
|
+
throw new BTreeValidationError("compareKeys must return a finite number.");
|
|
1393
1677
|
}
|
|
1394
1678
|
return result;
|
|
1395
1679
|
};
|
|
@@ -1449,6 +1733,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
|
|
|
1449
1733
|
throw error;
|
|
1450
1734
|
}
|
|
1451
1735
|
};
|
|
1736
|
+
var validateAdjacentLeafOrdering = (state, previous, cursor) => {
|
|
1737
|
+
const prevMax = getNodeMaxKey(previous);
|
|
1738
|
+
const currentMin = nodeMinKey(cursor);
|
|
1739
|
+
if (prevMax === null || currentMin === null) {
|
|
1740
|
+
throw new BTreeInvariantError(
|
|
1741
|
+
"Non-empty tree leaf chain contains empty leaf node."
|
|
1742
|
+
);
|
|
1743
|
+
}
|
|
1744
|
+
if (compareNodeKeys(
|
|
1745
|
+
state.compareKeys,
|
|
1746
|
+
prevMax.key,
|
|
1747
|
+
prevMax.sequence,
|
|
1748
|
+
currentMin.key,
|
|
1749
|
+
currentMin.sequence
|
|
1750
|
+
) > 0) {
|
|
1751
|
+
throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
|
|
1752
|
+
}
|
|
1753
|
+
const prevCount = leafEntryCount(previous);
|
|
1754
|
+
const curCount = leafEntryCount(cursor);
|
|
1755
|
+
if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
|
|
1756
|
+
leafEntryAt(previous, prevCount - 1).key,
|
|
1757
|
+
leafEntryAt(cursor, 0).key
|
|
1758
|
+
) === 0) {
|
|
1759
|
+
throw new BTreeInvariantError(
|
|
1760
|
+
"Duplicate user key detected across adjacent leaves with uniqueness policy."
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
};
|
|
1452
1764
|
var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
1453
1765
|
if (!isLeafNode(cursor)) {
|
|
1454
1766
|
throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
|
|
@@ -1460,22 +1772,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
|
1460
1772
|
throw new BTreeInvariantError("Leaf prev pointer mismatch.");
|
|
1461
1773
|
}
|
|
1462
1774
|
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
|
-
}
|
|
1775
|
+
validateAdjacentLeafOrdering(state, previous, cursor);
|
|
1479
1776
|
}
|
|
1480
1777
|
};
|
|
1481
1778
|
var validateLeafLinks = (state, expectedLeafCount) => {
|
|
@@ -1484,7 +1781,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1484
1781
|
throw new BTreeInvariantError("Empty tree root must be a leaf node.");
|
|
1485
1782
|
}
|
|
1486
1783
|
if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
|
|
1487
|
-
throw new BTreeInvariantError(
|
|
1784
|
+
throw new BTreeInvariantError(
|
|
1785
|
+
"Empty tree leaf pointers must reference root leaf."
|
|
1786
|
+
);
|
|
1488
1787
|
}
|
|
1489
1788
|
return;
|
|
1490
1789
|
}
|
|
@@ -1509,7 +1808,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1509
1808
|
throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
|
|
1510
1809
|
}
|
|
1511
1810
|
if (leafCount !== expectedLeafCount) {
|
|
1512
|
-
throw new BTreeInvariantError(
|
|
1811
|
+
throw new BTreeInvariantError(
|
|
1812
|
+
"Leaf chain count mismatch with tree traversal count."
|
|
1813
|
+
);
|
|
1513
1814
|
}
|
|
1514
1815
|
};
|
|
1515
1816
|
|
|
@@ -1536,7 +1837,9 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1536
1837
|
leafEntryAt(node, index - 1).key,
|
|
1537
1838
|
leafEntryAt(node, index).key
|
|
1538
1839
|
) === 0) {
|
|
1539
|
-
throw new BTreeInvariantError(
|
|
1840
|
+
throw new BTreeInvariantError(
|
|
1841
|
+
"Duplicate user key detected in tree with uniqueness policy."
|
|
1842
|
+
);
|
|
1540
1843
|
}
|
|
1541
1844
|
}
|
|
1542
1845
|
}
|
|
@@ -1544,12 +1847,7 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1544
1847
|
const first = leafEntryAt(node, index - 2);
|
|
1545
1848
|
const second = leafEntryAt(node, index - 1);
|
|
1546
1849
|
const third = leafEntryAt(node, index);
|
|
1547
|
-
assertTransitivityAsInvariant(
|
|
1548
|
-
state,
|
|
1549
|
-
first.key,
|
|
1550
|
-
second.key,
|
|
1551
|
-
third.key
|
|
1552
|
-
);
|
|
1850
|
+
assertTransitivityAsInvariant(state, first.key, second.key, third.key);
|
|
1553
1851
|
}
|
|
1554
1852
|
if (count > state.maxLeafEntries) {
|
|
1555
1853
|
throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
|
|
@@ -1558,9 +1856,14 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1558
1856
|
var validateLeafNode = (state, node, depth) => {
|
|
1559
1857
|
validateLeafNodeOrdering(state, node);
|
|
1560
1858
|
const count = leafEntryCount(node);
|
|
1561
|
-
|
|
1859
|
+
let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
|
|
1860
|
+
if (state.deleteRebalancePolicy === "lazy") {
|
|
1861
|
+
baseMinLeaf = applyLazyThreshold(baseMinLeaf);
|
|
1862
|
+
}
|
|
1562
1863
|
if (node !== state.root && count < baseMinLeaf) {
|
|
1563
|
-
throw new BTreeInvariantError(
|
|
1864
|
+
throw new BTreeInvariantError(
|
|
1865
|
+
"Non-root leaf node violates minimum occupancy."
|
|
1866
|
+
);
|
|
1564
1867
|
}
|
|
1565
1868
|
const first = count === 0 ? null : leafEntryAt(node, 0);
|
|
1566
1869
|
const last = count === 0 ? null : leafEntryAt(node, count - 1);
|
|
@@ -1582,76 +1885,110 @@ var validateBranchStructure = (state, node) => {
|
|
|
1582
1885
|
}
|
|
1583
1886
|
const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
|
|
1584
1887
|
if (node !== state.root && liveCount < baseMinBranch) {
|
|
1585
|
-
throw new BTreeInvariantError(
|
|
1888
|
+
throw new BTreeInvariantError(
|
|
1889
|
+
"Non-root branch node violates minimum occupancy."
|
|
1890
|
+
);
|
|
1586
1891
|
}
|
|
1587
1892
|
if (liveCount > state.maxBranchChildren) {
|
|
1588
1893
|
throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
|
|
1589
1894
|
}
|
|
1590
1895
|
if (node.keys.length !== node.children.length) {
|
|
1591
|
-
throw new BTreeInvariantError(
|
|
1896
|
+
throw new BTreeInvariantError(
|
|
1897
|
+
"Branch keys array length does not match children array length."
|
|
1898
|
+
);
|
|
1592
1899
|
}
|
|
1593
1900
|
};
|
|
1594
1901
|
var validateBranchChild = (state, node, childIndex, depth) => {
|
|
1595
1902
|
const child = node.children[childIndex];
|
|
1596
1903
|
if (child.parent !== node) {
|
|
1597
|
-
throw new BTreeInvariantError(
|
|
1904
|
+
throw new BTreeInvariantError(
|
|
1905
|
+
"Child-parent pointer mismatch in branch node."
|
|
1906
|
+
);
|
|
1598
1907
|
}
|
|
1599
1908
|
if (child.indexInParent !== childIndex) {
|
|
1600
|
-
throw new BTreeInvariantError(
|
|
1909
|
+
throw new BTreeInvariantError(
|
|
1910
|
+
"Child indexInParent does not match actual position in parent."
|
|
1911
|
+
);
|
|
1601
1912
|
}
|
|
1602
1913
|
const childValidation = validateNode(state, child, depth + 1);
|
|
1603
1914
|
if (childValidation.minKey === null || childValidation.maxKey === null) {
|
|
1604
|
-
throw new BTreeInvariantError(
|
|
1915
|
+
throw new BTreeInvariantError(
|
|
1916
|
+
"Branch child must not be empty in non-root branch tree."
|
|
1917
|
+
);
|
|
1605
1918
|
}
|
|
1606
1919
|
const cachedMinKey = node.keys[childIndex];
|
|
1607
1920
|
const actualMinKey = nodeMinKey(child);
|
|
1608
|
-
if (actualMinKey === null || compareNodeKeys(
|
|
1609
|
-
|
|
1921
|
+
if (actualMinKey === null || compareNodeKeys(
|
|
1922
|
+
state.compareKeys,
|
|
1923
|
+
cachedMinKey.key,
|
|
1924
|
+
cachedMinKey.sequence,
|
|
1925
|
+
actualMinKey.key,
|
|
1926
|
+
actualMinKey.sequence
|
|
1927
|
+
) !== 0) {
|
|
1928
|
+
throw new BTreeInvariantError(
|
|
1929
|
+
"Branch cached key does not match actual child minimum key."
|
|
1930
|
+
);
|
|
1610
1931
|
}
|
|
1611
1932
|
return childValidation;
|
|
1612
1933
|
};
|
|
1934
|
+
var mergeChildValidation = (state, accumulated, childValidation) => {
|
|
1935
|
+
if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
|
|
1936
|
+
throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
|
|
1937
|
+
}
|
|
1938
|
+
if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
|
|
1939
|
+
accumulated.leafDepth = childValidation.leafDepth;
|
|
1940
|
+
}
|
|
1941
|
+
if (accumulated.previousChildMax !== null && compareNodeKeys(
|
|
1942
|
+
state.compareKeys,
|
|
1943
|
+
accumulated.previousChildMax.key,
|
|
1944
|
+
accumulated.previousChildMax.sequence,
|
|
1945
|
+
childValidation.minKey.key,
|
|
1946
|
+
childValidation.minKey.sequence
|
|
1947
|
+
) >= 0) {
|
|
1948
|
+
throw new BTreeInvariantError(
|
|
1949
|
+
"Branch child key ranges are not strictly ordered."
|
|
1950
|
+
);
|
|
1951
|
+
}
|
|
1952
|
+
if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
|
|
1953
|
+
accumulated.maxKey = childValidation.maxKey;
|
|
1954
|
+
accumulated.previousChildMax = childValidation.maxKey;
|
|
1955
|
+
accumulated.leafCount += childValidation.leafCount;
|
|
1956
|
+
accumulated.branchCount += childValidation.branchCount;
|
|
1957
|
+
accumulated.entryCount += childValidation.entryCount;
|
|
1958
|
+
};
|
|
1613
1959
|
var validateNode = (state, node, depth) => {
|
|
1614
1960
|
if (isLeafNode(node)) {
|
|
1615
1961
|
return validateLeafNode(state, node, depth);
|
|
1616
1962
|
}
|
|
1617
1963
|
validateBranchStructure(state, node);
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1964
|
+
const acc = {
|
|
1965
|
+
leafDepth: null,
|
|
1966
|
+
leafCount: 0,
|
|
1967
|
+
branchCount: 1,
|
|
1968
|
+
entryCount: 0,
|
|
1969
|
+
minKey: null,
|
|
1970
|
+
maxKey: null,
|
|
1971
|
+
previousChildMax: null
|
|
1972
|
+
};
|
|
1625
1973
|
for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
|
|
1626
1974
|
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;
|
|
1975
|
+
mergeChildValidation(state, acc, childValidation);
|
|
1648
1976
|
}
|
|
1649
|
-
return {
|
|
1977
|
+
return {
|
|
1978
|
+
minKey: acc.minKey,
|
|
1979
|
+
maxKey: acc.maxKey,
|
|
1980
|
+
leafDepth: acc.leafDepth,
|
|
1981
|
+
leafCount: acc.leafCount,
|
|
1982
|
+
branchCount: acc.branchCount,
|
|
1983
|
+
entryCount: acc.entryCount
|
|
1984
|
+
};
|
|
1650
1985
|
};
|
|
1651
1986
|
var assertInvariants = (state) => {
|
|
1652
1987
|
const validation = validateNode(state, state.root, 0);
|
|
1653
1988
|
if (validation.entryCount !== state.entryCount) {
|
|
1654
|
-
throw new BTreeInvariantError(
|
|
1989
|
+
throw new BTreeInvariantError(
|
|
1990
|
+
"Index entry count mismatch between tree traversal and tracked state."
|
|
1991
|
+
);
|
|
1655
1992
|
}
|
|
1656
1993
|
validateLeafLinks(state, validation.leafCount);
|
|
1657
1994
|
};
|
|
@@ -1707,7 +2044,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1707
2044
|
remove(key) {
|
|
1708
2045
|
const entry = removeFirstMatchingEntry(this.state, key);
|
|
1709
2046
|
if (entry === null) return null;
|
|
1710
|
-
return
|
|
2047
|
+
return freezeEntry(entry);
|
|
1711
2048
|
}
|
|
1712
2049
|
removeById(entryId) {
|
|
1713
2050
|
if (this.state.entryKeys === null) {
|
|
@@ -1715,7 +2052,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1715
2052
|
}
|
|
1716
2053
|
const entry = removeEntryById(this.state, entryId);
|
|
1717
2054
|
if (entry === null) return null;
|
|
1718
|
-
return
|
|
2055
|
+
return freezeEntry(entry);
|
|
1719
2056
|
}
|
|
1720
2057
|
peekById(entryId) {
|
|
1721
2058
|
if (this.state.entryKeys === null) {
|
|
@@ -1723,7 +2060,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1723
2060
|
}
|
|
1724
2061
|
const entry = peekEntryById(this.state, entryId);
|
|
1725
2062
|
if (entry === null) return null;
|
|
1726
|
-
return
|
|
2063
|
+
return freezeEntry(entry);
|
|
1727
2064
|
}
|
|
1728
2065
|
updateById(entryId, value) {
|
|
1729
2066
|
if (this.state.entryKeys === null) {
|
|
@@ -1731,30 +2068,30 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1731
2068
|
}
|
|
1732
2069
|
const entry = updateEntryById(this.state, entryId, value);
|
|
1733
2070
|
if (entry === null) return null;
|
|
1734
|
-
return
|
|
2071
|
+
return freezeEntry(entry);
|
|
1735
2072
|
}
|
|
1736
2073
|
popFirst() {
|
|
1737
2074
|
const entry = popFirstEntry(this.state);
|
|
1738
2075
|
if (entry === null) return null;
|
|
1739
|
-
return
|
|
2076
|
+
return freezeEntry(entry);
|
|
1740
2077
|
}
|
|
1741
2078
|
peekFirst() {
|
|
1742
2079
|
if (this.state.entryCount === 0) {
|
|
1743
2080
|
return null;
|
|
1744
2081
|
}
|
|
1745
|
-
return
|
|
2082
|
+
return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
|
|
1746
2083
|
}
|
|
1747
2084
|
peekLast() {
|
|
1748
2085
|
if (this.state.entryCount === 0) {
|
|
1749
2086
|
return null;
|
|
1750
2087
|
}
|
|
1751
2088
|
const leaf = this.state.rightmostLeaf;
|
|
1752
|
-
return
|
|
2089
|
+
return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
|
|
1753
2090
|
}
|
|
1754
2091
|
popLast() {
|
|
1755
2092
|
const entry = popLastEntry(this.state);
|
|
1756
2093
|
if (entry === null) return null;
|
|
1757
|
-
return
|
|
2094
|
+
return freezeEntry(entry);
|
|
1758
2095
|
}
|
|
1759
2096
|
clear() {
|
|
1760
2097
|
const emptyLeaf = createLeafNode([], null);
|
|
@@ -1764,16 +2101,9 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1764
2101
|
this.state.entryCount = 0;
|
|
1765
2102
|
this.state._cursor.leaf = emptyLeaf;
|
|
1766
2103
|
this.state._cursor.index = 0;
|
|
1767
|
-
|
|
1768
|
-
this.state.entryKeys.clear();
|
|
1769
|
-
}
|
|
2104
|
+
this.state.entryKeys?.clear();
|
|
1770
2105
|
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);
|
|
2106
|
+
resetAutoScaleToTier0(this.state);
|
|
1777
2107
|
}
|
|
1778
2108
|
}
|
|
1779
2109
|
get(key) {
|
|
@@ -1787,65 +2117,39 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1787
2117
|
findFirst(key) {
|
|
1788
2118
|
const found = findFirstMatchingUserKey(this.state, key);
|
|
1789
2119
|
if (found === null) return null;
|
|
1790
|
-
return
|
|
2120
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1791
2121
|
}
|
|
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
2122
|
findLast(key) {
|
|
1797
2123
|
const found = findLastMatchingUserKey(this.state, key);
|
|
1798
2124
|
if (found === null) return null;
|
|
1799
|
-
return
|
|
2125
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1800
2126
|
}
|
|
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
2127
|
nextHigherKey(key) {
|
|
1806
2128
|
return findNextHigherKey(this.state, key);
|
|
1807
2129
|
}
|
|
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
2130
|
nextLowerKey(key) {
|
|
1813
2131
|
return findNextLowerKey(this.state, key);
|
|
1814
2132
|
}
|
|
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
2133
|
getPairOrNextLower(key) {
|
|
1821
2134
|
const found = findPairOrNextLower(this.state, key);
|
|
1822
2135
|
if (found === null) return null;
|
|
1823
|
-
return
|
|
2136
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1824
2137
|
}
|
|
1825
|
-
/**
|
|
1826
|
-
* Returns the number of entries whose keys fall within [`startKey`, `endKey`].
|
|
1827
|
-
* Pass `options` to make either bound exclusive.
|
|
1828
|
-
*/
|
|
1829
2138
|
count(startKey, endKey, options) {
|
|
1830
2139
|
return countRangeEntries(this.state, startKey, endKey, options);
|
|
1831
2140
|
}
|
|
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
2141
|
deleteRange(startKey, endKey, options) {
|
|
1838
2142
|
return deleteRangeEntries(this.state, startKey, endKey, options);
|
|
1839
2143
|
}
|
|
1840
2144
|
range(startKey, endKey, options) {
|
|
1841
|
-
return
|
|
2145
|
+
return rangeQueryPublicEntries(this.state, startKey, endKey, options);
|
|
1842
2146
|
}
|
|
1843
2147
|
*entries() {
|
|
1844
2148
|
let leaf = this.state.leftmostLeaf;
|
|
1845
2149
|
while (leaf !== null) {
|
|
1846
2150
|
const count = leafEntryCount(leaf);
|
|
1847
2151
|
for (let i = 0; i < count; i += 1) {
|
|
1848
|
-
yield
|
|
2152
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1849
2153
|
}
|
|
1850
2154
|
leaf = leaf.next;
|
|
1851
2155
|
}
|
|
@@ -1855,7 +2159,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1855
2159
|
while (leaf !== null) {
|
|
1856
2160
|
const count = leafEntryCount(leaf);
|
|
1857
2161
|
for (let i = count - 1; i >= 0; i -= 1) {
|
|
1858
|
-
yield
|
|
2162
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1859
2163
|
}
|
|
1860
2164
|
leaf = leaf.prev;
|
|
1861
2165
|
}
|
|
@@ -1873,55 +2177,26 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1873
2177
|
[Symbol.iterator]() {
|
|
1874
2178
|
return this.entries();
|
|
1875
2179
|
}
|
|
2180
|
+
forEachRange(startKey, endKey, callback, options) {
|
|
2181
|
+
forEachRangeEntries(this.state, startKey, endKey, callback, options);
|
|
2182
|
+
}
|
|
1876
2183
|
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
|
-
}
|
|
2184
|
+
forEachEntry(this.state, callback, thisArg);
|
|
1885
2185
|
}
|
|
1886
2186
|
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;
|
|
2187
|
+
return snapshotEntries(this.state);
|
|
1898
2188
|
}
|
|
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
2189
|
clone() {
|
|
1907
|
-
const cloned = new _InMemoryBTree(
|
|
2190
|
+
const cloned = new _InMemoryBTree(
|
|
2191
|
+
buildConfigFromState(this.state)
|
|
2192
|
+
);
|
|
1908
2193
|
applyAutoScaleCapacitySnapshot(
|
|
1909
2194
|
cloned.state,
|
|
1910
2195
|
this.state.maxLeafEntries,
|
|
1911
2196
|
this.state.maxBranchChildren
|
|
1912
2197
|
);
|
|
1913
2198
|
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);
|
|
2199
|
+
cloned.putMany(collectInternalEntries(this.state));
|
|
1925
2200
|
}
|
|
1926
2201
|
return cloned;
|
|
1927
2202
|
}
|
|
@@ -1930,26 +2205,19 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1930
2205
|
}
|
|
1931
2206
|
static fromJSON(json, compareKeys) {
|
|
1932
2207
|
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));
|
|
2208
|
+
validateBTreeJSONSortOrder(json, compareKeys);
|
|
2209
|
+
const tree = new _InMemoryBTree(
|
|
2210
|
+
buildConfigFromJSON(json, compareKeys)
|
|
2211
|
+
);
|
|
1946
2212
|
applyAutoScaleCapacitySnapshot(
|
|
1947
2213
|
tree.state,
|
|
1948
2214
|
json.config.maxLeafEntries,
|
|
1949
2215
|
json.config.maxBranchChildren
|
|
1950
2216
|
);
|
|
1951
2217
|
if (json.entries.length > 0) {
|
|
1952
|
-
const pairs = new Array(
|
|
2218
|
+
const pairs = new Array(
|
|
2219
|
+
json.entries.length
|
|
2220
|
+
);
|
|
1953
2221
|
for (let i = 0; i < json.entries.length; i += 1) {
|
|
1954
2222
|
pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
|
|
1955
2223
|
}
|
|
@@ -1993,13 +2261,17 @@ var assertNeverMutation = (mutation) => {
|
|
|
1993
2261
|
var validatePutManyEntries = (entries) => {
|
|
1994
2262
|
for (const entry of entries) {
|
|
1995
2263
|
if (typeof entry !== "object" || entry === null || !("key" in entry) || !("value" in entry)) {
|
|
1996
|
-
throw new BTreeConcurrencyError(
|
|
2264
|
+
throw new BTreeConcurrencyError(
|
|
2265
|
+
"Malformed putMany mutation: each entry must have key and value."
|
|
2266
|
+
);
|
|
1997
2267
|
}
|
|
1998
2268
|
}
|
|
1999
2269
|
};
|
|
2000
2270
|
var validateInitMutation = (m, expectedConfigFingerprint) => {
|
|
2001
2271
|
if (typeof m.configFingerprint !== "string") {
|
|
2002
|
-
throw new BTreeConcurrencyError(
|
|
2272
|
+
throw new BTreeConcurrencyError(
|
|
2273
|
+
"Malformed init mutation: missing configFingerprint."
|
|
2274
|
+
);
|
|
2003
2275
|
}
|
|
2004
2276
|
if (expectedConfigFingerprint !== void 0 && m.configFingerprint !== expectedConfigFingerprint) {
|
|
2005
2277
|
throw new BTreeConcurrencyError(
|
|
@@ -2007,45 +2279,70 @@ var validateInitMutation = (m, expectedConfigFingerprint) => {
|
|
|
2007
2279
|
);
|
|
2008
2280
|
}
|
|
2009
2281
|
};
|
|
2010
|
-
var
|
|
2282
|
+
var validateMutationFields = (m) => {
|
|
2011
2283
|
switch (m.type) {
|
|
2012
|
-
case "init":
|
|
2013
|
-
validateInitMutation(m, expectedConfigFingerprint);
|
|
2014
|
-
break;
|
|
2015
2284
|
case "put":
|
|
2016
2285
|
if (!("key" in m) || !("value" in m)) {
|
|
2017
|
-
throw new BTreeConcurrencyError(
|
|
2286
|
+
throw new BTreeConcurrencyError(
|
|
2287
|
+
"Malformed put mutation: missing key or value."
|
|
2288
|
+
);
|
|
2018
2289
|
}
|
|
2019
2290
|
break;
|
|
2020
2291
|
case "remove":
|
|
2021
2292
|
if (!("key" in m)) {
|
|
2022
|
-
throw new BTreeConcurrencyError(
|
|
2293
|
+
throw new BTreeConcurrencyError(
|
|
2294
|
+
"Malformed remove mutation: missing key."
|
|
2295
|
+
);
|
|
2023
2296
|
}
|
|
2024
2297
|
break;
|
|
2025
2298
|
case "removeById":
|
|
2026
2299
|
if (!("entryId" in m)) {
|
|
2027
|
-
throw new BTreeConcurrencyError(
|
|
2300
|
+
throw new BTreeConcurrencyError(
|
|
2301
|
+
"Malformed removeById mutation: missing entryId."
|
|
2302
|
+
);
|
|
2028
2303
|
}
|
|
2029
2304
|
break;
|
|
2030
2305
|
case "updateById":
|
|
2031
2306
|
if (!("entryId" in m) || !("value" in m)) {
|
|
2032
|
-
throw new BTreeConcurrencyError(
|
|
2307
|
+
throw new BTreeConcurrencyError(
|
|
2308
|
+
"Malformed updateById mutation: missing entryId or value."
|
|
2309
|
+
);
|
|
2033
2310
|
}
|
|
2034
2311
|
break;
|
|
2035
|
-
case "popFirst":
|
|
2036
|
-
case "popLast":
|
|
2037
|
-
break;
|
|
2038
2312
|
case "putMany":
|
|
2039
2313
|
if (!("entries" in m) || !Array.isArray(m.entries)) {
|
|
2040
|
-
throw new BTreeConcurrencyError(
|
|
2314
|
+
throw new BTreeConcurrencyError(
|
|
2315
|
+
"Malformed putMany mutation: missing entries array."
|
|
2316
|
+
);
|
|
2041
2317
|
}
|
|
2042
2318
|
validatePutManyEntries(m.entries);
|
|
2043
2319
|
break;
|
|
2044
2320
|
case "deleteRange":
|
|
2045
2321
|
if (!("startKey" in m) || !("endKey" in m)) {
|
|
2046
|
-
throw new BTreeConcurrencyError(
|
|
2322
|
+
throw new BTreeConcurrencyError(
|
|
2323
|
+
"Malformed deleteRange mutation: missing startKey or endKey."
|
|
2324
|
+
);
|
|
2047
2325
|
}
|
|
2048
2326
|
break;
|
|
2327
|
+
default:
|
|
2328
|
+
break;
|
|
2329
|
+
}
|
|
2330
|
+
};
|
|
2331
|
+
var validateSingleMutation = (m, expectedConfigFingerprint) => {
|
|
2332
|
+
switch (m.type) {
|
|
2333
|
+
case "init":
|
|
2334
|
+
validateInitMutation(m, expectedConfigFingerprint);
|
|
2335
|
+
break;
|
|
2336
|
+
case "put":
|
|
2337
|
+
case "remove":
|
|
2338
|
+
case "removeById":
|
|
2339
|
+
case "updateById":
|
|
2340
|
+
case "putMany":
|
|
2341
|
+
case "deleteRange":
|
|
2342
|
+
validateMutationFields(m);
|
|
2343
|
+
break;
|
|
2344
|
+
case "popFirst":
|
|
2345
|
+
case "popLast":
|
|
2049
2346
|
case "clear":
|
|
2050
2347
|
break;
|
|
2051
2348
|
default:
|
|
@@ -2057,7 +2354,9 @@ var validateSingleMutation = (m, expectedConfigFingerprint) => {
|
|
|
2057
2354
|
var validateMutationBatch = (mutations, expectedConfigFingerprint) => {
|
|
2058
2355
|
for (const mutation of mutations) {
|
|
2059
2356
|
if (typeof mutation !== "object" || mutation === null) {
|
|
2060
|
-
throw new BTreeConcurrencyError(
|
|
2357
|
+
throw new BTreeConcurrencyError(
|
|
2358
|
+
"Malformed mutation: expected an object."
|
|
2359
|
+
);
|
|
2061
2360
|
}
|
|
2062
2361
|
validateSingleMutation(
|
|
2063
2362
|
mutation,
|
|
@@ -2092,9 +2391,7 @@ var normalizeReadMode = (value) => {
|
|
|
2092
2391
|
return "strong";
|
|
2093
2392
|
}
|
|
2094
2393
|
if (value !== "strong" && value !== "local") {
|
|
2095
|
-
throw new BTreeConcurrencyError(
|
|
2096
|
-
`readMode: must be 'strong' or 'local'.`
|
|
2097
|
-
);
|
|
2394
|
+
throw new BTreeConcurrencyError(`readMode: must be 'strong' or 'local'.`);
|
|
2098
2395
|
}
|
|
2099
2396
|
return value;
|
|
2100
2397
|
};
|
|
@@ -2144,7 +2441,11 @@ var applyMutationLocal = (tree, mutation, onInit) => {
|
|
|
2144
2441
|
case "popLast":
|
|
2145
2442
|
return tree.popLast();
|
|
2146
2443
|
case "deleteRange":
|
|
2147
|
-
return tree.deleteRange(
|
|
2444
|
+
return tree.deleteRange(
|
|
2445
|
+
mutation.startKey,
|
|
2446
|
+
mutation.endKey,
|
|
2447
|
+
mutation.options
|
|
2448
|
+
);
|
|
2148
2449
|
case "clear":
|
|
2149
2450
|
tree.clear();
|
|
2150
2451
|
return null;
|
|
@@ -2191,7 +2492,9 @@ var createPutManyEvaluator = (entries, duplicateKeys, compareKeys) => {
|
|
|
2191
2492
|
for (let i = 1; i < entries.length; i += 1) {
|
|
2192
2493
|
const cmp = compareKeys(entries[i - 1].key, entries[i].key);
|
|
2193
2494
|
if (cmp > 0) {
|
|
2194
|
-
throw new BTreeValidationError(
|
|
2495
|
+
throw new BTreeValidationError(
|
|
2496
|
+
"putMany: entries not in ascending order."
|
|
2497
|
+
);
|
|
2195
2498
|
}
|
|
2196
2499
|
if (strictlyAscending && cmp === 0) {
|
|
2197
2500
|
throw new BTreeValidationError(
|
|
@@ -2222,59 +2525,48 @@ var createClearEvaluator = () => {
|
|
|
2222
2525
|
return () => ({ type: "clear" });
|
|
2223
2526
|
};
|
|
2224
2527
|
|
|
2225
|
-
// src/concurrency/
|
|
2226
|
-
var
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2528
|
+
// src/concurrency/syncLogValidation.ts
|
|
2529
|
+
var validateSyncLog = (log, maxSyncMutationsPerBatch) => {
|
|
2530
|
+
if (typeof log.version !== "bigint") {
|
|
2531
|
+
throw new BTreeConcurrencyError("Store contract: version must be bigint.");
|
|
2532
|
+
}
|
|
2533
|
+
if (!Array.isArray(log.mutations)) {
|
|
2534
|
+
throw new BTreeConcurrencyError(
|
|
2535
|
+
"Store contract: mutations must be an array."
|
|
2233
2536
|
);
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2537
|
+
}
|
|
2538
|
+
if (log.mutations.length > maxSyncMutationsPerBatch) {
|
|
2539
|
+
throw new BTreeConcurrencyError(
|
|
2540
|
+
`Sync batch exceeded limit (${String(maxSyncMutationsPerBatch)}).`
|
|
2541
|
+
);
|
|
2542
|
+
}
|
|
2543
|
+
};
|
|
2544
|
+
|
|
2545
|
+
// src/concurrency/coordinator.ts
|
|
2546
|
+
var Coordinator = class {
|
|
2547
|
+
constructor(tree, store, maxRetries, maxSyncMutationsPerBatch, configFingerprint, readMode) {
|
|
2548
|
+
this.tree = tree;
|
|
2549
|
+
this.store = store;
|
|
2550
|
+
this.maxRetries = maxRetries;
|
|
2551
|
+
this.maxSyncMutationsPerBatch = maxSyncMutationsPerBatch;
|
|
2552
|
+
this.configFingerprint = configFingerprint;
|
|
2553
|
+
this.readMode = readMode;
|
|
2245
2554
|
this.currentVersion = 0n;
|
|
2246
2555
|
this.operationQueue = Promise.resolve();
|
|
2247
2556
|
this.initSeen = false;
|
|
2248
2557
|
this.corrupted = false;
|
|
2249
2558
|
}
|
|
2250
|
-
async sync() {
|
|
2251
|
-
await this.runExclusive(async () => {
|
|
2252
|
-
await this.syncUnlocked();
|
|
2253
|
-
});
|
|
2254
|
-
}
|
|
2255
2559
|
async syncUnlocked() {
|
|
2256
2560
|
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
|
-
}
|
|
2561
|
+
validateSyncLog(log, this.maxSyncMutationsPerBatch);
|
|
2562
|
+
if (log.version <= this.currentVersion) return;
|
|
2271
2563
|
validateMutationBatch(log.mutations, this.configFingerprint);
|
|
2272
2564
|
try {
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2565
|
+
const markInit = () => {
|
|
2566
|
+
this.initSeen = true;
|
|
2567
|
+
};
|
|
2568
|
+
for (const mutation of log.mutations)
|
|
2569
|
+
applyMutationLocal(this.tree, mutation, markInit);
|
|
2278
2570
|
this.currentVersion = log.version;
|
|
2279
2571
|
} catch (error) {
|
|
2280
2572
|
this.corrupted = true;
|
|
@@ -2286,11 +2578,10 @@ var ConcurrentInMemoryBTree = class {
|
|
|
2286
2578
|
}
|
|
2287
2579
|
runExclusive(operation) {
|
|
2288
2580
|
const run = async () => {
|
|
2289
|
-
if (this.corrupted)
|
|
2581
|
+
if (this.corrupted)
|
|
2290
2582
|
throw new BTreeConcurrencyError(
|
|
2291
2583
|
"Instance is permanently corrupted. Discard and create a new instance."
|
|
2292
2584
|
);
|
|
2293
|
-
}
|
|
2294
2585
|
return operation();
|
|
2295
2586
|
};
|
|
2296
2587
|
const result = this.operationQueue.then(run, run);
|
|
@@ -2302,40 +2593,38 @@ var ConcurrentInMemoryBTree = class {
|
|
|
2302
2593
|
}
|
|
2303
2594
|
readOp(fn) {
|
|
2304
2595
|
return this.runExclusive(async () => {
|
|
2305
|
-
if (this.readMode === "strong")
|
|
2306
|
-
await this.syncUnlocked();
|
|
2307
|
-
}
|
|
2596
|
+
if (this.readMode === "strong") await this.syncUnlocked();
|
|
2308
2597
|
return fn(this.tree);
|
|
2309
2598
|
});
|
|
2310
2599
|
}
|
|
2311
|
-
async
|
|
2600
|
+
async appendAndApply(evaluate) {
|
|
2312
2601
|
for (let attempt = 0; attempt < this.maxRetries; attempt += 1) {
|
|
2313
2602
|
await this.syncUnlocked();
|
|
2314
2603
|
const mutation = evaluate(this.tree);
|
|
2315
|
-
if (mutation === null)
|
|
2316
|
-
return null;
|
|
2317
|
-
}
|
|
2604
|
+
if (mutation === null) return null;
|
|
2318
2605
|
const expectedVersion = this.currentVersion;
|
|
2319
|
-
const mutations = this.initSeen ? [mutation] : [
|
|
2606
|
+
const mutations = this.initSeen ? [mutation] : [
|
|
2607
|
+
{ type: "init", configFingerprint: this.configFingerprint },
|
|
2608
|
+
mutation
|
|
2609
|
+
];
|
|
2320
2610
|
const appendResult = await this.store.append(expectedVersion, mutations);
|
|
2321
2611
|
assertAppendVersionContract(expectedVersion, appendResult);
|
|
2322
2612
|
if (appendResult.applied) {
|
|
2323
2613
|
try {
|
|
2614
|
+
const markInit = () => {
|
|
2615
|
+
this.initSeen = true;
|
|
2616
|
+
};
|
|
2324
2617
|
for (const m of mutations) {
|
|
2325
2618
|
if (m === mutation) break;
|
|
2326
|
-
applyMutationLocal(this.tree, m,
|
|
2327
|
-
this.initSeen = true;
|
|
2328
|
-
});
|
|
2619
|
+
applyMutationLocal(this.tree, m, markInit);
|
|
2329
2620
|
}
|
|
2330
|
-
const
|
|
2621
|
+
const result = applyMutationLocal(
|
|
2331
2622
|
this.tree,
|
|
2332
2623
|
mutation,
|
|
2333
|
-
|
|
2334
|
-
this.initSeen = true;
|
|
2335
|
-
}
|
|
2624
|
+
markInit
|
|
2336
2625
|
);
|
|
2337
2626
|
this.currentVersion = appendResult.version;
|
|
2338
|
-
return
|
|
2627
|
+
return result;
|
|
2339
2628
|
} catch (error) {
|
|
2340
2629
|
this.corrupted = true;
|
|
2341
2630
|
const cause = error instanceof Error ? error.message : String(error);
|
|
@@ -2349,137 +2638,159 @@ var ConcurrentInMemoryBTree = class {
|
|
|
2349
2638
|
`Mutation failed after ${String(this.maxRetries)} retries.`
|
|
2350
2639
|
);
|
|
2351
2640
|
}
|
|
2352
|
-
|
|
2353
|
-
return this.runExclusive(async () =>
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2641
|
+
writeOp(evaluator) {
|
|
2642
|
+
return this.runExclusive(async () => this.appendAndApply(evaluator));
|
|
2643
|
+
}
|
|
2644
|
+
};
|
|
2645
|
+
|
|
2646
|
+
// src/concurrency/ConcurrentInMemoryBTree.ts
|
|
2647
|
+
var ConcurrentInMemoryBTree = class {
|
|
2648
|
+
constructor(config) {
|
|
2649
|
+
this.compareKeys = config.compareKeys;
|
|
2650
|
+
this.duplicateKeys = config.duplicateKeys ?? "replace";
|
|
2651
|
+
const tree = new InMemoryBTree({
|
|
2652
|
+
compareKeys: config.compareKeys,
|
|
2653
|
+
maxLeafEntries: config.maxLeafEntries,
|
|
2654
|
+
maxBranchChildren: config.maxBranchChildren,
|
|
2655
|
+
duplicateKeys: config.duplicateKeys,
|
|
2656
|
+
enableEntryIdLookup: config.enableEntryIdLookup,
|
|
2657
|
+
autoScale: config.autoScale,
|
|
2658
|
+
deleteRebalancePolicy: config.deleteRebalancePolicy
|
|
2357
2659
|
});
|
|
2660
|
+
this.coord = new Coordinator(
|
|
2661
|
+
tree,
|
|
2662
|
+
config.store,
|
|
2663
|
+
normalizeMaxRetries(config.maxRetries),
|
|
2664
|
+
normalizeMaxSyncMutationsPerBatch(config.maxSyncMutationsPerBatch),
|
|
2665
|
+
computeConfigFingerprint(config),
|
|
2666
|
+
normalizeReadMode(config.readMode)
|
|
2667
|
+
);
|
|
2358
2668
|
}
|
|
2359
|
-
async
|
|
2360
|
-
|
|
2361
|
-
|
|
2669
|
+
async sync() {
|
|
2670
|
+
await this.coord.runExclusive(async () => {
|
|
2671
|
+
await this.coord.syncUnlocked();
|
|
2362
2672
|
});
|
|
2363
2673
|
}
|
|
2364
|
-
async
|
|
2365
|
-
return this.runExclusive(async () => {
|
|
2366
|
-
|
|
2674
|
+
async syncThenRead(fn) {
|
|
2675
|
+
return this.coord.runExclusive(async () => {
|
|
2676
|
+
await this.coord.syncUnlocked();
|
|
2677
|
+
return fn(this.coord.tree);
|
|
2367
2678
|
});
|
|
2368
2679
|
}
|
|
2680
|
+
async put(key, value) {
|
|
2681
|
+
return this.coord.writeOp(
|
|
2682
|
+
createPutEvaluator(this.duplicateKeys, key, value)
|
|
2683
|
+
);
|
|
2684
|
+
}
|
|
2685
|
+
async remove(key) {
|
|
2686
|
+
return this.coord.writeOp(createRemoveEvaluator(key));
|
|
2687
|
+
}
|
|
2688
|
+
async removeById(entryId) {
|
|
2689
|
+
return this.coord.writeOp(createRemoveByIdEvaluator(entryId));
|
|
2690
|
+
}
|
|
2369
2691
|
async updateById(entryId, value) {
|
|
2370
|
-
return this.
|
|
2371
|
-
return this.appendMutationAndApplyUnlocked(createUpdateByIdEvaluator(entryId, value));
|
|
2372
|
-
});
|
|
2692
|
+
return this.coord.writeOp(createUpdateByIdEvaluator(entryId, value));
|
|
2373
2693
|
}
|
|
2374
2694
|
async popFirst() {
|
|
2375
|
-
return this.
|
|
2376
|
-
return this.appendMutationAndApplyUnlocked(createPopFirstEvaluator());
|
|
2377
|
-
});
|
|
2695
|
+
return this.coord.writeOp(createPopFirstEvaluator());
|
|
2378
2696
|
}
|
|
2379
2697
|
async popLast() {
|
|
2380
|
-
return this.
|
|
2381
|
-
return this.appendMutationAndApplyUnlocked(createPopLastEvaluator());
|
|
2382
|
-
});
|
|
2698
|
+
return this.coord.writeOp(createPopLastEvaluator());
|
|
2383
2699
|
}
|
|
2384
2700
|
async putMany(entries) {
|
|
2385
|
-
if (entries.length === 0)
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
return this.appendMutationAndApplyUnlocked(
|
|
2390
|
-
createPutManyEvaluator(entries, this.duplicateKeys, this.compareKeys)
|
|
2391
|
-
);
|
|
2392
|
-
});
|
|
2701
|
+
if (entries.length === 0) return [];
|
|
2702
|
+
return this.coord.writeOp(
|
|
2703
|
+
createPutManyEvaluator(entries, this.duplicateKeys, this.compareKeys)
|
|
2704
|
+
);
|
|
2393
2705
|
}
|
|
2394
2706
|
async deleteRange(startKey, endKey, options) {
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
return result ?? 0;
|
|
2400
|
-
});
|
|
2707
|
+
const result = await this.coord.writeOp(
|
|
2708
|
+
createDeleteRangeEvaluator(startKey, endKey, options)
|
|
2709
|
+
);
|
|
2710
|
+
return result ?? 0;
|
|
2401
2711
|
}
|
|
2402
2712
|
async clear() {
|
|
2403
|
-
await this.
|
|
2404
|
-
await this.appendMutationAndApplyUnlocked(createClearEvaluator());
|
|
2405
|
-
});
|
|
2713
|
+
await this.coord.writeOp(createClearEvaluator());
|
|
2406
2714
|
}
|
|
2407
2715
|
async get(key) {
|
|
2408
|
-
return this.readOp((
|
|
2716
|
+
return this.coord.readOp((t) => t.get(key));
|
|
2409
2717
|
}
|
|
2410
2718
|
async hasKey(key) {
|
|
2411
|
-
return this.readOp((
|
|
2719
|
+
return this.coord.readOp((t) => t.hasKey(key));
|
|
2412
2720
|
}
|
|
2413
2721
|
async findFirst(key) {
|
|
2414
|
-
return this.readOp((
|
|
2722
|
+
return this.coord.readOp((t) => t.findFirst(key));
|
|
2415
2723
|
}
|
|
2416
2724
|
async findLast(key) {
|
|
2417
|
-
return this.readOp((
|
|
2725
|
+
return this.coord.readOp((t) => t.findLast(key));
|
|
2418
2726
|
}
|
|
2419
2727
|
async range(startKey, endKey, options) {
|
|
2420
|
-
return this.readOp((
|
|
2728
|
+
return this.coord.readOp((t) => t.range(startKey, endKey, options));
|
|
2421
2729
|
}
|
|
2422
2730
|
async snapshot() {
|
|
2423
|
-
return this.readOp((
|
|
2731
|
+
return this.coord.readOp((t) => t.snapshot());
|
|
2424
2732
|
}
|
|
2425
2733
|
async size() {
|
|
2426
|
-
return this.readOp((
|
|
2734
|
+
return this.coord.readOp((t) => t.size());
|
|
2427
2735
|
}
|
|
2428
2736
|
async assertInvariants() {
|
|
2429
|
-
await this.readOp((
|
|
2737
|
+
await this.coord.readOp((t) => t.assertInvariants());
|
|
2430
2738
|
}
|
|
2431
2739
|
async getStats() {
|
|
2432
|
-
return this.readOp((
|
|
2740
|
+
return this.coord.readOp((t) => t.getStats());
|
|
2433
2741
|
}
|
|
2434
2742
|
async peekFirst() {
|
|
2435
|
-
return this.readOp((
|
|
2743
|
+
return this.coord.readOp((t) => t.peekFirst());
|
|
2436
2744
|
}
|
|
2437
2745
|
async peekLast() {
|
|
2438
|
-
return this.readOp((
|
|
2746
|
+
return this.coord.readOp((t) => t.peekLast());
|
|
2439
2747
|
}
|
|
2440
2748
|
async peekById(entryId) {
|
|
2441
|
-
return this.readOp((
|
|
2749
|
+
return this.coord.readOp((t) => t.peekById(entryId));
|
|
2442
2750
|
}
|
|
2443
2751
|
async count(startKey, endKey, options) {
|
|
2444
|
-
return this.readOp((
|
|
2752
|
+
return this.coord.readOp((t) => t.count(startKey, endKey, options));
|
|
2445
2753
|
}
|
|
2446
2754
|
async nextHigherKey(key) {
|
|
2447
|
-
return this.readOp((
|
|
2755
|
+
return this.coord.readOp((t) => t.nextHigherKey(key));
|
|
2448
2756
|
}
|
|
2449
2757
|
async nextLowerKey(key) {
|
|
2450
|
-
return this.readOp((
|
|
2758
|
+
return this.coord.readOp((t) => t.nextLowerKey(key));
|
|
2451
2759
|
}
|
|
2452
2760
|
async getPairOrNextLower(key) {
|
|
2453
|
-
return this.readOp((
|
|
2761
|
+
return this.coord.readOp((t) => t.getPairOrNextLower(key));
|
|
2454
2762
|
}
|
|
2455
2763
|
async entries() {
|
|
2456
|
-
return this.readOp((
|
|
2764
|
+
return this.coord.readOp((t) => Array.from(t.entries()));
|
|
2457
2765
|
}
|
|
2458
2766
|
async entriesReversed() {
|
|
2459
|
-
return this.readOp((
|
|
2767
|
+
return this.coord.readOp((t) => Array.from(t.entriesReversed()));
|
|
2460
2768
|
}
|
|
2461
2769
|
async keys() {
|
|
2462
|
-
return this.readOp((
|
|
2770
|
+
return this.coord.readOp((t) => Array.from(t.keys()));
|
|
2463
2771
|
}
|
|
2464
2772
|
async values() {
|
|
2465
|
-
return this.readOp((
|
|
2773
|
+
return this.coord.readOp((t) => Array.from(t.values()));
|
|
2466
2774
|
}
|
|
2467
2775
|
async forEach(callback) {
|
|
2468
|
-
await this.readOp((
|
|
2469
|
-
|
|
2776
|
+
await this.coord.readOp((t) => {
|
|
2777
|
+
t.forEach(callback);
|
|
2778
|
+
});
|
|
2779
|
+
}
|
|
2780
|
+
async forEachRange(startKey, endKey, callback, options) {
|
|
2781
|
+
await this.coord.readOp((t) => {
|
|
2782
|
+
t.forEachRange(startKey, endKey, callback, options);
|
|
2470
2783
|
});
|
|
2471
2784
|
}
|
|
2472
2785
|
async *[Symbol.asyncIterator]() {
|
|
2473
2786
|
const all = await this.entries();
|
|
2474
|
-
for (const entry of all)
|
|
2475
|
-
yield entry;
|
|
2476
|
-
}
|
|
2787
|
+
for (const entry of all) yield entry;
|
|
2477
2788
|
}
|
|
2478
2789
|
async clone() {
|
|
2479
|
-
return this.readOp((
|
|
2790
|
+
return this.coord.readOp((t) => t.clone());
|
|
2480
2791
|
}
|
|
2481
2792
|
async toJSON() {
|
|
2482
|
-
return this.readOp((
|
|
2793
|
+
return this.coord.readOp((t) => t.toJSON());
|
|
2483
2794
|
}
|
|
2484
2795
|
static fromJSON(json, compareKeys) {
|
|
2485
2796
|
return InMemoryBTree.fromJSON(json, compareKeys);
|