@frostpillar/frostpillar-btree 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -21,54 +21,10 @@ var BTreeConcurrencyError = class extends Error {
21
21
  }
22
22
  };
23
23
 
24
- // src/btree/types.ts
25
- var DEFAULT_MAX_LEAF_ENTRIES = 64;
26
- var DEFAULT_MAX_BRANCH_CHILDREN = 64;
27
- var MIN_NODE_CAPACITY = 3;
28
- var MAX_NODE_CAPACITY = 16384;
29
- var NODE_LEAF = 0;
30
- var NODE_BRANCH = 1;
31
- var normalizeDuplicateKeyPolicy = (value) => {
32
- if (value === void 0) {
33
- return "replace";
34
- }
35
- if (value !== "allow" && value !== "reject" && value !== "replace") {
36
- throw new BTreeValidationError(
37
- `Invalid duplicateKeys option.`
38
- );
39
- }
40
- return value;
41
- };
42
- var isLeafNode = (node) => {
43
- return node.kind === NODE_LEAF;
44
- };
45
- var writeMinKeyTo = (node, target) => {
46
- if (node.kind === NODE_LEAF) {
47
- if (node.entryOffset >= node.entries.length) return false;
48
- const e = node.entries[node.entryOffset];
49
- target.key = e.key;
50
- target.sequence = e.entryId;
51
- return true;
52
- }
53
- if (node.childOffset >= node.keys.length) return false;
54
- target.key = node.keys[node.childOffset].key;
55
- target.sequence = node.keys[node.childOffset].sequence;
56
- return true;
57
- };
58
- var normalizeNodeCapacity = (value, field, defaultValue) => {
59
- if (value === void 0) {
60
- return defaultValue;
61
- }
62
- if (!Number.isInteger(value) || value < MIN_NODE_CAPACITY || value > MAX_NODE_CAPACITY) {
63
- throw new BTreeValidationError(
64
- `${field}: integer ${MIN_NODE_CAPACITY}\u2013${MAX_NODE_CAPACITY} required.`
65
- );
66
- }
67
- return value;
68
- };
24
+ // src/btree/node-ops.ts
69
25
  var createLeafNode = (entries, parent) => {
70
26
  return {
71
- kind: NODE_LEAF,
27
+ kind: 0,
72
28
  entries,
73
29
  entryOffset: 0,
74
30
  parent,
@@ -80,7 +36,7 @@ var createLeafNode = (entries, parent) => {
80
36
  var createBranchNode = (children, parent) => {
81
37
  const keys = [];
82
38
  const branch = {
83
- kind: NODE_BRANCH,
39
+ kind: 1,
84
40
  children,
85
41
  keys,
86
42
  childOffset: 0,
@@ -91,11 +47,12 @@ var createBranchNode = (children, parent) => {
91
47
  const child = children[i];
92
48
  child.parent = branch;
93
49
  child.indexInParent = i;
94
- const target = { key: void 0, sequence: 0 };
50
+ const target = {
51
+ key: void 0,
52
+ sequence: 0
53
+ };
95
54
  if (!writeMinKeyTo(child, target)) {
96
- throw new BTreeInvariantError(
97
- "branch child has no min key"
98
- );
55
+ throw new BTreeInvariantError("branch child has no min key");
99
56
  }
100
57
  keys.push(target);
101
58
  }
@@ -149,7 +106,14 @@ var leafInsertAt = (leaf, logicalIndex, entry) => {
149
106
  leaf.entryOffset -= 1;
150
107
  leaf.entries[phys - 1] = entry;
151
108
  } else {
152
- leaf.entries.splice(phys, 0, entry);
109
+ const len = leaf.entries.length;
110
+ if (phys >= len) {
111
+ leaf.entries.push(entry);
112
+ } else {
113
+ leaf.entries.push(leaf.entries[len - 1]);
114
+ leaf.entries.copyWithin(phys + 1, phys, len);
115
+ leaf.entries[phys] = entry;
116
+ }
153
117
  }
154
118
  };
155
119
  var leafCompact = (leaf) => {
@@ -178,7 +142,11 @@ var branchInsertAt = (branch, logicalIndex, child, key) => {
178
142
  const phys = branch.childOffset + logicalIndex;
179
143
  const count = branch.children.length - branch.childOffset;
180
144
  if (branch.childOffset > 0 && logicalIndex < count >>> 1) {
181
- branch.children.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
145
+ branch.children.copyWithin(
146
+ branch.childOffset - 1,
147
+ branch.childOffset,
148
+ phys
149
+ );
182
150
  branch.keys.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
183
151
  branch.childOffset -= 1;
184
152
  branch.children[phys - 1] = child;
@@ -199,8 +167,16 @@ var branchRemoveAt = (branch, physIndex) => {
199
167
  const logicalIndex = physIndex - branch.childOffset;
200
168
  const count = branch.children.length - branch.childOffset;
201
169
  if (logicalIndex < count - 1 - logicalIndex) {
202
- branch.children.copyWithin(branch.childOffset + 1, branch.childOffset, physIndex);
203
- branch.keys.copyWithin(branch.childOffset + 1, branch.childOffset, physIndex);
170
+ branch.children.copyWithin(
171
+ branch.childOffset + 1,
172
+ branch.childOffset,
173
+ physIndex
174
+ );
175
+ branch.keys.copyWithin(
176
+ branch.childOffset + 1,
177
+ branch.childOffset,
178
+ physIndex
179
+ );
204
180
  branch.childOffset += 1;
205
181
  for (let i = branch.childOffset; i <= physIndex; i += 1) {
206
182
  branch.children[i].indexInParent = i;
@@ -219,6 +195,60 @@ var branchRemoveAt = (branch, physIndex) => {
219
195
  }
220
196
  };
221
197
 
198
+ // src/btree/types.ts
199
+ var DEFAULT_MAX_LEAF_ENTRIES = 64;
200
+ var DEFAULT_MAX_BRANCH_CHILDREN = 64;
201
+ var MIN_NODE_CAPACITY = 3;
202
+ var MAX_NODE_CAPACITY = 16384;
203
+ var NODE_LEAF = 0;
204
+ var normalizeDuplicateKeyPolicy = (value) => {
205
+ if (value === void 0) {
206
+ return "replace";
207
+ }
208
+ if (value !== "allow" && value !== "reject" && value !== "replace") {
209
+ throw new BTreeValidationError(`Invalid duplicateKeys option.`);
210
+ }
211
+ return value;
212
+ };
213
+ var normalizeDeleteRebalancePolicy = (value) => {
214
+ if (value === void 0) {
215
+ return "standard";
216
+ }
217
+ if (value !== "standard" && value !== "lazy") {
218
+ throw new BTreeValidationError(`Invalid deleteRebalancePolicy option.`);
219
+ }
220
+ return value;
221
+ };
222
+ var freezeEntry = (entry) => Object.freeze(entry);
223
+ var createEntry = (key, entryId, value) => Object.freeze({ key, entryId, value });
224
+ var isLeafNode = (node) => {
225
+ return node.kind === NODE_LEAF;
226
+ };
227
+ var writeMinKeyTo = (node, target) => {
228
+ if (node.kind === NODE_LEAF) {
229
+ if (node.entryOffset >= node.entries.length) return false;
230
+ const e = node.entries[node.entryOffset];
231
+ target.key = e.key;
232
+ target.sequence = e.entryId;
233
+ return true;
234
+ }
235
+ if (node.childOffset >= node.keys.length) return false;
236
+ target.key = node.keys[node.childOffset].key;
237
+ target.sequence = node.keys[node.childOffset].sequence;
238
+ return true;
239
+ };
240
+ var normalizeNodeCapacity = (value, field, defaultValue) => {
241
+ if (value === void 0) {
242
+ return defaultValue;
243
+ }
244
+ if (!Number.isInteger(value) || value < MIN_NODE_CAPACITY || value > MAX_NODE_CAPACITY) {
245
+ throw new BTreeValidationError(
246
+ `${field}: integer ${MIN_NODE_CAPACITY}\u2013${MAX_NODE_CAPACITY} required.`
247
+ );
248
+ }
249
+ return value;
250
+ };
251
+
222
252
  // src/btree/navigation.ts
223
253
  var selectBranchChild = (compare, branch, userKey, sequence) => {
224
254
  const off = branch.childOffset;
@@ -232,7 +262,7 @@ var selectBranchChild = (compare, branch, userKey, sequence) => {
232
262
  const mid = lower + upper >>> 1;
233
263
  const k = branch.keys[mid];
234
264
  const cmp = compare(k.key, userKey);
235
- if ((cmp !== 0 ? cmp : k.sequence - sequence) <= 0) {
265
+ if ((cmp !== 0 ? cmp : k.sequence < sequence ? -1 : k.sequence > sequence ? 1 : 0) <= 0) {
236
266
  selectedIndex = mid;
237
267
  lower = mid + 1;
238
268
  } else {
@@ -257,7 +287,7 @@ var lowerBoundInLeaf = (state, leaf, userKey, sequence) => {
257
287
  const mid = lower + upper >>> 1;
258
288
  const e = leaf.entries[mid];
259
289
  const cmp = compare(e.key, userKey);
260
- if ((cmp !== 0 ? cmp : e.entryId - sequence) < 0) {
290
+ if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) < 0) {
261
291
  lower = mid + 1;
262
292
  } else {
263
293
  upper = mid;
@@ -273,7 +303,7 @@ var upperBoundInLeaf = (state, leaf, userKey, sequence) => {
273
303
  const mid = lower + upper >>> 1;
274
304
  const e = leaf.entries[mid];
275
305
  const cmp = compare(e.key, userKey);
276
- if ((cmp !== 0 ? cmp : e.entryId - sequence) <= 0) {
306
+ if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) <= 0) {
277
307
  lower = mid + 1;
278
308
  } else {
279
309
  upper = mid;
@@ -340,7 +370,11 @@ var hasKeyEntry = (state, key) => {
340
370
  var findNextHigherKey = (state, key) => {
341
371
  if (state.entryCount === 0) return null;
342
372
  const compare = state.compareKeys;
343
- let leaf = findLeafForKey(state, key, Number.MAX_SAFE_INTEGER);
373
+ let leaf = findLeafForKey(
374
+ state,
375
+ key,
376
+ Number.MAX_SAFE_INTEGER
377
+ );
344
378
  let idx = upperBoundInLeaf(state, leaf, key, Number.MAX_SAFE_INTEGER);
345
379
  while (leaf !== null) {
346
380
  if (idx < leafEntryCount(leaf)) {
@@ -410,7 +444,175 @@ var findPairOrNextLower = (state, key) => {
410
444
  return null;
411
445
  };
412
446
 
413
- // src/btree/rebalance.ts
447
+ // src/btree/rangeQuery.ts
448
+ function isEmptyRange(compare, startKey, endKey, options) {
449
+ const cmp = compare(startKey, endKey);
450
+ if (cmp > 0) return true;
451
+ const lowerExclusive = options?.lowerBound === "exclusive";
452
+ const upperExclusive = options?.upperBound === "exclusive";
453
+ return lowerExclusive && upperExclusive && cmp === 0;
454
+ }
455
+ var initRangeCursor = (state, startKey, endKey, options) => {
456
+ if (state.entryCount === 0) return null;
457
+ const compare = state.compareKeys;
458
+ if (isEmptyRange(compare, startKey, endKey, options)) return null;
459
+ const lowerExclusive = options?.lowerBound === "exclusive";
460
+ const upperExclusive = options?.upperBound === "exclusive";
461
+ const startSeq = lowerExclusive ? Number.MAX_SAFE_INTEGER : 0;
462
+ const leaf = findLeafForKey(state, startKey, startSeq);
463
+ const index = lowerExclusive ? upperBoundInLeaf(state, leaf, startKey, Number.MAX_SAFE_INTEGER) : lowerBoundInLeaf(state, leaf, startKey, 0);
464
+ return { leaf, index, compare, upperExclusive };
465
+ };
466
+ var countRangeEntries = (state, startKey, endKey, options) => {
467
+ const cursor = initRangeCursor(state, startKey, endKey, options);
468
+ if (cursor === null) return 0;
469
+ let cursorLeaf = cursor.leaf;
470
+ let cursorIndex = cursor.index;
471
+ const { compare, upperExclusive } = cursor;
472
+ let count = 0;
473
+ while (cursorLeaf !== null) {
474
+ const leafCount = leafEntryCount(cursorLeaf);
475
+ if (cursorIndex >= leafCount) {
476
+ cursorLeaf = cursorLeaf.next;
477
+ cursorIndex = 0;
478
+ continue;
479
+ }
480
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
481
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
482
+ count += leafCount - cursorIndex;
483
+ cursorLeaf = cursorLeaf.next;
484
+ cursorIndex = 0;
485
+ continue;
486
+ }
487
+ count += findBoundaryEnd(state, cursorLeaf, endKey, upperExclusive, leafCount) - cursorIndex;
488
+ return count;
489
+ }
490
+ return count;
491
+ };
492
+ var isLastEntryInRange = (lastKey, endKey, compare, upperExclusive) => {
493
+ const cmp = compare(lastKey, endKey);
494
+ return upperExclusive ? cmp < 0 : cmp <= 0;
495
+ };
496
+ var findBoundaryEnd = (state, leaf, endKey, upperExclusive, leafCount) => {
497
+ const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
498
+ const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
499
+ return endBound < leafCount ? endBound : leafCount;
500
+ };
501
+ var RANGE_PREALLOC_THRESHOLD = 200;
502
+ var allocateRangeOutput = (state, cursor, startKey, endKey, options) => {
503
+ const firstLeafCount = leafEntryCount(cursor.leaf);
504
+ const firstLeafRemainder = firstLeafCount - cursor.index;
505
+ if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursor.leaf.next !== null) {
506
+ const lastKey = leafEntryAt(cursor.leaf, firstLeafCount - 1).key;
507
+ if (isLastEntryInRange(lastKey, endKey, cursor.compare, cursor.upperExclusive)) {
508
+ return new Array(
509
+ countRangeEntries(state, startKey, endKey, options)
510
+ );
511
+ }
512
+ }
513
+ return [];
514
+ };
515
+ var appendLeafSlicePublic = (leaf, from, to, output, useIndexed, writeIdx) => {
516
+ if (useIndexed) {
517
+ for (let i = from; i < to; i += 1) {
518
+ output[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
519
+ }
520
+ } else {
521
+ for (let i = from; i < to; i += 1) {
522
+ output.push(freezeEntry(leafEntryAt(leaf, i)));
523
+ }
524
+ }
525
+ return writeIdx;
526
+ };
527
+ var collectPublicEntries = (state, cursor, endKey, output) => {
528
+ const { compare, upperExclusive } = cursor;
529
+ let cursorLeaf = cursor.leaf;
530
+ let cursorIndex = cursor.index;
531
+ let writeIdx = 0;
532
+ const useIndexed = output.length > 0;
533
+ while (cursorLeaf !== null) {
534
+ const leafCount = leafEntryCount(cursorLeaf);
535
+ if (cursorIndex >= leafCount) {
536
+ cursorLeaf = cursorLeaf.next;
537
+ cursorIndex = 0;
538
+ continue;
539
+ }
540
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
541
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
542
+ writeIdx = appendLeafSlicePublic(
543
+ cursorLeaf,
544
+ cursorIndex,
545
+ leafCount,
546
+ output,
547
+ useIndexed,
548
+ writeIdx
549
+ );
550
+ cursorLeaf = cursorLeaf.next;
551
+ cursorIndex = 0;
552
+ continue;
553
+ }
554
+ const limit = findBoundaryEnd(
555
+ state,
556
+ cursorLeaf,
557
+ endKey,
558
+ upperExclusive,
559
+ leafCount
560
+ );
561
+ appendLeafSlicePublic(
562
+ cursorLeaf,
563
+ cursorIndex,
564
+ limit,
565
+ output,
566
+ useIndexed,
567
+ writeIdx
568
+ );
569
+ return;
570
+ }
571
+ };
572
+ var rangeQueryPublicEntries = (state, startKey, endKey, options) => {
573
+ const cursor = initRangeCursor(state, startKey, endKey, options);
574
+ if (cursor === null) return [];
575
+ const output = allocateRangeOutput(state, cursor, startKey, endKey, options);
576
+ collectPublicEntries(state, cursor, endKey, output);
577
+ return output;
578
+ };
579
+ var forEachRangeEntries = (state, startKey, endKey, callback, options) => {
580
+ const cursor = initRangeCursor(state, startKey, endKey, options);
581
+ if (cursor === null) return;
582
+ let cursorLeaf = cursor.leaf;
583
+ let cursorIndex = cursor.index;
584
+ const { compare, upperExclusive } = cursor;
585
+ while (cursorLeaf !== null) {
586
+ const leafCount = leafEntryCount(cursorLeaf);
587
+ if (cursorIndex >= leafCount) {
588
+ cursorLeaf = cursorLeaf.next;
589
+ cursorIndex = 0;
590
+ continue;
591
+ }
592
+ const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
593
+ if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
594
+ for (let i = cursorIndex; i < leafCount; i += 1) {
595
+ callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
596
+ }
597
+ cursorLeaf = cursorLeaf.next;
598
+ cursorIndex = 0;
599
+ continue;
600
+ }
601
+ const limit = findBoundaryEnd(
602
+ state,
603
+ cursorLeaf,
604
+ endKey,
605
+ upperExclusive,
606
+ leafCount
607
+ );
608
+ for (let i = cursorIndex; i < limit; i += 1) {
609
+ callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
610
+ }
611
+ return;
612
+ }
613
+ };
614
+
615
+ // src/btree/rebalance-branch.ts
414
616
  var updateMinKeyInAncestors = (node) => {
415
617
  let current = node;
416
618
  while (current.parent !== null) {
@@ -426,12 +628,9 @@ var requireParent = (node) => {
426
628
  }
427
629
  return node.parent;
428
630
  };
429
- var requireLeafNode = (node) => {
430
- if (!isLeafNode(node)) throw new BTreeInvariantError("expected leaf, got branch");
431
- return node;
432
- };
433
631
  var requireBranchNode = (node) => {
434
- if (isLeafNode(node)) throw new BTreeInvariantError("expected branch, got leaf");
632
+ if (isLeafNode(node))
633
+ throw new BTreeInvariantError("expected branch, got leaf");
435
634
  return node;
436
635
  };
437
636
  var removeChildFromBranch = (branch, childIndex) => {
@@ -440,27 +639,18 @@ var removeChildFromBranch = (branch, childIndex) => {
440
639
  }
441
640
  branchRemoveAt(branch, childIndex);
442
641
  };
443
- var detachLeafFromChain = (state, leaf) => {
444
- if (leaf.prev !== null) {
445
- leaf.prev.next = leaf.next;
446
- } else if (leaf.next !== null) {
447
- state.leftmostLeaf = leaf.next;
448
- }
449
- if (leaf.next !== null) {
450
- leaf.next.prev = leaf.prev;
451
- } else if (leaf.prev !== null) {
452
- state.rightmostLeaf = leaf.prev;
453
- }
454
- leaf.prev = null;
455
- leaf.next = null;
456
- };
457
642
  var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
458
643
  const borrowedChild = leftSibling.children.pop();
459
- if (borrowedChild === void 0) throw new BTreeInvariantError("left branch borrow failed");
644
+ if (borrowedChild === void 0)
645
+ throw new BTreeInvariantError("left branch borrow failed");
460
646
  leftSibling.keys.pop();
461
647
  borrowedChild.parent = branch;
462
- const borrowedMinKey = { key: void 0, sequence: 0 };
463
- if (!writeMinKeyTo(borrowedChild, borrowedMinKey)) throw new BTreeInvariantError("borrowed child has no min key");
648
+ const borrowedMinKey = {
649
+ key: void 0,
650
+ sequence: 0
651
+ };
652
+ if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
653
+ throw new BTreeInvariantError("borrowed child has no min key");
464
654
  if (branch.childOffset > 0) {
465
655
  branch.childOffset -= 1;
466
656
  branch.children[branch.childOffset] = borrowedChild;
@@ -469,15 +659,20 @@ var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
469
659
  } else {
470
660
  branch.children.unshift(borrowedChild);
471
661
  branch.keys.unshift(borrowedMinKey);
472
- for (let i = 0; i < branch.children.length; i += 1) branch.children[i].indexInParent = i;
662
+ for (let i = 0; i < branch.children.length; i += 1)
663
+ branch.children[i].indexInParent = i;
473
664
  }
474
665
  const parent = requireParent(branch);
475
- parent.keys[branchIndex] = { key: borrowedMinKey.key, sequence: borrowedMinKey.sequence };
666
+ parent.keys[branchIndex] = {
667
+ key: borrowedMinKey.key,
668
+ sequence: borrowedMinKey.sequence
669
+ };
476
670
  updateMinKeyInAncestors(branch);
477
671
  };
478
672
  var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
479
673
  const shiftIdx = rightSibling.childOffset;
480
- if (shiftIdx >= rightSibling.children.length) throw new BTreeInvariantError("right branch borrow failed");
674
+ if (shiftIdx >= rightSibling.children.length)
675
+ throw new BTreeInvariantError("right branch borrow failed");
481
676
  const borrowedChild = rightSibling.children[shiftIdx];
482
677
  rightSibling.childOffset += 1;
483
678
  if (rightSibling.childOffset >= rightSibling.children.length >>> 1) {
@@ -485,8 +680,12 @@ var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
485
680
  }
486
681
  branch.children.push(borrowedChild);
487
682
  borrowedChild.parent = branch;
488
- const borrowedMinKey = { key: void 0, sequence: 0 };
489
- if (!writeMinKeyTo(borrowedChild, borrowedMinKey)) throw new BTreeInvariantError("borrowed child has no min key");
683
+ const borrowedMinKey = {
684
+ key: void 0,
685
+ sequence: 0
686
+ };
687
+ if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
688
+ throw new BTreeInvariantError("borrowed child has no min key");
490
689
  branch.keys.push(borrowedMinKey);
491
690
  borrowedChild.indexInParent = branch.children.length - 1;
492
691
  const parent = requireParent(branch);
@@ -539,7 +738,10 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
539
738
  const parent = branch.parent;
540
739
  if (parent === null) throw new BTreeInvariantError("branch has no parent");
541
740
  const branchIndex = branch.indexInParent;
542
- const { left: leftSibling, right: rightSibling } = findBranchSiblings(parent, branchIndex);
741
+ const { left: leftSibling, right: rightSibling } = findBranchSiblings(
742
+ parent,
743
+ branchIndex
744
+ );
543
745
  if (rightSibling !== null && branchChildCount(rightSibling) > state.minBranchChildren) {
544
746
  borrowFromRightBranch(branch, rightSibling, branchIndex);
545
747
  return;
@@ -558,44 +760,68 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
558
760
  }
559
761
  throw new BTreeInvariantError("no branch siblings to rebalance");
560
762
  };
763
+
764
+ // src/btree/rebalance.ts
765
+ var requireLeafNode = (node) => {
766
+ if (!isLeafNode(node))
767
+ throw new BTreeInvariantError("expected leaf, got branch");
768
+ return node;
769
+ };
770
+ var detachLeafFromChain = (state, leaf) => {
771
+ if (leaf.prev !== null) {
772
+ leaf.prev.next = leaf.next;
773
+ } else if (leaf.next !== null) {
774
+ state.leftmostLeaf = leaf.next;
775
+ }
776
+ if (leaf.next !== null) {
777
+ leaf.next.prev = leaf.prev;
778
+ } else if (leaf.prev !== null) {
779
+ state.rightmostLeaf = leaf.prev;
780
+ }
781
+ leaf.prev = null;
782
+ leaf.next = null;
783
+ };
561
784
  var mergeLeafEntries = (target, source) => {
562
785
  if (target.entryOffset > 0) {
563
786
  target.entries.copyWithin(0, target.entryOffset);
564
787
  target.entries.length = target.entries.length - target.entryOffset;
565
788
  target.entryOffset = 0;
566
789
  }
567
- const src = source.entries;
568
- for (let i = source.entryOffset; i < src.length; i += 1) target.entries.push(src[i]);
790
+ target.entries.push(...source.entries.slice(source.entryOffset));
569
791
  };
570
- var rebalanceAfterLeafRemoval = (state, leaf) => {
571
- if (leaf === state.root) {
572
- if (state.entryCount === 0) {
573
- state.leftmostLeaf = leaf;
574
- state.rightmostLeaf = leaf;
575
- }
576
- return;
792
+ var applyLazyThreshold = (min) => Math.max(1, Math.ceil(min / 4));
793
+ var leafRebalanceThreshold = (state) => {
794
+ if (state.deleteRebalancePolicy === "lazy") {
795
+ return applyLazyThreshold(state.minLeafEntries);
577
796
  }
578
- if (leafEntryCount(leaf) >= state.minLeafEntries) return;
579
- const parent = leaf.parent;
580
- if (parent === null) throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
581
- const leafIndex = leaf.indexInParent;
582
- const leftSibling = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
583
- const rightSibling = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
797
+ return state.minLeafEntries;
798
+ };
799
+ var findLeafSiblings = (parent, leafIndex) => {
800
+ const left = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
801
+ const right = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
802
+ return { left, right };
803
+ };
804
+ var tryBorrowFromLeafSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
584
805
  if (rightSibling !== null && leafEntryCount(rightSibling) > state.minLeafEntries) {
585
806
  const borrowed = leafShiftEntry(rightSibling);
586
- if (borrowed === void 0) throw new BTreeInvariantError("right leaf borrow failed");
807
+ if (borrowed === void 0)
808
+ throw new BTreeInvariantError("right leaf borrow failed");
587
809
  leaf.entries.push(borrowed);
588
810
  writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
589
- return;
811
+ return true;
590
812
  }
591
813
  if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
592
814
  const borrowed = leftSibling.entries.pop();
593
- if (borrowed === void 0) throw new BTreeInvariantError("left leaf borrow failed");
815
+ if (borrowed === void 0)
816
+ throw new BTreeInvariantError("left leaf borrow failed");
594
817
  leafUnshiftEntry(leaf, borrowed);
595
818
  parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
596
819
  updateMinKeyInAncestors(leaf);
597
- return;
820
+ return true;
598
821
  }
822
+ return false;
823
+ };
824
+ var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
599
825
  if (leftSibling !== null) {
600
826
  mergeLeafEntries(leftSibling, leaf);
601
827
  detachLeafFromChain(state, leaf);
@@ -612,6 +838,24 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
612
838
  }
613
839
  throw new BTreeInvariantError("no leaf siblings to rebalance");
614
840
  };
841
+ var rebalanceAfterLeafRemoval = (state, leaf) => {
842
+ if (leaf === state.root) {
843
+ if (state.entryCount === 0) {
844
+ state.leftmostLeaf = leaf;
845
+ state.rightmostLeaf = leaf;
846
+ }
847
+ return;
848
+ }
849
+ if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
850
+ const parent = leaf.parent;
851
+ if (parent === null)
852
+ throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
853
+ const leafIndex = leaf.indexInParent;
854
+ const { left, right } = findLeafSiblings(parent, leafIndex);
855
+ if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
856
+ return;
857
+ mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
858
+ };
615
859
 
616
860
  // src/btree/deleteRange.ts
617
861
  var navigateToStart = (state, startKey, lowerExclusive) => {
@@ -624,27 +868,18 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
624
868
  }
625
869
  return { leaf, idx };
626
870
  };
627
- var findRemoveEnd = (state, leaf, idx, endKey, upperExclusive) => {
871
+ var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
628
872
  const count = leafEntryCount(leaf);
629
- let removeEnd = idx;
630
- while (removeEnd < count) {
631
- const e = leafEntryAt(leaf, removeEnd);
632
- const cmpEnd = state.compareKeys(e.key, endKey);
633
- if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
634
- removeEnd += 1;
635
- }
636
- return removeEnd;
637
- };
638
- var computeTreeHeight = (state) => {
639
- let h = 0;
640
- let n = state.root;
641
- while (!isLeafNode(n)) {
642
- n = n.children[n.childOffset];
643
- h += 1;
873
+ const lastEntry = leafEntryAt(leaf, count - 1);
874
+ const cmpLast = state.compareKeys(lastEntry.key, endKey);
875
+ if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
876
+ return count;
644
877
  }
645
- return h;
878
+ const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
879
+ const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
880
+ return endBound < count ? endBound : count;
646
881
  };
647
- var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth) => {
882
+ var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
648
883
  if (state.entryKeys !== null) {
649
884
  for (let i = idx; i < idx + removeCount; i += 1) {
650
885
  state.entryKeys.delete(leafEntryAt(leaf, i).entryId);
@@ -659,11 +894,13 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
659
894
  updateMinKeyInAncestors(leaf);
660
895
  }
661
896
  const countAfterSplice = leafEntryCount(leaf);
662
- let maxIter = maxRebalanceDepth;
663
- while (maxIter > 0 && leaf !== state.root && leafEntryCount(leaf) < state.minLeafEntries) {
897
+ const rebalThreshold = leafRebalanceThreshold(state);
898
+ let safetyGuard = state.minLeafEntries + 4;
899
+ while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
664
900
  rebalanceAfterLeafRemoval(state, leaf);
665
- if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf) break;
666
- maxIter -= 1;
901
+ if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
902
+ break;
903
+ safetyGuard -= 1;
667
904
  }
668
905
  if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
669
906
  updateMinKeyInAncestors(leaf);
@@ -673,12 +910,9 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
673
910
  var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
674
911
  var deleteRangeEntries = (state, startKey, endKey, options) => {
675
912
  if (state.entryCount === 0) return 0;
676
- const boundCompared = state.compareKeys(startKey, endKey);
677
- if (boundCompared > 0) return 0;
913
+ if (isEmptyRange(state.compareKeys, startKey, endKey, options)) return 0;
678
914
  const lowerExclusive = options?.lowerBound === "exclusive";
679
915
  const upperExclusive = options?.upperBound === "exclusive";
680
- if (lowerExclusive && upperExclusive && boundCompared === 0) return 0;
681
- const treeHeight = computeTreeHeight(state);
682
916
  let deleted = 0;
683
917
  let needsNavigate = true;
684
918
  let leaf = null;
@@ -693,10 +927,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
693
927
  }
694
928
  if (idx >= leafEntryCount(leaf)) break;
695
929
  const count = leafEntryCount(leaf);
696
- const removeEnd = findRemoveEnd(state, leaf, idx, endKey, upperExclusive);
930
+ const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
697
931
  const removeCount = removeEnd - idx;
698
932
  if (removeCount === 0) break;
699
- const countAfterSplice = spliceLeafAndRebalance(state, leaf, idx, removeCount, treeHeight);
933
+ const countAfterSplice = spliceLeafAndRebalance(
934
+ state,
935
+ leaf,
936
+ idx,
937
+ removeCount
938
+ );
700
939
  deleted += removeCount;
701
940
  if (removeEnd < count) break;
702
941
  if (!isLeafStillValid(state, leaf)) {
@@ -715,6 +954,7 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
715
954
  };
716
955
 
717
956
  // src/btree/autoScale.ts
957
+ var minOccupancy = (max) => Math.ceil(max / 2);
718
958
  var AUTO_SCALE_TIERS = [
719
959
  { threshold: 0, maxLeaf: 32, maxBranch: 32 },
720
960
  { threshold: 1e3, maxLeaf: 64, maxBranch: 64 },
@@ -741,33 +981,50 @@ var computeNextAutoScaleThreshold = (entryCount) => {
741
981
  }
742
982
  return Number.MAX_SAFE_INTEGER;
743
983
  };
744
- var createInitialState = (config) => {
745
- if (typeof config.compareKeys !== "function") {
746
- throw new BTreeValidationError("compareKeys must be a function.");
747
- }
748
- const autoScale = config.autoScale === true;
984
+ var resolveInitialCapacity = (config, autoScale) => {
749
985
  if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
750
- throw new BTreeValidationError("autoScale conflicts with explicit capacity.");
986
+ throw new BTreeValidationError(
987
+ "autoScale conflicts with explicit capacity."
988
+ );
751
989
  }
752
- let maxLeafEntries;
753
- let maxBranchChildren;
754
990
  if (autoScale) {
755
991
  const tier = computeAutoScaleTier(0);
756
- maxLeafEntries = tier.maxLeaf;
757
- maxBranchChildren = tier.maxBranch;
758
- } else {
759
- maxLeafEntries = normalizeNodeCapacity(config.maxLeafEntries, "maxLeafEntries", DEFAULT_MAX_LEAF_ENTRIES);
760
- maxBranchChildren = normalizeNodeCapacity(config.maxBranchChildren, "maxBranchChildren", DEFAULT_MAX_BRANCH_CHILDREN);
992
+ return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
761
993
  }
994
+ return {
995
+ maxLeafEntries: normalizeNodeCapacity(
996
+ config.maxLeafEntries,
997
+ "maxLeafEntries",
998
+ DEFAULT_MAX_LEAF_ENTRIES
999
+ ),
1000
+ maxBranchChildren: normalizeNodeCapacity(
1001
+ config.maxBranchChildren,
1002
+ "maxBranchChildren",
1003
+ DEFAULT_MAX_BRANCH_CHILDREN
1004
+ )
1005
+ };
1006
+ };
1007
+ var createInitialState = (config) => {
1008
+ if (typeof config.compareKeys !== "function") {
1009
+ throw new BTreeValidationError("compareKeys must be a function.");
1010
+ }
1011
+ const autoScale = config.autoScale === true;
1012
+ const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
1013
+ config,
1014
+ autoScale
1015
+ );
762
1016
  const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
1017
+ const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
1018
+ config.deleteRebalancePolicy
1019
+ );
763
1020
  const emptyLeaf = createLeafNode([], null);
764
1021
  return {
765
1022
  compareKeys: config.compareKeys,
766
1023
  maxLeafEntries,
767
1024
  maxBranchChildren,
768
1025
  duplicateKeys,
769
- minLeafEntries: Math.ceil(maxLeafEntries / 2),
770
- minBranchChildren: Math.ceil(maxBranchChildren / 2),
1026
+ minLeafEntries: minOccupancy(maxLeafEntries),
1027
+ minBranchChildren: minOccupancy(maxBranchChildren),
771
1028
  root: emptyLeaf,
772
1029
  leftmostLeaf: emptyLeaf,
773
1030
  rightmostLeaf: emptyLeaf,
@@ -775,6 +1032,7 @@ var createInitialState = (config) => {
775
1032
  nextSequence: 0,
776
1033
  entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
777
1034
  autoScale,
1035
+ deleteRebalancePolicy,
778
1036
  _nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
779
1037
  _cursor: { leaf: emptyLeaf, index: 0 }
780
1038
  };
@@ -784,13 +1042,15 @@ var maybeAutoScale = (state) => {
784
1042
  const { maxLeaf, maxBranch } = computeAutoScaleTier(state.entryCount);
785
1043
  if (maxLeaf > state.maxLeafEntries) {
786
1044
  state.maxLeafEntries = maxLeaf;
787
- state.minLeafEntries = Math.ceil(maxLeaf / 2);
1045
+ state.minLeafEntries = minOccupancy(maxLeaf);
788
1046
  }
789
1047
  if (maxBranch > state.maxBranchChildren) {
790
1048
  state.maxBranchChildren = maxBranch;
791
- state.minBranchChildren = Math.ceil(maxBranch / 2);
1049
+ state.minBranchChildren = minOccupancy(maxBranch);
792
1050
  }
793
- state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(state.entryCount);
1051
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
1052
+ state.entryCount
1053
+ );
794
1054
  };
795
1055
  var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
796
1056
  if (!state.autoScale) {
@@ -814,8 +1074,16 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
814
1074
  }
815
1075
  state.maxLeafEntries = normalizedLeaf;
816
1076
  state.maxBranchChildren = normalizedBranch;
817
- state.minLeafEntries = Math.ceil(normalizedLeaf / 2);
818
- state.minBranchChildren = Math.ceil(normalizedBranch / 2);
1077
+ state.minLeafEntries = minOccupancy(normalizedLeaf);
1078
+ state.minBranchChildren = minOccupancy(normalizedBranch);
1079
+ };
1080
+ var resetAutoScaleToTier0 = (state) => {
1081
+ const tier = computeAutoScaleTier(0);
1082
+ state.maxLeafEntries = tier.maxLeaf;
1083
+ state.maxBranchChildren = tier.maxBranch;
1084
+ state.minLeafEntries = minOccupancy(tier.maxLeaf);
1085
+ state.minBranchChildren = minOccupancy(tier.maxBranch);
1086
+ state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
819
1087
  };
820
1088
 
821
1089
  // src/btree/bulkLoad.ts
@@ -837,7 +1105,11 @@ var computeChunkBoundaries = (total, max, min) => {
837
1105
  return boundaries;
838
1106
  };
839
1107
  var buildLeaves = (state, entries, ids, baseSequence) => {
840
- const boundaries = computeChunkBoundaries(entries.length, state.maxLeafEntries, state.minLeafEntries);
1108
+ const boundaries = computeChunkBoundaries(
1109
+ entries.length,
1110
+ state.maxLeafEntries,
1111
+ state.minLeafEntries
1112
+ );
841
1113
  const leaves = new Array(boundaries.length);
842
1114
  let chunkStart = 0;
843
1115
  for (let c = 0; c < boundaries.length; c += 1) {
@@ -845,7 +1117,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
845
1117
  const chunk = new Array(chunkEnd - chunkStart);
846
1118
  for (let i = chunkStart; i < chunkEnd; i += 1) {
847
1119
  const seq = baseSequence + i;
848
- chunk[i - chunkStart] = { key: entries[i].key, entryId: seq, value: entries[i].value };
1120
+ chunk[i - chunkStart] = createEntry(
1121
+ entries[i].key,
1122
+ seq,
1123
+ entries[i].value
1124
+ );
849
1125
  ids[i] = seq;
850
1126
  if (state.entryKeys !== null) {
851
1127
  state.entryKeys.set(seq, entries[i].key);
@@ -879,11 +1155,18 @@ var bulkLoadEntries = (state, entries) => {
879
1155
  } else {
880
1156
  let currentLevel = leaves;
881
1157
  while (currentLevel.length > 1) {
882
- const bounds = computeChunkBoundaries(currentLevel.length, state.maxBranchChildren, state.minBranchChildren);
1158
+ const bounds = computeChunkBoundaries(
1159
+ currentLevel.length,
1160
+ state.maxBranchChildren,
1161
+ state.minBranchChildren
1162
+ );
883
1163
  const nextLevel = new Array(bounds.length);
884
1164
  let start = 0;
885
1165
  for (let b = 0; b < bounds.length; b += 1) {
886
- nextLevel[b] = createBranchNode(currentLevel.slice(start, bounds[b]), null);
1166
+ nextLevel[b] = createBranchNode(
1167
+ currentLevel.slice(start, bounds[b]),
1168
+ null
1169
+ );
887
1170
  start = bounds[b];
888
1171
  }
889
1172
  currentLevel = nextLevel;
@@ -894,9 +1177,12 @@ var bulkLoadEntries = (state, entries) => {
894
1177
  return ids;
895
1178
  };
896
1179
 
897
- // src/btree/mutations.ts
1180
+ // src/btree/split.ts
898
1181
  var insertChildAfter = (state, parent, existingChild, childToInsert) => {
899
- const newChildMinKey = { key: void 0, sequence: 0 };
1182
+ const newChildMinKey = {
1183
+ key: void 0,
1184
+ sequence: 0
1185
+ };
900
1186
  if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
901
1187
  throw new BTreeInvariantError("inserted child has no min key");
902
1188
  }
@@ -924,60 +1210,146 @@ var splitLeaf = (state, leaf) => {
924
1210
  } else {
925
1211
  state.rightmostLeaf = sibling;
926
1212
  }
927
- leaf.next = sibling;
928
- if (leaf.parent === null) {
929
- state.root = createBranchNode([leaf, sibling], null);
930
- return;
1213
+ leaf.next = sibling;
1214
+ if (leaf.parent === null) {
1215
+ state.root = createBranchNode([leaf, sibling], null);
1216
+ return;
1217
+ }
1218
+ insertChildAfter(state, leaf.parent, leaf, sibling);
1219
+ };
1220
+ var splitBranch = (state, branch) => {
1221
+ branchCompact(branch);
1222
+ const splitAt = Math.ceil(branch.children.length / 2);
1223
+ const sibling = createBranchNode(
1224
+ branch.children.splice(splitAt),
1225
+ branch.parent
1226
+ );
1227
+ branch.keys.splice(splitAt);
1228
+ if (branch.parent === null) {
1229
+ state.root = createBranchNode([branch, sibling], null);
1230
+ return;
1231
+ }
1232
+ insertChildAfter(state, branch.parent, branch, sibling);
1233
+ };
1234
+
1235
+ // src/btree/entry-lookup.ts
1236
+ var findLeafEntryBySequence = (state, userKey, sequence) => {
1237
+ const targetLeaf = findLeafForKey(state, userKey, sequence);
1238
+ const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
1239
+ if (index >= leafEntryCount(targetLeaf)) return null;
1240
+ const entry = leafEntryAt(targetLeaf, index);
1241
+ if (entry.entryId !== sequence) return null;
1242
+ return { leaf: targetLeaf, index };
1243
+ };
1244
+ var peekEntryById = (state, entryId) => {
1245
+ if (state.entryKeys === null) {
1246
+ throw new BTreeInvariantError(
1247
+ "entryKeys lookup map is not enabled on this tree."
1248
+ );
1249
+ }
1250
+ const userKey = state.entryKeys.get(entryId);
1251
+ if (userKey === void 0) return null;
1252
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1253
+ if (found === null) return null;
1254
+ const entry = leafEntryAt(found.leaf, found.index);
1255
+ return entry;
1256
+ };
1257
+ var updateEntryById = (state, entryId, newValue) => {
1258
+ if (state.entryKeys === null) {
1259
+ throw new BTreeInvariantError(
1260
+ "entryKeys lookup map is not enabled on this tree."
1261
+ );
1262
+ }
1263
+ const userKey = state.entryKeys.get(entryId);
1264
+ if (userKey === void 0) return null;
1265
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1266
+ if (found === null) return null;
1267
+ const entry = leafEntryAt(found.leaf, found.index);
1268
+ const updated = createEntry(entry.key, entry.entryId, newValue);
1269
+ found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
1270
+ return updated;
1271
+ };
1272
+ var removeEntryById = (state, entryId) => {
1273
+ if (state.entryKeys === null) {
1274
+ throw new BTreeInvariantError(
1275
+ "entryKeys lookup map is not enabled on this tree."
1276
+ );
1277
+ }
1278
+ const userKey = state.entryKeys.get(entryId);
1279
+ if (userKey === void 0) return null;
1280
+ const found = findLeafEntryBySequence(state, userKey, entryId);
1281
+ if (found === null) return null;
1282
+ const entry = leafEntryAt(found.leaf, found.index);
1283
+ leafRemoveAt(found.leaf, found.index);
1284
+ state.entryCount -= 1;
1285
+ state.entryKeys.delete(entryId);
1286
+ if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
1287
+ updateMinKeyInAncestors(found.leaf);
1288
+ }
1289
+ if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
1290
+ rebalanceAfterLeafRemoval(state, found.leaf);
931
1291
  }
932
- insertChildAfter(state, leaf.parent, leaf, sibling);
1292
+ return entry;
933
1293
  };
934
- var splitBranch = (state, branch) => {
935
- branchCompact(branch);
936
- const splitAt = Math.ceil(branch.children.length / 2);
937
- const sibling = createBranchNode(branch.children.splice(splitAt), branch.parent);
938
- branch.keys.splice(splitAt);
939
- if (branch.parent === null) {
940
- state.root = createBranchNode([branch, sibling], null);
941
- return;
1294
+
1295
+ // src/btree/mutations.ts
1296
+ var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
1297
+ if (state.duplicateKeys === "allow") return null;
1298
+ if (insertAt > 0) {
1299
+ const candidate = leafEntryAt(targetLeaf, insertAt - 1);
1300
+ if (state.compareKeys(candidate.key, key) === 0) {
1301
+ return {
1302
+ leaf: targetLeaf,
1303
+ physIndex: targetLeaf.entryOffset + insertAt - 1,
1304
+ entry: candidate
1305
+ };
1306
+ }
1307
+ } else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
1308
+ const prevLeaf = targetLeaf.prev;
1309
+ const prevCount = leafEntryCount(prevLeaf);
1310
+ const candidate = leafEntryAt(prevLeaf, prevCount - 1);
1311
+ if (state.compareKeys(candidate.key, key) === 0) {
1312
+ return {
1313
+ leaf: prevLeaf,
1314
+ physIndex: prevLeaf.entryOffset + prevCount - 1,
1315
+ entry: candidate
1316
+ };
1317
+ }
942
1318
  }
943
- insertChildAfter(state, branch.parent, branch, sibling);
1319
+ return null;
944
1320
  };
945
1321
  var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
946
1322
  const sequence = state.nextSequence;
947
1323
  const insertAt = upperBoundInLeaf(state, targetLeaf, key, sequence);
948
- if (state.duplicateKeys !== "allow") {
949
- let existingEntry = null;
950
- if (insertAt > 0) {
951
- const candidate = leafEntryAt(targetLeaf, insertAt - 1);
952
- if (state.compareKeys(candidate.key, key) === 0) {
953
- existingEntry = candidate;
954
- }
955
- } else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
956
- const prevLeaf = targetLeaf.prev;
957
- const candidate = leafEntryAt(prevLeaf, leafEntryCount(prevLeaf) - 1);
958
- if (state.compareKeys(candidate.key, key) === 0) {
959
- existingEntry = candidate;
960
- }
961
- }
962
- if (existingEntry !== null) {
963
- if (state.duplicateKeys === "reject") {
964
- throw new BTreeValidationError("Duplicate key rejected.");
965
- }
966
- existingEntry.value = value;
967
- return existingEntry.entryId;
968
- }
1324
+ const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
1325
+ if (dup !== null) {
1326
+ if (state.duplicateKeys === "reject") {
1327
+ throw new BTreeValidationError("Duplicate key rejected.");
1328
+ }
1329
+ dup.leaf.entries[dup.physIndex] = createEntry(
1330
+ dup.entry.key,
1331
+ dup.entry.entryId,
1332
+ value
1333
+ );
1334
+ return dup.entry.entryId;
969
1335
  }
970
1336
  if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
971
1337
  throw new BTreeValidationError("Sequence overflow.");
972
1338
  }
973
1339
  state.nextSequence += 1;
974
- leafInsertAt(targetLeaf, insertAt, { key, entryId: sequence, value });
1340
+ leafInsertAt(
1341
+ targetLeaf,
1342
+ insertAt,
1343
+ createEntry(key, sequence, value)
1344
+ );
975
1345
  state.entryCount += 1;
976
1346
  if (state.entryKeys !== null) {
977
1347
  state.entryKeys.set(sequence, key);
978
1348
  }
979
- if (insertAt === 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
980
- if (leafEntryCount(targetLeaf) > state.maxLeafEntries) splitLeaf(state, targetLeaf);
1349
+ if (insertAt === 0 && targetLeaf.parent !== null)
1350
+ updateMinKeyInAncestors(targetLeaf);
1351
+ if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
1352
+ splitLeaf(state, targetLeaf);
981
1353
  maybeAutoScale(state);
982
1354
  return sequence;
983
1355
  };
@@ -988,7 +1360,8 @@ var putEntry = (state, key, value) => {
988
1360
  var popFirstEntry = (state) => {
989
1361
  if (state.entryCount === 0) return null;
990
1362
  const firstEntry = leafShiftEntry(state.leftmostLeaf);
991
- if (firstEntry === void 0) throw new BTreeInvariantError("leftmost leaf empty but count > 0");
1363
+ if (firstEntry === void 0)
1364
+ throw new BTreeInvariantError("leftmost leaf empty but count > 0");
992
1365
  state.entryCount -= 1;
993
1366
  if (state.entryKeys !== null) {
994
1367
  state.entryKeys.delete(firstEntry.entryId);
@@ -1004,7 +1377,8 @@ var popFirstEntry = (state) => {
1004
1377
  var popLastEntry = (state) => {
1005
1378
  if (state.entryCount === 0) return null;
1006
1379
  const lastEntry = leafPopEntry(state.rightmostLeaf);
1007
- if (lastEntry === void 0) throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1380
+ if (lastEntry === void 0)
1381
+ throw new BTreeInvariantError("rightmost leaf empty but count > 0");
1008
1382
  state.entryCount -= 1;
1009
1383
  if (state.entryKeys !== null) {
1010
1384
  state.entryKeys.delete(lastEntry.entryId);
@@ -1025,62 +1399,26 @@ var removeFirstMatchingEntry = (state, key) => {
1025
1399
  if (state.entryKeys !== null) {
1026
1400
  state.entryKeys.delete(targetEntry.entryId);
1027
1401
  }
1028
- if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null) updateMinKeyInAncestors(targetLeaf);
1402
+ if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
1403
+ updateMinKeyInAncestors(targetLeaf);
1029
1404
  if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
1030
1405
  rebalanceAfterLeafRemoval(state, targetLeaf);
1031
1406
  }
1032
1407
  return targetEntry;
1033
1408
  };
1034
- var findLeafEntryBySequence = (state, userKey, sequence) => {
1035
- const targetLeaf = findLeafForKey(state, userKey, sequence);
1036
- const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
1037
- if (index >= leafEntryCount(targetLeaf)) return null;
1038
- const entry = leafEntryAt(targetLeaf, index);
1039
- if (entry.entryId !== sequence) return null;
1040
- return { leaf: targetLeaf, index };
1041
- };
1042
- var removeEntryById = (state, entryId) => {
1043
- const userKey = state.entryKeys.get(entryId);
1044
- if (userKey === void 0) return null;
1045
- const found = findLeafEntryBySequence(state, userKey, entryId);
1046
- if (found === null) return null;
1047
- const entry = leafEntryAt(found.leaf, found.index);
1048
- leafRemoveAt(found.leaf, found.index);
1049
- state.entryCount -= 1;
1050
- state.entryKeys.delete(entryId);
1051
- if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
1052
- updateMinKeyInAncestors(found.leaf);
1053
- }
1054
- if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
1055
- rebalanceAfterLeafRemoval(state, found.leaf);
1056
- }
1057
- return entry;
1058
- };
1059
- var peekEntryById = (state, entryId) => {
1060
- const userKey = state.entryKeys.get(entryId);
1061
- if (userKey === void 0) return null;
1062
- const found = findLeafEntryBySequence(state, userKey, entryId);
1063
- if (found === null) return null;
1064
- const entry = leafEntryAt(found.leaf, found.index);
1065
- return entry;
1066
- };
1067
- var updateEntryById = (state, entryId, newValue) => {
1068
- const userKey = state.entryKeys.get(entryId);
1069
- if (userKey === void 0) return null;
1070
- const found = findLeafEntryBySequence(state, userKey, entryId);
1071
- if (found === null) return null;
1072
- const entry = leafEntryAt(found.leaf, found.index);
1073
- entry.value = newValue;
1074
- return entry;
1075
- };
1076
1409
  var putManyEntries = (state, entries) => {
1077
1410
  if (entries.length === 0) return [];
1078
1411
  const strictlyAscending = state.duplicateKeys !== "allow";
1079
1412
  for (let i = 1; i < entries.length; i += 1) {
1080
1413
  const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
1081
- if (strictlyAscending ? cmp >= 0 : cmp > 0) {
1414
+ if (cmp > 0) {
1415
+ throw new BTreeValidationError(
1416
+ "putMany: entries not in ascending order."
1417
+ );
1418
+ }
1419
+ if (strictlyAscending && cmp === 0) {
1082
1420
  throw new BTreeValidationError(
1083
- strictlyAscending ? "putMany: not sorted in strict ascending order." : "putMany: not sorted in non-descending order."
1421
+ state.duplicateKeys === "reject" ? "putMany: duplicate key rejected." : "putMany: equal keys not allowed in strict mode."
1084
1422
  );
1085
1423
  }
1086
1424
  }
@@ -1089,7 +1427,12 @@ var putManyEntries = (state, entries) => {
1089
1427
  let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
1090
1428
  for (let i = 0; i < entries.length; i += 1) {
1091
1429
  const entry = entries[i];
1092
- const targetLeaf = findLeafFromHint(state, hintLeaf, entry.key, state.nextSequence);
1430
+ const targetLeaf = findLeafFromHint(
1431
+ state,
1432
+ hintLeaf,
1433
+ entry.key,
1434
+ state.nextSequence
1435
+ );
1093
1436
  ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
1094
1437
  hintLeaf = targetLeaf;
1095
1438
  }
@@ -1098,116 +1441,14 @@ var putManyEntries = (state, entries) => {
1098
1441
  return bulkLoadEntries(state, entries);
1099
1442
  };
1100
1443
 
1101
- // src/btree/rangeQuery.ts
1102
- var initRangeCursor = (state, startKey, endKey, options) => {
1103
- if (state.entryCount === 0) return null;
1104
- const compare = state.compareKeys;
1105
- const boundCompared = compare(startKey, endKey);
1106
- if (boundCompared > 0) return null;
1107
- const lowerExclusive = options?.lowerBound === "exclusive";
1108
- const upperExclusive = options?.upperBound === "exclusive";
1109
- if (lowerExclusive && upperExclusive && boundCompared === 0) return null;
1110
- const startSeq = lowerExclusive ? Number.MAX_SAFE_INTEGER : 0;
1111
- const leaf = findLeafForKey(state, startKey, startSeq);
1112
- const index = lowerExclusive ? upperBoundInLeaf(state, leaf, startKey, Number.MAX_SAFE_INTEGER) : lowerBoundInLeaf(state, leaf, startKey, 0);
1113
- return { leaf, index, compare, upperExclusive };
1114
- };
1115
- var countRangeEntries = (state, startKey, endKey, options) => {
1116
- const cursor = initRangeCursor(state, startKey, endKey, options);
1117
- if (cursor === null) return 0;
1118
- let cursorLeaf = cursor.leaf;
1119
- let cursorIndex = cursor.index;
1120
- const { compare, upperExclusive } = cursor;
1121
- let count = 0;
1122
- while (cursorLeaf !== null) {
1123
- const leafCount = leafEntryCount(cursorLeaf);
1124
- if (cursorIndex >= leafCount) {
1125
- cursorLeaf = cursorLeaf.next;
1126
- cursorIndex = 0;
1127
- continue;
1128
- }
1129
- const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
1130
- const cmpLast = compare(lastEntry.key, endKey);
1131
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
1132
- count += leafCount - cursorIndex;
1133
- cursorLeaf = cursorLeaf.next;
1134
- cursorIndex = 0;
1135
- continue;
1136
- }
1137
- const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
1138
- const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
1139
- const limit = endBound < leafCount ? endBound : leafCount;
1140
- count += limit - cursorIndex;
1141
- return count;
1142
- }
1143
- return count;
1144
- };
1145
- var RANGE_PREALLOC_THRESHOLD = 200;
1146
- var allocateRangeOutput = (state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options) => {
1147
- const firstLeafCount = leafEntryCount(cursorLeaf);
1148
- const firstLeafRemainder = firstLeafCount - cursorIndex;
1149
- if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursorLeaf.next !== null) {
1150
- const lastEntry = leafEntryAt(cursorLeaf, firstLeafCount - 1);
1151
- const cmpLast = compare(lastEntry.key, endKey);
1152
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
1153
- const total = countRangeEntries(state, startKey, endKey, options);
1154
- return new Array(total);
1155
- }
1156
- }
1157
- return [];
1158
- };
1159
- var appendLeafSlice = (leaf, from, to, output, useIndexed, writeIdx) => {
1160
- if (useIndexed) {
1161
- for (let i = from; i < to; i += 1) {
1162
- output[writeIdx++] = leafEntryAt(leaf, i);
1163
- }
1164
- } else {
1165
- for (let i = from; i < to; i += 1) {
1166
- output.push(leafEntryAt(leaf, i));
1167
- }
1168
- }
1169
- return writeIdx;
1170
- };
1171
- var rangeQueryEntries = (state, startKey, endKey, options) => {
1172
- const cursor = initRangeCursor(state, startKey, endKey, options);
1173
- if (cursor === null) return [];
1174
- let cursorLeaf = cursor.leaf;
1175
- let cursorIndex = cursor.index;
1176
- const { compare, upperExclusive } = cursor;
1177
- const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
1178
- let writeIdx = 0;
1179
- const useIndexed = output.length > 0;
1180
- while (cursorLeaf !== null) {
1181
- const leafCount = leafEntryCount(cursorLeaf);
1182
- if (cursorIndex >= leafCount) {
1183
- cursorLeaf = cursorLeaf.next;
1184
- cursorIndex = 0;
1185
- continue;
1186
- }
1187
- const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
1188
- const cmpLast = compare(lastEntry.key, endKey);
1189
- if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
1190
- writeIdx = appendLeafSlice(cursorLeaf, cursorIndex, leafCount, output, useIndexed, writeIdx);
1191
- cursorLeaf = cursorLeaf.next;
1192
- cursorIndex = 0;
1193
- continue;
1194
- }
1195
- const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
1196
- const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
1197
- const limit = endBound < leafCount ? endBound : leafCount;
1198
- appendLeafSlice(cursorLeaf, cursorIndex, limit, output, useIndexed, writeIdx);
1199
- return output;
1200
- }
1201
- return output;
1202
- };
1203
-
1204
1444
  // src/btree/serialization.ts
1205
1445
  var buildConfigFromState = (state) => {
1206
1446
  const config = {
1207
1447
  compareKeys: state.compareKeys,
1208
1448
  duplicateKeys: state.duplicateKeys,
1209
1449
  enableEntryIdLookup: state.entryKeys !== null,
1210
- autoScale: state.autoScale
1450
+ autoScale: state.autoScale,
1451
+ deleteRebalancePolicy: state.deleteRebalancePolicy
1211
1452
  };
1212
1453
  if (!state.autoScale) {
1213
1454
  config.maxLeafEntries = state.maxLeafEntries;
@@ -1227,17 +1468,17 @@ var serializeToJSON = (state) => {
1227
1468
  }
1228
1469
  leaf = leaf.next;
1229
1470
  }
1230
- return {
1231
- version: 1,
1232
- config: {
1233
- maxLeafEntries: state.maxLeafEntries,
1234
- maxBranchChildren: state.maxBranchChildren,
1235
- duplicateKeys: state.duplicateKeys,
1236
- enableEntryIdLookup: state.entryKeys !== null,
1237
- autoScale: state.autoScale
1238
- },
1239
- entries
1471
+ const config = {
1472
+ maxLeafEntries: state.maxLeafEntries,
1473
+ maxBranchChildren: state.maxBranchChildren,
1474
+ duplicateKeys: state.duplicateKeys,
1475
+ enableEntryIdLookup: state.entryKeys !== null,
1476
+ autoScale: state.autoScale
1240
1477
  };
1478
+ if (state.deleteRebalancePolicy !== "standard") {
1479
+ config.deleteRebalancePolicy = state.deleteRebalancePolicy;
1480
+ }
1481
+ return { version: 1, config, entries };
1241
1482
  };
1242
1483
  var MAX_SERIALIZED_ENTRIES = 1e6;
1243
1484
  var validateStructure = (json) => {
@@ -1300,13 +1541,28 @@ var validateBTreeJSON = (json) => {
1300
1541
  validateStructure(json);
1301
1542
  validateConfig(json.config);
1302
1543
  };
1544
+ var validateBTreeJSONSortOrder = (json, compareKeys) => {
1545
+ const strict = json.config.duplicateKeys !== "allow";
1546
+ for (let i = 1; i < json.entries.length; i += 1) {
1547
+ const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
1548
+ if (cmp > 0) {
1549
+ throw new BTreeValidationError("fromJSON: entries not sorted.");
1550
+ }
1551
+ if (strict && cmp === 0) {
1552
+ throw new BTreeValidationError(
1553
+ 'fromJSON: duplicate keys require duplicateKeys "allow".'
1554
+ );
1555
+ }
1556
+ }
1557
+ };
1303
1558
  var buildConfigFromJSON = (json, compareKeys) => {
1304
1559
  const cfg = json.config;
1305
1560
  const config = {
1306
1561
  compareKeys,
1307
1562
  duplicateKeys: cfg.duplicateKeys,
1308
1563
  enableEntryIdLookup: cfg.enableEntryIdLookup,
1309
- autoScale: cfg.autoScale
1564
+ autoScale: cfg.autoScale,
1565
+ deleteRebalancePolicy: cfg.deleteRebalancePolicy
1310
1566
  };
1311
1567
  if (!cfg.autoScale) {
1312
1568
  config.maxLeafEntries = cfg.maxLeafEntries;
@@ -1315,6 +1571,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
1315
1571
  return config;
1316
1572
  };
1317
1573
 
1574
+ // src/btree/traversal.ts
1575
+ var snapshotEntries = (state) => {
1576
+ const result = new Array(state.entryCount);
1577
+ let leaf = state.leftmostLeaf;
1578
+ let writeIdx = 0;
1579
+ while (leaf !== null) {
1580
+ const count = leafEntryCount(leaf);
1581
+ for (let i = 0; i < count; i += 1) {
1582
+ result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
1583
+ }
1584
+ leaf = leaf.next;
1585
+ }
1586
+ return result;
1587
+ };
1588
+ var collectInternalEntries = (state) => {
1589
+ const result = new Array(state.entryCount);
1590
+ let leaf = state.leftmostLeaf;
1591
+ let writeIdx = 0;
1592
+ while (leaf !== null) {
1593
+ const count = leafEntryCount(leaf);
1594
+ for (let i = 0; i < count; i += 1) {
1595
+ result[writeIdx++] = leafEntryAt(leaf, i);
1596
+ }
1597
+ leaf = leaf.next;
1598
+ }
1599
+ return result;
1600
+ };
1601
+ var forEachEntry = (state, callback, thisArg) => {
1602
+ let leaf = state.leftmostLeaf;
1603
+ while (leaf !== null) {
1604
+ const count = leafEntryCount(leaf);
1605
+ for (let i = 0; i < count; i += 1) {
1606
+ callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
1607
+ }
1608
+ leaf = leaf.next;
1609
+ }
1610
+ };
1611
+
1318
1612
  // src/btree/integrity-helpers.ts
1319
1613
  var nodeMinKey = (node) => {
1320
1614
  if (isLeafNode(node)) {
@@ -1323,14 +1617,17 @@ var nodeMinKey = (node) => {
1323
1617
  return { key: e.key, sequence: e.entryId };
1324
1618
  }
1325
1619
  if (node.childOffset >= node.keys.length) return null;
1326
- return { key: node.keys[node.childOffset].key, sequence: node.keys[node.childOffset].sequence };
1620
+ return {
1621
+ key: node.keys[node.childOffset].key,
1622
+ sequence: node.keys[node.childOffset].sequence
1623
+ };
1327
1624
  };
1328
1625
  var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
1329
1626
  const cmp = comparator(leftKey, rightKey);
1330
1627
  if (cmp !== 0) {
1331
1628
  return cmp;
1332
1629
  }
1333
- return leftSeq - rightSeq;
1630
+ return leftSeq < rightSeq ? -1 : leftSeq > rightSeq ? 1 : 0;
1334
1631
  };
1335
1632
  var getNodeMaxKey = (node) => {
1336
1633
  if (isLeafNode(node)) {
@@ -1345,9 +1642,7 @@ var getNodeMaxKey = (node) => {
1345
1642
  };
1346
1643
  var validateComparatorResult = (result) => {
1347
1644
  if (!Number.isFinite(result)) {
1348
- throw new BTreeValidationError(
1349
- "compareKeys must return a finite number."
1350
- );
1645
+ throw new BTreeValidationError("compareKeys must return a finite number.");
1351
1646
  }
1352
1647
  return result;
1353
1648
  };
@@ -1407,6 +1702,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
1407
1702
  throw error;
1408
1703
  }
1409
1704
  };
1705
+ var validateAdjacentLeafOrdering = (state, previous, cursor) => {
1706
+ const prevMax = getNodeMaxKey(previous);
1707
+ const currentMin = nodeMinKey(cursor);
1708
+ if (prevMax === null || currentMin === null) {
1709
+ throw new BTreeInvariantError(
1710
+ "Non-empty tree leaf chain contains empty leaf node."
1711
+ );
1712
+ }
1713
+ if (compareNodeKeys(
1714
+ state.compareKeys,
1715
+ prevMax.key,
1716
+ prevMax.sequence,
1717
+ currentMin.key,
1718
+ currentMin.sequence
1719
+ ) > 0) {
1720
+ throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
1721
+ }
1722
+ const prevCount = leafEntryCount(previous);
1723
+ const curCount = leafEntryCount(cursor);
1724
+ if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
1725
+ leafEntryAt(previous, prevCount - 1).key,
1726
+ leafEntryAt(cursor, 0).key
1727
+ ) === 0) {
1728
+ throw new BTreeInvariantError(
1729
+ "Duplicate user key detected across adjacent leaves with uniqueness policy."
1730
+ );
1731
+ }
1732
+ };
1410
1733
  var validateLeafChainStep = (state, cursor, previous, visited) => {
1411
1734
  if (!isLeafNode(cursor)) {
1412
1735
  throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
@@ -1418,22 +1741,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
1418
1741
  throw new BTreeInvariantError("Leaf prev pointer mismatch.");
1419
1742
  }
1420
1743
  if (previous !== null && isLeafNode(previous)) {
1421
- const prevMax = getNodeMaxKey(previous);
1422
- const currentMin = nodeMinKey(cursor);
1423
- if (prevMax === null || currentMin === null) {
1424
- throw new BTreeInvariantError("Non-empty tree leaf chain contains empty leaf node.");
1425
- }
1426
- if (compareNodeKeys(state.compareKeys, prevMax.key, prevMax.sequence, currentMin.key, currentMin.sequence) > 0) {
1427
- throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
1428
- }
1429
- const prevCount = leafEntryCount(previous);
1430
- const curCount = leafEntryCount(cursor);
1431
- if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
1432
- leafEntryAt(previous, prevCount - 1).key,
1433
- leafEntryAt(cursor, 0).key
1434
- ) === 0) {
1435
- throw new BTreeInvariantError("Duplicate user key detected across adjacent leaves with uniqueness policy.");
1436
- }
1744
+ validateAdjacentLeafOrdering(state, previous, cursor);
1437
1745
  }
1438
1746
  };
1439
1747
  var validateLeafLinks = (state, expectedLeafCount) => {
@@ -1442,7 +1750,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1442
1750
  throw new BTreeInvariantError("Empty tree root must be a leaf node.");
1443
1751
  }
1444
1752
  if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
1445
- throw new BTreeInvariantError("Empty tree leaf pointers must reference root leaf.");
1753
+ throw new BTreeInvariantError(
1754
+ "Empty tree leaf pointers must reference root leaf."
1755
+ );
1446
1756
  }
1447
1757
  return;
1448
1758
  }
@@ -1467,7 +1777,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
1467
1777
  throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
1468
1778
  }
1469
1779
  if (leafCount !== expectedLeafCount) {
1470
- throw new BTreeInvariantError("Leaf chain count mismatch with tree traversal count.");
1780
+ throw new BTreeInvariantError(
1781
+ "Leaf chain count mismatch with tree traversal count."
1782
+ );
1471
1783
  }
1472
1784
  };
1473
1785
 
@@ -1494,7 +1806,9 @@ var validateLeafNodeOrdering = (state, node) => {
1494
1806
  leafEntryAt(node, index - 1).key,
1495
1807
  leafEntryAt(node, index).key
1496
1808
  ) === 0) {
1497
- throw new BTreeInvariantError("Duplicate user key detected in tree with uniqueness policy.");
1809
+ throw new BTreeInvariantError(
1810
+ "Duplicate user key detected in tree with uniqueness policy."
1811
+ );
1498
1812
  }
1499
1813
  }
1500
1814
  }
@@ -1502,12 +1816,7 @@ var validateLeafNodeOrdering = (state, node) => {
1502
1816
  const first = leafEntryAt(node, index - 2);
1503
1817
  const second = leafEntryAt(node, index - 1);
1504
1818
  const third = leafEntryAt(node, index);
1505
- assertTransitivityAsInvariant(
1506
- state,
1507
- first.key,
1508
- second.key,
1509
- third.key
1510
- );
1819
+ assertTransitivityAsInvariant(state, first.key, second.key, third.key);
1511
1820
  }
1512
1821
  if (count > state.maxLeafEntries) {
1513
1822
  throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
@@ -1516,9 +1825,14 @@ var validateLeafNodeOrdering = (state, node) => {
1516
1825
  var validateLeafNode = (state, node, depth) => {
1517
1826
  validateLeafNodeOrdering(state, node);
1518
1827
  const count = leafEntryCount(node);
1519
- const baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1828
+ let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
1829
+ if (state.deleteRebalancePolicy === "lazy") {
1830
+ baseMinLeaf = applyLazyThreshold(baseMinLeaf);
1831
+ }
1520
1832
  if (node !== state.root && count < baseMinLeaf) {
1521
- throw new BTreeInvariantError("Non-root leaf node violates minimum occupancy.");
1833
+ throw new BTreeInvariantError(
1834
+ "Non-root leaf node violates minimum occupancy."
1835
+ );
1522
1836
  }
1523
1837
  const first = count === 0 ? null : leafEntryAt(node, 0);
1524
1838
  const last = count === 0 ? null : leafEntryAt(node, count - 1);
@@ -1540,76 +1854,110 @@ var validateBranchStructure = (state, node) => {
1540
1854
  }
1541
1855
  const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
1542
1856
  if (node !== state.root && liveCount < baseMinBranch) {
1543
- throw new BTreeInvariantError("Non-root branch node violates minimum occupancy.");
1857
+ throw new BTreeInvariantError(
1858
+ "Non-root branch node violates minimum occupancy."
1859
+ );
1544
1860
  }
1545
1861
  if (liveCount > state.maxBranchChildren) {
1546
1862
  throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
1547
1863
  }
1548
1864
  if (node.keys.length !== node.children.length) {
1549
- throw new BTreeInvariantError("Branch keys array length does not match children array length.");
1865
+ throw new BTreeInvariantError(
1866
+ "Branch keys array length does not match children array length."
1867
+ );
1550
1868
  }
1551
1869
  };
1552
1870
  var validateBranchChild = (state, node, childIndex, depth) => {
1553
1871
  const child = node.children[childIndex];
1554
1872
  if (child.parent !== node) {
1555
- throw new BTreeInvariantError("Child-parent pointer mismatch in branch node.");
1873
+ throw new BTreeInvariantError(
1874
+ "Child-parent pointer mismatch in branch node."
1875
+ );
1556
1876
  }
1557
1877
  if (child.indexInParent !== childIndex) {
1558
- throw new BTreeInvariantError("Child indexInParent does not match actual position in parent.");
1878
+ throw new BTreeInvariantError(
1879
+ "Child indexInParent does not match actual position in parent."
1880
+ );
1559
1881
  }
1560
1882
  const childValidation = validateNode(state, child, depth + 1);
1561
1883
  if (childValidation.minKey === null || childValidation.maxKey === null) {
1562
- throw new BTreeInvariantError("Branch child must not be empty in non-root branch tree.");
1884
+ throw new BTreeInvariantError(
1885
+ "Branch child must not be empty in non-root branch tree."
1886
+ );
1563
1887
  }
1564
1888
  const cachedMinKey = node.keys[childIndex];
1565
1889
  const actualMinKey = nodeMinKey(child);
1566
- if (actualMinKey === null || compareNodeKeys(state.compareKeys, cachedMinKey.key, cachedMinKey.sequence, actualMinKey.key, actualMinKey.sequence) !== 0) {
1567
- throw new BTreeInvariantError("Branch cached key does not match actual child minimum key.");
1890
+ if (actualMinKey === null || compareNodeKeys(
1891
+ state.compareKeys,
1892
+ cachedMinKey.key,
1893
+ cachedMinKey.sequence,
1894
+ actualMinKey.key,
1895
+ actualMinKey.sequence
1896
+ ) !== 0) {
1897
+ throw new BTreeInvariantError(
1898
+ "Branch cached key does not match actual child minimum key."
1899
+ );
1568
1900
  }
1569
1901
  return childValidation;
1570
1902
  };
1903
+ var mergeChildValidation = (state, accumulated, childValidation) => {
1904
+ if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
1905
+ throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
1906
+ }
1907
+ if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
1908
+ accumulated.leafDepth = childValidation.leafDepth;
1909
+ }
1910
+ if (accumulated.previousChildMax !== null && compareNodeKeys(
1911
+ state.compareKeys,
1912
+ accumulated.previousChildMax.key,
1913
+ accumulated.previousChildMax.sequence,
1914
+ childValidation.minKey.key,
1915
+ childValidation.minKey.sequence
1916
+ ) >= 0) {
1917
+ throw new BTreeInvariantError(
1918
+ "Branch child key ranges are not strictly ordered."
1919
+ );
1920
+ }
1921
+ if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
1922
+ accumulated.maxKey = childValidation.maxKey;
1923
+ accumulated.previousChildMax = childValidation.maxKey;
1924
+ accumulated.leafCount += childValidation.leafCount;
1925
+ accumulated.branchCount += childValidation.branchCount;
1926
+ accumulated.entryCount += childValidation.entryCount;
1927
+ };
1571
1928
  var validateNode = (state, node, depth) => {
1572
1929
  if (isLeafNode(node)) {
1573
1930
  return validateLeafNode(state, node, depth);
1574
1931
  }
1575
1932
  validateBranchStructure(state, node);
1576
- let leafDepth = null;
1577
- let leafCount = 0;
1578
- let branchCount = 1;
1579
- let entryCount = 0;
1580
- let minKey = null;
1581
- let maxKey = null;
1582
- let previousChildMax = null;
1933
+ const acc = {
1934
+ leafDepth: null,
1935
+ leafCount: 0,
1936
+ branchCount: 1,
1937
+ entryCount: 0,
1938
+ minKey: null,
1939
+ maxKey: null,
1940
+ previousChildMax: null
1941
+ };
1583
1942
  for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
1584
1943
  const childValidation = validateBranchChild(state, node, childIndex, depth);
1585
- if (leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== leafDepth) {
1586
- throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
1587
- }
1588
- if (leafDepth === null && childValidation.leafDepth !== null) {
1589
- leafDepth = childValidation.leafDepth;
1590
- }
1591
- if (previousChildMax !== null && compareNodeKeys(
1592
- state.compareKeys,
1593
- previousChildMax.key,
1594
- previousChildMax.sequence,
1595
- childValidation.minKey.key,
1596
- childValidation.minKey.sequence
1597
- ) >= 0) {
1598
- throw new BTreeInvariantError("Branch child key ranges are not strictly ordered.");
1599
- }
1600
- if (minKey === null) minKey = childValidation.minKey;
1601
- maxKey = childValidation.maxKey;
1602
- previousChildMax = childValidation.maxKey;
1603
- leafCount += childValidation.leafCount;
1604
- branchCount += childValidation.branchCount;
1605
- entryCount += childValidation.entryCount;
1944
+ mergeChildValidation(state, acc, childValidation);
1606
1945
  }
1607
- return { minKey, maxKey, leafDepth, leafCount, branchCount, entryCount };
1946
+ return {
1947
+ minKey: acc.minKey,
1948
+ maxKey: acc.maxKey,
1949
+ leafDepth: acc.leafDepth,
1950
+ leafCount: acc.leafCount,
1951
+ branchCount: acc.branchCount,
1952
+ entryCount: acc.entryCount
1953
+ };
1608
1954
  };
1609
1955
  var assertInvariants = (state) => {
1610
1956
  const validation = validateNode(state, state.root, 0);
1611
1957
  if (validation.entryCount !== state.entryCount) {
1612
- throw new BTreeInvariantError("Index entry count mismatch between tree traversal and tracked state.");
1958
+ throw new BTreeInvariantError(
1959
+ "Index entry count mismatch between tree traversal and tracked state."
1960
+ );
1613
1961
  }
1614
1962
  validateLeafLinks(state, validation.leafCount);
1615
1963
  };
@@ -1663,44 +2011,56 @@ var InMemoryBTree = class _InMemoryBTree {
1663
2011
  return putManyEntries(this.state, entries);
1664
2012
  }
1665
2013
  remove(key) {
1666
- return removeFirstMatchingEntry(this.state, key);
2014
+ const entry = removeFirstMatchingEntry(this.state, key);
2015
+ if (entry === null) return null;
2016
+ return freezeEntry(entry);
1667
2017
  }
1668
2018
  removeById(entryId) {
1669
2019
  if (this.state.entryKeys === null) {
1670
2020
  throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
1671
2021
  }
1672
- return removeEntryById(this.state, entryId);
2022
+ const entry = removeEntryById(this.state, entryId);
2023
+ if (entry === null) return null;
2024
+ return freezeEntry(entry);
1673
2025
  }
1674
2026
  peekById(entryId) {
1675
2027
  if (this.state.entryKeys === null) {
1676
2028
  throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
1677
2029
  }
1678
- return peekEntryById(this.state, entryId);
2030
+ const entry = peekEntryById(this.state, entryId);
2031
+ if (entry === null) return null;
2032
+ return freezeEntry(entry);
1679
2033
  }
1680
2034
  updateById(entryId, value) {
1681
2035
  if (this.state.entryKeys === null) {
1682
2036
  throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
1683
2037
  }
1684
- return updateEntryById(this.state, entryId, value);
2038
+ const entry = updateEntryById(this.state, entryId, value);
2039
+ if (entry === null) return null;
2040
+ return freezeEntry(entry);
1685
2041
  }
1686
2042
  popFirst() {
1687
- return popFirstEntry(this.state);
2043
+ const entry = popFirstEntry(this.state);
2044
+ if (entry === null) return null;
2045
+ return freezeEntry(entry);
1688
2046
  }
1689
2047
  peekFirst() {
1690
2048
  if (this.state.entryCount === 0) {
1691
2049
  return null;
1692
2050
  }
1693
- return leafEntryAt(this.state.leftmostLeaf, 0);
2051
+ return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
1694
2052
  }
1695
2053
  peekLast() {
1696
2054
  if (this.state.entryCount === 0) {
1697
2055
  return null;
1698
2056
  }
1699
2057
  const leaf = this.state.rightmostLeaf;
1700
- return leafEntryAt(leaf, leafEntryCount(leaf) - 1);
2058
+ return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
1701
2059
  }
1702
2060
  popLast() {
1703
- return popLastEntry(this.state);
2061
+ const entry = popLastEntry(this.state);
2062
+ if (entry === null) return null;
2063
+ return freezeEntry(entry);
1704
2064
  }
1705
2065
  clear() {
1706
2066
  const emptyLeaf = createLeafNode([], null);
@@ -1710,16 +2070,9 @@ var InMemoryBTree = class _InMemoryBTree {
1710
2070
  this.state.entryCount = 0;
1711
2071
  this.state._cursor.leaf = emptyLeaf;
1712
2072
  this.state._cursor.index = 0;
1713
- if (this.state.entryKeys !== null) {
1714
- this.state.entryKeys.clear();
1715
- }
2073
+ this.state.entryKeys?.clear();
1716
2074
  if (this.state.autoScale) {
1717
- const tier = computeAutoScaleTier(0);
1718
- this.state.maxLeafEntries = tier.maxLeaf;
1719
- this.state.maxBranchChildren = tier.maxBranch;
1720
- this.state.minLeafEntries = Math.ceil(tier.maxLeaf / 2);
1721
- this.state.minBranchChildren = Math.ceil(tier.maxBranch / 2);
1722
- this.state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
2075
+ resetAutoScaleToTier0(this.state);
1723
2076
  }
1724
2077
  }
1725
2078
  get(key) {
@@ -1733,12 +2086,12 @@ var InMemoryBTree = class _InMemoryBTree {
1733
2086
  findFirst(key) {
1734
2087
  const found = findFirstMatchingUserKey(this.state, key);
1735
2088
  if (found === null) return null;
1736
- return leafEntryAt(found.leaf, found.index);
2089
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1737
2090
  }
1738
2091
  findLast(key) {
1739
2092
  const found = findLastMatchingUserKey(this.state, key);
1740
2093
  if (found === null) return null;
1741
- return leafEntryAt(found.leaf, found.index);
2094
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1742
2095
  }
1743
2096
  nextHigherKey(key) {
1744
2097
  return findNextHigherKey(this.state, key);
@@ -1749,7 +2102,7 @@ var InMemoryBTree = class _InMemoryBTree {
1749
2102
  getPairOrNextLower(key) {
1750
2103
  const found = findPairOrNextLower(this.state, key);
1751
2104
  if (found === null) return null;
1752
- return leafEntryAt(found.leaf, found.index);
2105
+ return freezeEntry(leafEntryAt(found.leaf, found.index));
1753
2106
  }
1754
2107
  count(startKey, endKey, options) {
1755
2108
  return countRangeEntries(this.state, startKey, endKey, options);
@@ -1758,14 +2111,14 @@ var InMemoryBTree = class _InMemoryBTree {
1758
2111
  return deleteRangeEntries(this.state, startKey, endKey, options);
1759
2112
  }
1760
2113
  range(startKey, endKey, options) {
1761
- return rangeQueryEntries(this.state, startKey, endKey, options);
2114
+ return rangeQueryPublicEntries(this.state, startKey, endKey, options);
1762
2115
  }
1763
2116
  *entries() {
1764
2117
  let leaf = this.state.leftmostLeaf;
1765
2118
  while (leaf !== null) {
1766
2119
  const count = leafEntryCount(leaf);
1767
2120
  for (let i = 0; i < count; i += 1) {
1768
- yield leafEntryAt(leaf, i);
2121
+ yield freezeEntry(leafEntryAt(leaf, i));
1769
2122
  }
1770
2123
  leaf = leaf.next;
1771
2124
  }
@@ -1775,7 +2128,7 @@ var InMemoryBTree = class _InMemoryBTree {
1775
2128
  while (leaf !== null) {
1776
2129
  const count = leafEntryCount(leaf);
1777
2130
  for (let i = count - 1; i >= 0; i -= 1) {
1778
- yield leafEntryAt(leaf, i);
2131
+ yield freezeEntry(leafEntryAt(leaf, i));
1779
2132
  }
1780
2133
  leaf = leaf.prev;
1781
2134
  }
@@ -1793,48 +2146,26 @@ var InMemoryBTree = class _InMemoryBTree {
1793
2146
  [Symbol.iterator]() {
1794
2147
  return this.entries();
1795
2148
  }
2149
+ forEachRange(startKey, endKey, callback, options) {
2150
+ forEachRangeEntries(this.state, startKey, endKey, callback, options);
2151
+ }
1796
2152
  forEach(callback, thisArg) {
1797
- let leaf = this.state.leftmostLeaf;
1798
- while (leaf !== null) {
1799
- const count = leafEntryCount(leaf);
1800
- for (let i = 0; i < count; i += 1) {
1801
- callback.call(thisArg, leafEntryAt(leaf, i));
1802
- }
1803
- leaf = leaf.next;
1804
- }
2153
+ forEachEntry(this.state, callback, thisArg);
1805
2154
  }
1806
2155
  snapshot() {
1807
- const result = new Array(this.state.entryCount);
1808
- let leaf = this.state.leftmostLeaf;
1809
- let writeIdx = 0;
1810
- while (leaf !== null) {
1811
- const count = leafEntryCount(leaf);
1812
- for (let i = 0; i < count; i += 1) {
1813
- result[writeIdx++] = leafEntryAt(leaf, i);
1814
- }
1815
- leaf = leaf.next;
1816
- }
1817
- return result;
2156
+ return snapshotEntries(this.state);
1818
2157
  }
1819
2158
  clone() {
1820
- const cloned = new _InMemoryBTree(buildConfigFromState(this.state));
2159
+ const cloned = new _InMemoryBTree(
2160
+ buildConfigFromState(this.state)
2161
+ );
1821
2162
  applyAutoScaleCapacitySnapshot(
1822
2163
  cloned.state,
1823
2164
  this.state.maxLeafEntries,
1824
2165
  this.state.maxBranchChildren
1825
2166
  );
1826
2167
  if (this.state.entryCount > 0) {
1827
- const pairs = new Array(this.state.entryCount);
1828
- let leaf = this.state.leftmostLeaf;
1829
- let writeIdx = 0;
1830
- while (leaf !== null) {
1831
- const count = leafEntryCount(leaf);
1832
- for (let i = 0; i < count; i += 1) {
1833
- pairs[writeIdx++] = leafEntryAt(leaf, i);
1834
- }
1835
- leaf = leaf.next;
1836
- }
1837
- cloned.putMany(pairs);
2168
+ cloned.putMany(collectInternalEntries(this.state));
1838
2169
  }
1839
2170
  return cloned;
1840
2171
  }
@@ -1843,26 +2174,19 @@ var InMemoryBTree = class _InMemoryBTree {
1843
2174
  }
1844
2175
  static fromJSON(json, compareKeys) {
1845
2176
  validateBTreeJSON(json);
1846
- const strict = json.config.duplicateKeys !== "allow";
1847
- for (let i = 1; i < json.entries.length; i += 1) {
1848
- const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
1849
- if (cmp > 0) {
1850
- throw new BTreeValidationError("fromJSON: entries not sorted.");
1851
- }
1852
- if (strict && cmp === 0) {
1853
- throw new BTreeValidationError(
1854
- 'fromJSON: duplicate keys require duplicateKeys "allow".'
1855
- );
1856
- }
1857
- }
1858
- const tree = new _InMemoryBTree(buildConfigFromJSON(json, compareKeys));
2177
+ validateBTreeJSONSortOrder(json, compareKeys);
2178
+ const tree = new _InMemoryBTree(
2179
+ buildConfigFromJSON(json, compareKeys)
2180
+ );
1859
2181
  applyAutoScaleCapacitySnapshot(
1860
2182
  tree.state,
1861
2183
  json.config.maxLeafEntries,
1862
2184
  json.config.maxBranchChildren
1863
2185
  );
1864
2186
  if (json.entries.length > 0) {
1865
- const pairs = new Array(json.entries.length);
2187
+ const pairs = new Array(
2188
+ json.entries.length
2189
+ );
1866
2190
  for (let i = 0; i < json.entries.length; i += 1) {
1867
2191
  pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
1868
2192
  }