@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.
- package/README-JA.md +117 -64
- package/README.md +117 -64
- package/dist/InMemoryBTree.d.ts +3 -3
- package/dist/btree/autoScale.d.ts +2 -0
- package/dist/btree/entry-lookup.d.ts +8 -0
- package/dist/btree/mutations.d.ts +1 -3
- package/dist/btree/node-ops.d.ts +15 -0
- package/dist/btree/rangeQuery.d.ts +5 -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 +23 -26
- package/dist/{chunk-CZFRT2NN.js → chunk-UGGWGP4E.js} +823 -499
- package/dist/concurrency/ConcurrentInMemoryBTree.d.ts +22 -29
- package/dist/concurrency/coordinator.d.ts +21 -0
- package/dist/concurrency/helpers.d.ts +8 -2
- package/dist/concurrency/syncLogValidation.d.ts +2 -0
- package/dist/concurrency/types.d.ts +14 -1
- package/dist/concurrency/writeOps.d.ts +48 -0
- package/dist/core.cjs +823 -499
- package/dist/core.d.ts +2 -2
- package/dist/core.js +1 -1
- package/dist/errors.d.ts +3 -0
- package/dist/frostpillar-btree-core.min.js +1 -1
- package/dist/frostpillar-btree.min.js +1 -1
- package/dist/index.cjs +1212 -670
- package/dist/index.d.ts +1 -1
- package/dist/index.js +397 -179
- package/package.json +1 -1
package/dist/core.cjs
CHANGED
|
@@ -42,54 +42,10 @@ var BTreeInvariantError = class extends Error {
|
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
44
|
|
|
45
|
-
// src/btree/
|
|
46
|
-
var DEFAULT_MAX_LEAF_ENTRIES = 64;
|
|
47
|
-
var DEFAULT_MAX_BRANCH_CHILDREN = 64;
|
|
48
|
-
var MIN_NODE_CAPACITY = 3;
|
|
49
|
-
var MAX_NODE_CAPACITY = 16384;
|
|
50
|
-
var NODE_LEAF = 0;
|
|
51
|
-
var NODE_BRANCH = 1;
|
|
52
|
-
var normalizeDuplicateKeyPolicy = (value) => {
|
|
53
|
-
if (value === void 0) {
|
|
54
|
-
return "replace";
|
|
55
|
-
}
|
|
56
|
-
if (value !== "allow" && value !== "reject" && value !== "replace") {
|
|
57
|
-
throw new BTreeValidationError(
|
|
58
|
-
`Invalid duplicateKeys option.`
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
return value;
|
|
62
|
-
};
|
|
63
|
-
var isLeafNode = (node) => {
|
|
64
|
-
return node.kind === NODE_LEAF;
|
|
65
|
-
};
|
|
66
|
-
var writeMinKeyTo = (node, target) => {
|
|
67
|
-
if (node.kind === NODE_LEAF) {
|
|
68
|
-
if (node.entryOffset >= node.entries.length) return false;
|
|
69
|
-
const e = node.entries[node.entryOffset];
|
|
70
|
-
target.key = e.key;
|
|
71
|
-
target.sequence = e.entryId;
|
|
72
|
-
return true;
|
|
73
|
-
}
|
|
74
|
-
if (node.childOffset >= node.keys.length) return false;
|
|
75
|
-
target.key = node.keys[node.childOffset].key;
|
|
76
|
-
target.sequence = node.keys[node.childOffset].sequence;
|
|
77
|
-
return true;
|
|
78
|
-
};
|
|
79
|
-
var normalizeNodeCapacity = (value, field, defaultValue) => {
|
|
80
|
-
if (value === void 0) {
|
|
81
|
-
return defaultValue;
|
|
82
|
-
}
|
|
83
|
-
if (!Number.isInteger(value) || value < MIN_NODE_CAPACITY || value > MAX_NODE_CAPACITY) {
|
|
84
|
-
throw new BTreeValidationError(
|
|
85
|
-
`${field}: integer ${MIN_NODE_CAPACITY}\u2013${MAX_NODE_CAPACITY} required.`
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
return value;
|
|
89
|
-
};
|
|
45
|
+
// src/btree/node-ops.ts
|
|
90
46
|
var createLeafNode = (entries, parent) => {
|
|
91
47
|
return {
|
|
92
|
-
kind:
|
|
48
|
+
kind: 0,
|
|
93
49
|
entries,
|
|
94
50
|
entryOffset: 0,
|
|
95
51
|
parent,
|
|
@@ -101,7 +57,7 @@ var createLeafNode = (entries, parent) => {
|
|
|
101
57
|
var createBranchNode = (children, parent) => {
|
|
102
58
|
const keys = [];
|
|
103
59
|
const branch = {
|
|
104
|
-
kind:
|
|
60
|
+
kind: 1,
|
|
105
61
|
children,
|
|
106
62
|
keys,
|
|
107
63
|
childOffset: 0,
|
|
@@ -112,11 +68,12 @@ var createBranchNode = (children, parent) => {
|
|
|
112
68
|
const child = children[i];
|
|
113
69
|
child.parent = branch;
|
|
114
70
|
child.indexInParent = i;
|
|
115
|
-
const target = {
|
|
71
|
+
const target = {
|
|
72
|
+
key: void 0,
|
|
73
|
+
sequence: 0
|
|
74
|
+
};
|
|
116
75
|
if (!writeMinKeyTo(child, target)) {
|
|
117
|
-
throw new BTreeInvariantError(
|
|
118
|
-
"branch child has no min key"
|
|
119
|
-
);
|
|
76
|
+
throw new BTreeInvariantError("branch child has no min key");
|
|
120
77
|
}
|
|
121
78
|
keys.push(target);
|
|
122
79
|
}
|
|
@@ -170,7 +127,14 @@ var leafInsertAt = (leaf, logicalIndex, entry) => {
|
|
|
170
127
|
leaf.entryOffset -= 1;
|
|
171
128
|
leaf.entries[phys - 1] = entry;
|
|
172
129
|
} else {
|
|
173
|
-
leaf.entries.
|
|
130
|
+
const len = leaf.entries.length;
|
|
131
|
+
if (phys >= len) {
|
|
132
|
+
leaf.entries.push(entry);
|
|
133
|
+
} else {
|
|
134
|
+
leaf.entries.push(leaf.entries[len - 1]);
|
|
135
|
+
leaf.entries.copyWithin(phys + 1, phys, len);
|
|
136
|
+
leaf.entries[phys] = entry;
|
|
137
|
+
}
|
|
174
138
|
}
|
|
175
139
|
};
|
|
176
140
|
var leafCompact = (leaf) => {
|
|
@@ -199,7 +163,11 @@ var branchInsertAt = (branch, logicalIndex, child, key) => {
|
|
|
199
163
|
const phys = branch.childOffset + logicalIndex;
|
|
200
164
|
const count = branch.children.length - branch.childOffset;
|
|
201
165
|
if (branch.childOffset > 0 && logicalIndex < count >>> 1) {
|
|
202
|
-
branch.children.copyWithin(
|
|
166
|
+
branch.children.copyWithin(
|
|
167
|
+
branch.childOffset - 1,
|
|
168
|
+
branch.childOffset,
|
|
169
|
+
phys
|
|
170
|
+
);
|
|
203
171
|
branch.keys.copyWithin(branch.childOffset - 1, branch.childOffset, phys);
|
|
204
172
|
branch.childOffset -= 1;
|
|
205
173
|
branch.children[phys - 1] = child;
|
|
@@ -220,8 +188,16 @@ var branchRemoveAt = (branch, physIndex) => {
|
|
|
220
188
|
const logicalIndex = physIndex - branch.childOffset;
|
|
221
189
|
const count = branch.children.length - branch.childOffset;
|
|
222
190
|
if (logicalIndex < count - 1 - logicalIndex) {
|
|
223
|
-
branch.children.copyWithin(
|
|
224
|
-
|
|
191
|
+
branch.children.copyWithin(
|
|
192
|
+
branch.childOffset + 1,
|
|
193
|
+
branch.childOffset,
|
|
194
|
+
physIndex
|
|
195
|
+
);
|
|
196
|
+
branch.keys.copyWithin(
|
|
197
|
+
branch.childOffset + 1,
|
|
198
|
+
branch.childOffset,
|
|
199
|
+
physIndex
|
|
200
|
+
);
|
|
225
201
|
branch.childOffset += 1;
|
|
226
202
|
for (let i = branch.childOffset; i <= physIndex; i += 1) {
|
|
227
203
|
branch.children[i].indexInParent = i;
|
|
@@ -240,6 +216,60 @@ var branchRemoveAt = (branch, physIndex) => {
|
|
|
240
216
|
}
|
|
241
217
|
};
|
|
242
218
|
|
|
219
|
+
// src/btree/types.ts
|
|
220
|
+
var DEFAULT_MAX_LEAF_ENTRIES = 64;
|
|
221
|
+
var DEFAULT_MAX_BRANCH_CHILDREN = 64;
|
|
222
|
+
var MIN_NODE_CAPACITY = 3;
|
|
223
|
+
var MAX_NODE_CAPACITY = 16384;
|
|
224
|
+
var NODE_LEAF = 0;
|
|
225
|
+
var normalizeDuplicateKeyPolicy = (value) => {
|
|
226
|
+
if (value === void 0) {
|
|
227
|
+
return "replace";
|
|
228
|
+
}
|
|
229
|
+
if (value !== "allow" && value !== "reject" && value !== "replace") {
|
|
230
|
+
throw new BTreeValidationError(`Invalid duplicateKeys option.`);
|
|
231
|
+
}
|
|
232
|
+
return value;
|
|
233
|
+
};
|
|
234
|
+
var normalizeDeleteRebalancePolicy = (value) => {
|
|
235
|
+
if (value === void 0) {
|
|
236
|
+
return "standard";
|
|
237
|
+
}
|
|
238
|
+
if (value !== "standard" && value !== "lazy") {
|
|
239
|
+
throw new BTreeValidationError(`Invalid deleteRebalancePolicy option.`);
|
|
240
|
+
}
|
|
241
|
+
return value;
|
|
242
|
+
};
|
|
243
|
+
var freezeEntry = (entry) => Object.freeze(entry);
|
|
244
|
+
var createEntry = (key, entryId, value) => Object.freeze({ key, entryId, value });
|
|
245
|
+
var isLeafNode = (node) => {
|
|
246
|
+
return node.kind === NODE_LEAF;
|
|
247
|
+
};
|
|
248
|
+
var writeMinKeyTo = (node, target) => {
|
|
249
|
+
if (node.kind === NODE_LEAF) {
|
|
250
|
+
if (node.entryOffset >= node.entries.length) return false;
|
|
251
|
+
const e = node.entries[node.entryOffset];
|
|
252
|
+
target.key = e.key;
|
|
253
|
+
target.sequence = e.entryId;
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
if (node.childOffset >= node.keys.length) return false;
|
|
257
|
+
target.key = node.keys[node.childOffset].key;
|
|
258
|
+
target.sequence = node.keys[node.childOffset].sequence;
|
|
259
|
+
return true;
|
|
260
|
+
};
|
|
261
|
+
var normalizeNodeCapacity = (value, field, defaultValue) => {
|
|
262
|
+
if (value === void 0) {
|
|
263
|
+
return defaultValue;
|
|
264
|
+
}
|
|
265
|
+
if (!Number.isInteger(value) || value < MIN_NODE_CAPACITY || value > MAX_NODE_CAPACITY) {
|
|
266
|
+
throw new BTreeValidationError(
|
|
267
|
+
`${field}: integer ${MIN_NODE_CAPACITY}\u2013${MAX_NODE_CAPACITY} required.`
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
return value;
|
|
271
|
+
};
|
|
272
|
+
|
|
243
273
|
// src/btree/navigation.ts
|
|
244
274
|
var selectBranchChild = (compare, branch, userKey, sequence) => {
|
|
245
275
|
const off = branch.childOffset;
|
|
@@ -253,7 +283,7 @@ var selectBranchChild = (compare, branch, userKey, sequence) => {
|
|
|
253
283
|
const mid = lower + upper >>> 1;
|
|
254
284
|
const k = branch.keys[mid];
|
|
255
285
|
const cmp = compare(k.key, userKey);
|
|
256
|
-
if ((cmp !== 0 ? cmp : k.sequence - sequence) <= 0) {
|
|
286
|
+
if ((cmp !== 0 ? cmp : k.sequence < sequence ? -1 : k.sequence > sequence ? 1 : 0) <= 0) {
|
|
257
287
|
selectedIndex = mid;
|
|
258
288
|
lower = mid + 1;
|
|
259
289
|
} else {
|
|
@@ -278,7 +308,7 @@ var lowerBoundInLeaf = (state, leaf, userKey, sequence) => {
|
|
|
278
308
|
const mid = lower + upper >>> 1;
|
|
279
309
|
const e = leaf.entries[mid];
|
|
280
310
|
const cmp = compare(e.key, userKey);
|
|
281
|
-
if ((cmp !== 0 ? cmp : e.entryId - sequence) < 0) {
|
|
311
|
+
if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) < 0) {
|
|
282
312
|
lower = mid + 1;
|
|
283
313
|
} else {
|
|
284
314
|
upper = mid;
|
|
@@ -294,7 +324,7 @@ var upperBoundInLeaf = (state, leaf, userKey, sequence) => {
|
|
|
294
324
|
const mid = lower + upper >>> 1;
|
|
295
325
|
const e = leaf.entries[mid];
|
|
296
326
|
const cmp = compare(e.key, userKey);
|
|
297
|
-
if ((cmp !== 0 ? cmp : e.entryId - sequence) <= 0) {
|
|
327
|
+
if ((cmp !== 0 ? cmp : e.entryId < sequence ? -1 : e.entryId > sequence ? 1 : 0) <= 0) {
|
|
298
328
|
lower = mid + 1;
|
|
299
329
|
} else {
|
|
300
330
|
upper = mid;
|
|
@@ -361,7 +391,11 @@ var hasKeyEntry = (state, key) => {
|
|
|
361
391
|
var findNextHigherKey = (state, key) => {
|
|
362
392
|
if (state.entryCount === 0) return null;
|
|
363
393
|
const compare = state.compareKeys;
|
|
364
|
-
let leaf = findLeafForKey(
|
|
394
|
+
let leaf = findLeafForKey(
|
|
395
|
+
state,
|
|
396
|
+
key,
|
|
397
|
+
Number.MAX_SAFE_INTEGER
|
|
398
|
+
);
|
|
365
399
|
let idx = upperBoundInLeaf(state, leaf, key, Number.MAX_SAFE_INTEGER);
|
|
366
400
|
while (leaf !== null) {
|
|
367
401
|
if (idx < leafEntryCount(leaf)) {
|
|
@@ -431,7 +465,175 @@ var findPairOrNextLower = (state, key) => {
|
|
|
431
465
|
return null;
|
|
432
466
|
};
|
|
433
467
|
|
|
434
|
-
// src/btree/
|
|
468
|
+
// src/btree/rangeQuery.ts
|
|
469
|
+
function isEmptyRange(compare, startKey, endKey, options) {
|
|
470
|
+
const cmp = compare(startKey, endKey);
|
|
471
|
+
if (cmp > 0) return true;
|
|
472
|
+
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
473
|
+
const upperExclusive = options?.upperBound === "exclusive";
|
|
474
|
+
return lowerExclusive && upperExclusive && cmp === 0;
|
|
475
|
+
}
|
|
476
|
+
var initRangeCursor = (state, startKey, endKey, options) => {
|
|
477
|
+
if (state.entryCount === 0) return null;
|
|
478
|
+
const compare = state.compareKeys;
|
|
479
|
+
if (isEmptyRange(compare, startKey, endKey, options)) return null;
|
|
480
|
+
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
481
|
+
const upperExclusive = options?.upperBound === "exclusive";
|
|
482
|
+
const startSeq = lowerExclusive ? Number.MAX_SAFE_INTEGER : 0;
|
|
483
|
+
const leaf = findLeafForKey(state, startKey, startSeq);
|
|
484
|
+
const index = lowerExclusive ? upperBoundInLeaf(state, leaf, startKey, Number.MAX_SAFE_INTEGER) : lowerBoundInLeaf(state, leaf, startKey, 0);
|
|
485
|
+
return { leaf, index, compare, upperExclusive };
|
|
486
|
+
};
|
|
487
|
+
var countRangeEntries = (state, startKey, endKey, options) => {
|
|
488
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
489
|
+
if (cursor === null) return 0;
|
|
490
|
+
let cursorLeaf = cursor.leaf;
|
|
491
|
+
let cursorIndex = cursor.index;
|
|
492
|
+
const { compare, upperExclusive } = cursor;
|
|
493
|
+
let count = 0;
|
|
494
|
+
while (cursorLeaf !== null) {
|
|
495
|
+
const leafCount = leafEntryCount(cursorLeaf);
|
|
496
|
+
if (cursorIndex >= leafCount) {
|
|
497
|
+
cursorLeaf = cursorLeaf.next;
|
|
498
|
+
cursorIndex = 0;
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
502
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
503
|
+
count += leafCount - cursorIndex;
|
|
504
|
+
cursorLeaf = cursorLeaf.next;
|
|
505
|
+
cursorIndex = 0;
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
count += findBoundaryEnd(state, cursorLeaf, endKey, upperExclusive, leafCount) - cursorIndex;
|
|
509
|
+
return count;
|
|
510
|
+
}
|
|
511
|
+
return count;
|
|
512
|
+
};
|
|
513
|
+
var isLastEntryInRange = (lastKey, endKey, compare, upperExclusive) => {
|
|
514
|
+
const cmp = compare(lastKey, endKey);
|
|
515
|
+
return upperExclusive ? cmp < 0 : cmp <= 0;
|
|
516
|
+
};
|
|
517
|
+
var findBoundaryEnd = (state, leaf, endKey, upperExclusive, leafCount) => {
|
|
518
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
519
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
|
|
520
|
+
return endBound < leafCount ? endBound : leafCount;
|
|
521
|
+
};
|
|
522
|
+
var RANGE_PREALLOC_THRESHOLD = 200;
|
|
523
|
+
var allocateRangeOutput = (state, cursor, startKey, endKey, options) => {
|
|
524
|
+
const firstLeafCount = leafEntryCount(cursor.leaf);
|
|
525
|
+
const firstLeafRemainder = firstLeafCount - cursor.index;
|
|
526
|
+
if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursor.leaf.next !== null) {
|
|
527
|
+
const lastKey = leafEntryAt(cursor.leaf, firstLeafCount - 1).key;
|
|
528
|
+
if (isLastEntryInRange(lastKey, endKey, cursor.compare, cursor.upperExclusive)) {
|
|
529
|
+
return new Array(
|
|
530
|
+
countRangeEntries(state, startKey, endKey, options)
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return [];
|
|
535
|
+
};
|
|
536
|
+
var appendLeafSlicePublic = (leaf, from, to, output, useIndexed, writeIdx) => {
|
|
537
|
+
if (useIndexed) {
|
|
538
|
+
for (let i = from; i < to; i += 1) {
|
|
539
|
+
output[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
|
|
540
|
+
}
|
|
541
|
+
} else {
|
|
542
|
+
for (let i = from; i < to; i += 1) {
|
|
543
|
+
output.push(freezeEntry(leafEntryAt(leaf, i)));
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return writeIdx;
|
|
547
|
+
};
|
|
548
|
+
var collectPublicEntries = (state, cursor, endKey, output) => {
|
|
549
|
+
const { compare, upperExclusive } = cursor;
|
|
550
|
+
let cursorLeaf = cursor.leaf;
|
|
551
|
+
let cursorIndex = cursor.index;
|
|
552
|
+
let writeIdx = 0;
|
|
553
|
+
const useIndexed = output.length > 0;
|
|
554
|
+
while (cursorLeaf !== null) {
|
|
555
|
+
const leafCount = leafEntryCount(cursorLeaf);
|
|
556
|
+
if (cursorIndex >= leafCount) {
|
|
557
|
+
cursorLeaf = cursorLeaf.next;
|
|
558
|
+
cursorIndex = 0;
|
|
559
|
+
continue;
|
|
560
|
+
}
|
|
561
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
562
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
563
|
+
writeIdx = appendLeafSlicePublic(
|
|
564
|
+
cursorLeaf,
|
|
565
|
+
cursorIndex,
|
|
566
|
+
leafCount,
|
|
567
|
+
output,
|
|
568
|
+
useIndexed,
|
|
569
|
+
writeIdx
|
|
570
|
+
);
|
|
571
|
+
cursorLeaf = cursorLeaf.next;
|
|
572
|
+
cursorIndex = 0;
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
const limit = findBoundaryEnd(
|
|
576
|
+
state,
|
|
577
|
+
cursorLeaf,
|
|
578
|
+
endKey,
|
|
579
|
+
upperExclusive,
|
|
580
|
+
leafCount
|
|
581
|
+
);
|
|
582
|
+
appendLeafSlicePublic(
|
|
583
|
+
cursorLeaf,
|
|
584
|
+
cursorIndex,
|
|
585
|
+
limit,
|
|
586
|
+
output,
|
|
587
|
+
useIndexed,
|
|
588
|
+
writeIdx
|
|
589
|
+
);
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
var rangeQueryPublicEntries = (state, startKey, endKey, options) => {
|
|
594
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
595
|
+
if (cursor === null) return [];
|
|
596
|
+
const output = allocateRangeOutput(state, cursor, startKey, endKey, options);
|
|
597
|
+
collectPublicEntries(state, cursor, endKey, output);
|
|
598
|
+
return output;
|
|
599
|
+
};
|
|
600
|
+
var forEachRangeEntries = (state, startKey, endKey, callback, options) => {
|
|
601
|
+
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
602
|
+
if (cursor === null) return;
|
|
603
|
+
let cursorLeaf = cursor.leaf;
|
|
604
|
+
let cursorIndex = cursor.index;
|
|
605
|
+
const { compare, upperExclusive } = cursor;
|
|
606
|
+
while (cursorLeaf !== null) {
|
|
607
|
+
const leafCount = leafEntryCount(cursorLeaf);
|
|
608
|
+
if (cursorIndex >= leafCount) {
|
|
609
|
+
cursorLeaf = cursorLeaf.next;
|
|
610
|
+
cursorIndex = 0;
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
const lastKey = leafEntryAt(cursorLeaf, leafCount - 1).key;
|
|
614
|
+
if (isLastEntryInRange(lastKey, endKey, compare, upperExclusive)) {
|
|
615
|
+
for (let i = cursorIndex; i < leafCount; i += 1) {
|
|
616
|
+
callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
|
|
617
|
+
}
|
|
618
|
+
cursorLeaf = cursorLeaf.next;
|
|
619
|
+
cursorIndex = 0;
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
const limit = findBoundaryEnd(
|
|
623
|
+
state,
|
|
624
|
+
cursorLeaf,
|
|
625
|
+
endKey,
|
|
626
|
+
upperExclusive,
|
|
627
|
+
leafCount
|
|
628
|
+
);
|
|
629
|
+
for (let i = cursorIndex; i < limit; i += 1) {
|
|
630
|
+
callback(freezeEntry(leafEntryAt(cursorLeaf, i)));
|
|
631
|
+
}
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
// src/btree/rebalance-branch.ts
|
|
435
637
|
var updateMinKeyInAncestors = (node) => {
|
|
436
638
|
let current = node;
|
|
437
639
|
while (current.parent !== null) {
|
|
@@ -447,12 +649,9 @@ var requireParent = (node) => {
|
|
|
447
649
|
}
|
|
448
650
|
return node.parent;
|
|
449
651
|
};
|
|
450
|
-
var requireLeafNode = (node) => {
|
|
451
|
-
if (!isLeafNode(node)) throw new BTreeInvariantError("expected leaf, got branch");
|
|
452
|
-
return node;
|
|
453
|
-
};
|
|
454
652
|
var requireBranchNode = (node) => {
|
|
455
|
-
if (isLeafNode(node))
|
|
653
|
+
if (isLeafNode(node))
|
|
654
|
+
throw new BTreeInvariantError("expected branch, got leaf");
|
|
456
655
|
return node;
|
|
457
656
|
};
|
|
458
657
|
var removeChildFromBranch = (branch, childIndex) => {
|
|
@@ -461,27 +660,18 @@ var removeChildFromBranch = (branch, childIndex) => {
|
|
|
461
660
|
}
|
|
462
661
|
branchRemoveAt(branch, childIndex);
|
|
463
662
|
};
|
|
464
|
-
var detachLeafFromChain = (state, leaf) => {
|
|
465
|
-
if (leaf.prev !== null) {
|
|
466
|
-
leaf.prev.next = leaf.next;
|
|
467
|
-
} else if (leaf.next !== null) {
|
|
468
|
-
state.leftmostLeaf = leaf.next;
|
|
469
|
-
}
|
|
470
|
-
if (leaf.next !== null) {
|
|
471
|
-
leaf.next.prev = leaf.prev;
|
|
472
|
-
} else if (leaf.prev !== null) {
|
|
473
|
-
state.rightmostLeaf = leaf.prev;
|
|
474
|
-
}
|
|
475
|
-
leaf.prev = null;
|
|
476
|
-
leaf.next = null;
|
|
477
|
-
};
|
|
478
663
|
var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
|
|
479
664
|
const borrowedChild = leftSibling.children.pop();
|
|
480
|
-
if (borrowedChild === void 0)
|
|
665
|
+
if (borrowedChild === void 0)
|
|
666
|
+
throw new BTreeInvariantError("left branch borrow failed");
|
|
481
667
|
leftSibling.keys.pop();
|
|
482
668
|
borrowedChild.parent = branch;
|
|
483
|
-
const borrowedMinKey = {
|
|
484
|
-
|
|
669
|
+
const borrowedMinKey = {
|
|
670
|
+
key: void 0,
|
|
671
|
+
sequence: 0
|
|
672
|
+
};
|
|
673
|
+
if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
|
|
674
|
+
throw new BTreeInvariantError("borrowed child has no min key");
|
|
485
675
|
if (branch.childOffset > 0) {
|
|
486
676
|
branch.childOffset -= 1;
|
|
487
677
|
branch.children[branch.childOffset] = borrowedChild;
|
|
@@ -490,15 +680,20 @@ var borrowFromLeftBranch = (branch, leftSibling, branchIndex) => {
|
|
|
490
680
|
} else {
|
|
491
681
|
branch.children.unshift(borrowedChild);
|
|
492
682
|
branch.keys.unshift(borrowedMinKey);
|
|
493
|
-
for (let i = 0; i < branch.children.length; i += 1)
|
|
683
|
+
for (let i = 0; i < branch.children.length; i += 1)
|
|
684
|
+
branch.children[i].indexInParent = i;
|
|
494
685
|
}
|
|
495
686
|
const parent = requireParent(branch);
|
|
496
|
-
parent.keys[branchIndex] = {
|
|
687
|
+
parent.keys[branchIndex] = {
|
|
688
|
+
key: borrowedMinKey.key,
|
|
689
|
+
sequence: borrowedMinKey.sequence
|
|
690
|
+
};
|
|
497
691
|
updateMinKeyInAncestors(branch);
|
|
498
692
|
};
|
|
499
693
|
var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
|
|
500
694
|
const shiftIdx = rightSibling.childOffset;
|
|
501
|
-
if (shiftIdx >= rightSibling.children.length)
|
|
695
|
+
if (shiftIdx >= rightSibling.children.length)
|
|
696
|
+
throw new BTreeInvariantError("right branch borrow failed");
|
|
502
697
|
const borrowedChild = rightSibling.children[shiftIdx];
|
|
503
698
|
rightSibling.childOffset += 1;
|
|
504
699
|
if (rightSibling.childOffset >= rightSibling.children.length >>> 1) {
|
|
@@ -506,8 +701,12 @@ var borrowFromRightBranch = (branch, rightSibling, branchIndex) => {
|
|
|
506
701
|
}
|
|
507
702
|
branch.children.push(borrowedChild);
|
|
508
703
|
borrowedChild.parent = branch;
|
|
509
|
-
const borrowedMinKey = {
|
|
510
|
-
|
|
704
|
+
const borrowedMinKey = {
|
|
705
|
+
key: void 0,
|
|
706
|
+
sequence: 0
|
|
707
|
+
};
|
|
708
|
+
if (!writeMinKeyTo(borrowedChild, borrowedMinKey))
|
|
709
|
+
throw new BTreeInvariantError("borrowed child has no min key");
|
|
511
710
|
branch.keys.push(borrowedMinKey);
|
|
512
711
|
borrowedChild.indexInParent = branch.children.length - 1;
|
|
513
712
|
const parent = requireParent(branch);
|
|
@@ -560,7 +759,10 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
|
|
|
560
759
|
const parent = branch.parent;
|
|
561
760
|
if (parent === null) throw new BTreeInvariantError("branch has no parent");
|
|
562
761
|
const branchIndex = branch.indexInParent;
|
|
563
|
-
const { left: leftSibling, right: rightSibling } = findBranchSiblings(
|
|
762
|
+
const { left: leftSibling, right: rightSibling } = findBranchSiblings(
|
|
763
|
+
parent,
|
|
764
|
+
branchIndex
|
|
765
|
+
);
|
|
564
766
|
if (rightSibling !== null && branchChildCount(rightSibling) > state.minBranchChildren) {
|
|
565
767
|
borrowFromRightBranch(branch, rightSibling, branchIndex);
|
|
566
768
|
return;
|
|
@@ -579,44 +781,68 @@ var rebalanceAfterBranchRemoval = (state, branch) => {
|
|
|
579
781
|
}
|
|
580
782
|
throw new BTreeInvariantError("no branch siblings to rebalance");
|
|
581
783
|
};
|
|
784
|
+
|
|
785
|
+
// src/btree/rebalance.ts
|
|
786
|
+
var requireLeafNode = (node) => {
|
|
787
|
+
if (!isLeafNode(node))
|
|
788
|
+
throw new BTreeInvariantError("expected leaf, got branch");
|
|
789
|
+
return node;
|
|
790
|
+
};
|
|
791
|
+
var detachLeafFromChain = (state, leaf) => {
|
|
792
|
+
if (leaf.prev !== null) {
|
|
793
|
+
leaf.prev.next = leaf.next;
|
|
794
|
+
} else if (leaf.next !== null) {
|
|
795
|
+
state.leftmostLeaf = leaf.next;
|
|
796
|
+
}
|
|
797
|
+
if (leaf.next !== null) {
|
|
798
|
+
leaf.next.prev = leaf.prev;
|
|
799
|
+
} else if (leaf.prev !== null) {
|
|
800
|
+
state.rightmostLeaf = leaf.prev;
|
|
801
|
+
}
|
|
802
|
+
leaf.prev = null;
|
|
803
|
+
leaf.next = null;
|
|
804
|
+
};
|
|
582
805
|
var mergeLeafEntries = (target, source) => {
|
|
583
806
|
if (target.entryOffset > 0) {
|
|
584
807
|
target.entries.copyWithin(0, target.entryOffset);
|
|
585
808
|
target.entries.length = target.entries.length - target.entryOffset;
|
|
586
809
|
target.entryOffset = 0;
|
|
587
810
|
}
|
|
588
|
-
|
|
589
|
-
for (let i = source.entryOffset; i < src.length; i += 1) target.entries.push(src[i]);
|
|
811
|
+
target.entries.push(...source.entries.slice(source.entryOffset));
|
|
590
812
|
};
|
|
591
|
-
var
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
state.rightmostLeaf = leaf;
|
|
596
|
-
}
|
|
597
|
-
return;
|
|
813
|
+
var applyLazyThreshold = (min) => Math.max(1, Math.ceil(min / 4));
|
|
814
|
+
var leafRebalanceThreshold = (state) => {
|
|
815
|
+
if (state.deleteRebalancePolicy === "lazy") {
|
|
816
|
+
return applyLazyThreshold(state.minLeafEntries);
|
|
598
817
|
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
const
|
|
603
|
-
const
|
|
604
|
-
|
|
818
|
+
return state.minLeafEntries;
|
|
819
|
+
};
|
|
820
|
+
var findLeafSiblings = (parent, leafIndex) => {
|
|
821
|
+
const left = leafIndex > parent.childOffset ? requireLeafNode(parent.children[leafIndex - 1]) : null;
|
|
822
|
+
const right = leafIndex + 1 < parent.children.length ? requireLeafNode(parent.children[leafIndex + 1]) : null;
|
|
823
|
+
return { left, right };
|
|
824
|
+
};
|
|
825
|
+
var tryBorrowFromLeafSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
|
|
605
826
|
if (rightSibling !== null && leafEntryCount(rightSibling) > state.minLeafEntries) {
|
|
606
827
|
const borrowed = leafShiftEntry(rightSibling);
|
|
607
|
-
if (borrowed === void 0)
|
|
828
|
+
if (borrowed === void 0)
|
|
829
|
+
throw new BTreeInvariantError("right leaf borrow failed");
|
|
608
830
|
leaf.entries.push(borrowed);
|
|
609
831
|
writeMinKeyTo(rightSibling, parent.keys[leafIndex + 1]);
|
|
610
|
-
return;
|
|
832
|
+
return true;
|
|
611
833
|
}
|
|
612
834
|
if (leftSibling !== null && leafEntryCount(leftSibling) > state.minLeafEntries) {
|
|
613
835
|
const borrowed = leftSibling.entries.pop();
|
|
614
|
-
if (borrowed === void 0)
|
|
836
|
+
if (borrowed === void 0)
|
|
837
|
+
throw new BTreeInvariantError("left leaf borrow failed");
|
|
615
838
|
leafUnshiftEntry(leaf, borrowed);
|
|
616
839
|
parent.keys[leafIndex] = { key: borrowed.key, sequence: borrowed.entryId };
|
|
617
840
|
updateMinKeyInAncestors(leaf);
|
|
618
|
-
return;
|
|
841
|
+
return true;
|
|
619
842
|
}
|
|
843
|
+
return false;
|
|
844
|
+
};
|
|
845
|
+
var mergeLeafWithSibling = (state, leaf, parent, leafIndex, leftSibling, rightSibling) => {
|
|
620
846
|
if (leftSibling !== null) {
|
|
621
847
|
mergeLeafEntries(leftSibling, leaf);
|
|
622
848
|
detachLeafFromChain(state, leaf);
|
|
@@ -633,6 +859,24 @@ var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
|
633
859
|
}
|
|
634
860
|
throw new BTreeInvariantError("no leaf siblings to rebalance");
|
|
635
861
|
};
|
|
862
|
+
var rebalanceAfterLeafRemoval = (state, leaf) => {
|
|
863
|
+
if (leaf === state.root) {
|
|
864
|
+
if (state.entryCount === 0) {
|
|
865
|
+
state.leftmostLeaf = leaf;
|
|
866
|
+
state.rightmostLeaf = leaf;
|
|
867
|
+
}
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
if (leafEntryCount(leaf) >= leafRebalanceThreshold(state)) return;
|
|
871
|
+
const parent = leaf.parent;
|
|
872
|
+
if (parent === null)
|
|
873
|
+
throw new BTreeInvariantError("Leaf node has no parent during rebalance.");
|
|
874
|
+
const leafIndex = leaf.indexInParent;
|
|
875
|
+
const { left, right } = findLeafSiblings(parent, leafIndex);
|
|
876
|
+
if (tryBorrowFromLeafSibling(state, leaf, parent, leafIndex, left, right))
|
|
877
|
+
return;
|
|
878
|
+
mergeLeafWithSibling(state, leaf, parent, leafIndex, left, right);
|
|
879
|
+
};
|
|
636
880
|
|
|
637
881
|
// src/btree/deleteRange.ts
|
|
638
882
|
var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
@@ -645,27 +889,18 @@ var navigateToStart = (state, startKey, lowerExclusive) => {
|
|
|
645
889
|
}
|
|
646
890
|
return { leaf, idx };
|
|
647
891
|
};
|
|
648
|
-
var findRemoveEnd = (state, leaf,
|
|
892
|
+
var findRemoveEnd = (state, leaf, endKey, upperExclusive) => {
|
|
649
893
|
const count = leafEntryCount(leaf);
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
if (upperExclusive ? cmpEnd >= 0 : cmpEnd > 0) break;
|
|
655
|
-
removeEnd += 1;
|
|
656
|
-
}
|
|
657
|
-
return removeEnd;
|
|
658
|
-
};
|
|
659
|
-
var computeTreeHeight = (state) => {
|
|
660
|
-
let h = 0;
|
|
661
|
-
let n = state.root;
|
|
662
|
-
while (!isLeafNode(n)) {
|
|
663
|
-
n = n.children[n.childOffset];
|
|
664
|
-
h += 1;
|
|
894
|
+
const lastEntry = leafEntryAt(leaf, count - 1);
|
|
895
|
+
const cmpLast = state.compareKeys(lastEntry.key, endKey);
|
|
896
|
+
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
897
|
+
return count;
|
|
665
898
|
}
|
|
666
|
-
|
|
899
|
+
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
900
|
+
const endBound = upperExclusive ? lowerBoundInLeaf(state, leaf, endKey, endSeq) : upperBoundInLeaf(state, leaf, endKey, endSeq);
|
|
901
|
+
return endBound < count ? endBound : count;
|
|
667
902
|
};
|
|
668
|
-
var spliceLeafAndRebalance = (state, leaf, idx, removeCount
|
|
903
|
+
var spliceLeafAndRebalance = (state, leaf, idx, removeCount) => {
|
|
669
904
|
if (state.entryKeys !== null) {
|
|
670
905
|
for (let i = idx; i < idx + removeCount; i += 1) {
|
|
671
906
|
state.entryKeys.delete(leafEntryAt(leaf, i).entryId);
|
|
@@ -680,11 +915,13 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
|
|
|
680
915
|
updateMinKeyInAncestors(leaf);
|
|
681
916
|
}
|
|
682
917
|
const countAfterSplice = leafEntryCount(leaf);
|
|
683
|
-
|
|
684
|
-
|
|
918
|
+
const rebalThreshold = leafRebalanceThreshold(state);
|
|
919
|
+
let safetyGuard = state.minLeafEntries + 4;
|
|
920
|
+
while (safetyGuard > 0 && leaf !== state.root && leafEntryCount(leaf) < rebalThreshold) {
|
|
685
921
|
rebalanceAfterLeafRemoval(state, leaf);
|
|
686
|
-
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
687
|
-
|
|
922
|
+
if (leaf.parent !== null && leaf.parent.children[leaf.indexInParent] !== leaf)
|
|
923
|
+
break;
|
|
924
|
+
safetyGuard -= 1;
|
|
688
925
|
}
|
|
689
926
|
if (leafEmptied && leafEntryCount(leaf) > 0 && leaf.parent !== null && leaf.parent.children[leaf.indexInParent] === leaf) {
|
|
690
927
|
updateMinKeyInAncestors(leaf);
|
|
@@ -694,12 +931,9 @@ var spliceLeafAndRebalance = (state, leaf, idx, removeCount, maxRebalanceDepth)
|
|
|
694
931
|
var isLeafStillValid = (state, leaf) => leaf.parent === null ? leaf === state.root : leaf.parent.children[leaf.indexInParent] === leaf;
|
|
695
932
|
var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
696
933
|
if (state.entryCount === 0) return 0;
|
|
697
|
-
|
|
698
|
-
if (boundCompared > 0) return 0;
|
|
934
|
+
if (isEmptyRange(state.compareKeys, startKey, endKey, options)) return 0;
|
|
699
935
|
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
700
936
|
const upperExclusive = options?.upperBound === "exclusive";
|
|
701
|
-
if (lowerExclusive && upperExclusive && boundCompared === 0) return 0;
|
|
702
|
-
const treeHeight = computeTreeHeight(state);
|
|
703
937
|
let deleted = 0;
|
|
704
938
|
let needsNavigate = true;
|
|
705
939
|
let leaf = null;
|
|
@@ -714,10 +948,15 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
714
948
|
}
|
|
715
949
|
if (idx >= leafEntryCount(leaf)) break;
|
|
716
950
|
const count = leafEntryCount(leaf);
|
|
717
|
-
const removeEnd = findRemoveEnd(state, leaf,
|
|
951
|
+
const removeEnd = findRemoveEnd(state, leaf, endKey, upperExclusive);
|
|
718
952
|
const removeCount = removeEnd - idx;
|
|
719
953
|
if (removeCount === 0) break;
|
|
720
|
-
const countAfterSplice = spliceLeafAndRebalance(
|
|
954
|
+
const countAfterSplice = spliceLeafAndRebalance(
|
|
955
|
+
state,
|
|
956
|
+
leaf,
|
|
957
|
+
idx,
|
|
958
|
+
removeCount
|
|
959
|
+
);
|
|
721
960
|
deleted += removeCount;
|
|
722
961
|
if (removeEnd < count) break;
|
|
723
962
|
if (!isLeafStillValid(state, leaf)) {
|
|
@@ -736,6 +975,7 @@ var deleteRangeEntries = (state, startKey, endKey, options) => {
|
|
|
736
975
|
};
|
|
737
976
|
|
|
738
977
|
// src/btree/autoScale.ts
|
|
978
|
+
var minOccupancy = (max) => Math.ceil(max / 2);
|
|
739
979
|
var AUTO_SCALE_TIERS = [
|
|
740
980
|
{ threshold: 0, maxLeaf: 32, maxBranch: 32 },
|
|
741
981
|
{ threshold: 1e3, maxLeaf: 64, maxBranch: 64 },
|
|
@@ -762,33 +1002,50 @@ var computeNextAutoScaleThreshold = (entryCount) => {
|
|
|
762
1002
|
}
|
|
763
1003
|
return Number.MAX_SAFE_INTEGER;
|
|
764
1004
|
};
|
|
765
|
-
var
|
|
766
|
-
if (typeof config.compareKeys !== "function") {
|
|
767
|
-
throw new BTreeValidationError("compareKeys must be a function.");
|
|
768
|
-
}
|
|
769
|
-
const autoScale = config.autoScale === true;
|
|
1005
|
+
var resolveInitialCapacity = (config, autoScale) => {
|
|
770
1006
|
if (autoScale && (config.maxLeafEntries !== void 0 || config.maxBranchChildren !== void 0)) {
|
|
771
|
-
throw new BTreeValidationError(
|
|
1007
|
+
throw new BTreeValidationError(
|
|
1008
|
+
"autoScale conflicts with explicit capacity."
|
|
1009
|
+
);
|
|
772
1010
|
}
|
|
773
|
-
let maxLeafEntries;
|
|
774
|
-
let maxBranchChildren;
|
|
775
1011
|
if (autoScale) {
|
|
776
1012
|
const tier = computeAutoScaleTier(0);
|
|
777
|
-
maxLeafEntries
|
|
778
|
-
maxBranchChildren = tier.maxBranch;
|
|
779
|
-
} else {
|
|
780
|
-
maxLeafEntries = normalizeNodeCapacity(config.maxLeafEntries, "maxLeafEntries", DEFAULT_MAX_LEAF_ENTRIES);
|
|
781
|
-
maxBranchChildren = normalizeNodeCapacity(config.maxBranchChildren, "maxBranchChildren", DEFAULT_MAX_BRANCH_CHILDREN);
|
|
1013
|
+
return { maxLeafEntries: tier.maxLeaf, maxBranchChildren: tier.maxBranch };
|
|
782
1014
|
}
|
|
1015
|
+
return {
|
|
1016
|
+
maxLeafEntries: normalizeNodeCapacity(
|
|
1017
|
+
config.maxLeafEntries,
|
|
1018
|
+
"maxLeafEntries",
|
|
1019
|
+
DEFAULT_MAX_LEAF_ENTRIES
|
|
1020
|
+
),
|
|
1021
|
+
maxBranchChildren: normalizeNodeCapacity(
|
|
1022
|
+
config.maxBranchChildren,
|
|
1023
|
+
"maxBranchChildren",
|
|
1024
|
+
DEFAULT_MAX_BRANCH_CHILDREN
|
|
1025
|
+
)
|
|
1026
|
+
};
|
|
1027
|
+
};
|
|
1028
|
+
var createInitialState = (config) => {
|
|
1029
|
+
if (typeof config.compareKeys !== "function") {
|
|
1030
|
+
throw new BTreeValidationError("compareKeys must be a function.");
|
|
1031
|
+
}
|
|
1032
|
+
const autoScale = config.autoScale === true;
|
|
1033
|
+
const { maxLeafEntries, maxBranchChildren } = resolveInitialCapacity(
|
|
1034
|
+
config,
|
|
1035
|
+
autoScale
|
|
1036
|
+
);
|
|
783
1037
|
const duplicateKeys = normalizeDuplicateKeyPolicy(config.duplicateKeys);
|
|
1038
|
+
const deleteRebalancePolicy = normalizeDeleteRebalancePolicy(
|
|
1039
|
+
config.deleteRebalancePolicy
|
|
1040
|
+
);
|
|
784
1041
|
const emptyLeaf = createLeafNode([], null);
|
|
785
1042
|
return {
|
|
786
1043
|
compareKeys: config.compareKeys,
|
|
787
1044
|
maxLeafEntries,
|
|
788
1045
|
maxBranchChildren,
|
|
789
1046
|
duplicateKeys,
|
|
790
|
-
minLeafEntries:
|
|
791
|
-
minBranchChildren:
|
|
1047
|
+
minLeafEntries: minOccupancy(maxLeafEntries),
|
|
1048
|
+
minBranchChildren: minOccupancy(maxBranchChildren),
|
|
792
1049
|
root: emptyLeaf,
|
|
793
1050
|
leftmostLeaf: emptyLeaf,
|
|
794
1051
|
rightmostLeaf: emptyLeaf,
|
|
@@ -796,6 +1053,7 @@ var createInitialState = (config) => {
|
|
|
796
1053
|
nextSequence: 0,
|
|
797
1054
|
entryKeys: config.enableEntryIdLookup === true ? /* @__PURE__ */ new Map() : null,
|
|
798
1055
|
autoScale,
|
|
1056
|
+
deleteRebalancePolicy,
|
|
799
1057
|
_nextAutoScaleThreshold: autoScale ? computeNextAutoScaleThreshold(0) : Number.MAX_SAFE_INTEGER,
|
|
800
1058
|
_cursor: { leaf: emptyLeaf, index: 0 }
|
|
801
1059
|
};
|
|
@@ -805,13 +1063,15 @@ var maybeAutoScale = (state) => {
|
|
|
805
1063
|
const { maxLeaf, maxBranch } = computeAutoScaleTier(state.entryCount);
|
|
806
1064
|
if (maxLeaf > state.maxLeafEntries) {
|
|
807
1065
|
state.maxLeafEntries = maxLeaf;
|
|
808
|
-
state.minLeafEntries =
|
|
1066
|
+
state.minLeafEntries = minOccupancy(maxLeaf);
|
|
809
1067
|
}
|
|
810
1068
|
if (maxBranch > state.maxBranchChildren) {
|
|
811
1069
|
state.maxBranchChildren = maxBranch;
|
|
812
|
-
state.minBranchChildren =
|
|
1070
|
+
state.minBranchChildren = minOccupancy(maxBranch);
|
|
813
1071
|
}
|
|
814
|
-
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1072
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(
|
|
1073
|
+
state.entryCount
|
|
1074
|
+
);
|
|
815
1075
|
};
|
|
816
1076
|
var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren) => {
|
|
817
1077
|
if (!state.autoScale) {
|
|
@@ -835,8 +1095,16 @@ var applyAutoScaleCapacitySnapshot = (state, maxLeafEntries, maxBranchChildren)
|
|
|
835
1095
|
}
|
|
836
1096
|
state.maxLeafEntries = normalizedLeaf;
|
|
837
1097
|
state.maxBranchChildren = normalizedBranch;
|
|
838
|
-
state.minLeafEntries =
|
|
839
|
-
state.minBranchChildren =
|
|
1098
|
+
state.minLeafEntries = minOccupancy(normalizedLeaf);
|
|
1099
|
+
state.minBranchChildren = minOccupancy(normalizedBranch);
|
|
1100
|
+
};
|
|
1101
|
+
var resetAutoScaleToTier0 = (state) => {
|
|
1102
|
+
const tier = computeAutoScaleTier(0);
|
|
1103
|
+
state.maxLeafEntries = tier.maxLeaf;
|
|
1104
|
+
state.maxBranchChildren = tier.maxBranch;
|
|
1105
|
+
state.minLeafEntries = minOccupancy(tier.maxLeaf);
|
|
1106
|
+
state.minBranchChildren = minOccupancy(tier.maxBranch);
|
|
1107
|
+
state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
840
1108
|
};
|
|
841
1109
|
|
|
842
1110
|
// src/btree/bulkLoad.ts
|
|
@@ -858,7 +1126,11 @@ var computeChunkBoundaries = (total, max, min) => {
|
|
|
858
1126
|
return boundaries;
|
|
859
1127
|
};
|
|
860
1128
|
var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
861
|
-
const boundaries = computeChunkBoundaries(
|
|
1129
|
+
const boundaries = computeChunkBoundaries(
|
|
1130
|
+
entries.length,
|
|
1131
|
+
state.maxLeafEntries,
|
|
1132
|
+
state.minLeafEntries
|
|
1133
|
+
);
|
|
862
1134
|
const leaves = new Array(boundaries.length);
|
|
863
1135
|
let chunkStart = 0;
|
|
864
1136
|
for (let c = 0; c < boundaries.length; c += 1) {
|
|
@@ -866,7 +1138,11 @@ var buildLeaves = (state, entries, ids, baseSequence) => {
|
|
|
866
1138
|
const chunk = new Array(chunkEnd - chunkStart);
|
|
867
1139
|
for (let i = chunkStart; i < chunkEnd; i += 1) {
|
|
868
1140
|
const seq = baseSequence + i;
|
|
869
|
-
chunk[i - chunkStart] =
|
|
1141
|
+
chunk[i - chunkStart] = createEntry(
|
|
1142
|
+
entries[i].key,
|
|
1143
|
+
seq,
|
|
1144
|
+
entries[i].value
|
|
1145
|
+
);
|
|
870
1146
|
ids[i] = seq;
|
|
871
1147
|
if (state.entryKeys !== null) {
|
|
872
1148
|
state.entryKeys.set(seq, entries[i].key);
|
|
@@ -900,11 +1176,18 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
900
1176
|
} else {
|
|
901
1177
|
let currentLevel = leaves;
|
|
902
1178
|
while (currentLevel.length > 1) {
|
|
903
|
-
const bounds = computeChunkBoundaries(
|
|
1179
|
+
const bounds = computeChunkBoundaries(
|
|
1180
|
+
currentLevel.length,
|
|
1181
|
+
state.maxBranchChildren,
|
|
1182
|
+
state.minBranchChildren
|
|
1183
|
+
);
|
|
904
1184
|
const nextLevel = new Array(bounds.length);
|
|
905
1185
|
let start = 0;
|
|
906
1186
|
for (let b = 0; b < bounds.length; b += 1) {
|
|
907
|
-
nextLevel[b] = createBranchNode(
|
|
1187
|
+
nextLevel[b] = createBranchNode(
|
|
1188
|
+
currentLevel.slice(start, bounds[b]),
|
|
1189
|
+
null
|
|
1190
|
+
);
|
|
908
1191
|
start = bounds[b];
|
|
909
1192
|
}
|
|
910
1193
|
currentLevel = nextLevel;
|
|
@@ -915,9 +1198,12 @@ var bulkLoadEntries = (state, entries) => {
|
|
|
915
1198
|
return ids;
|
|
916
1199
|
};
|
|
917
1200
|
|
|
918
|
-
// src/btree/
|
|
1201
|
+
// src/btree/split.ts
|
|
919
1202
|
var insertChildAfter = (state, parent, existingChild, childToInsert) => {
|
|
920
|
-
const newChildMinKey = {
|
|
1203
|
+
const newChildMinKey = {
|
|
1204
|
+
key: void 0,
|
|
1205
|
+
sequence: 0
|
|
1206
|
+
};
|
|
921
1207
|
if (!writeMinKeyTo(childToInsert, newChildMinKey)) {
|
|
922
1208
|
throw new BTreeInvariantError("inserted child has no min key");
|
|
923
1209
|
}
|
|
@@ -945,60 +1231,146 @@ var splitLeaf = (state, leaf) => {
|
|
|
945
1231
|
} else {
|
|
946
1232
|
state.rightmostLeaf = sibling;
|
|
947
1233
|
}
|
|
948
|
-
leaf.next = sibling;
|
|
949
|
-
if (leaf.parent === null) {
|
|
950
|
-
state.root = createBranchNode([leaf, sibling], null);
|
|
951
|
-
return;
|
|
1234
|
+
leaf.next = sibling;
|
|
1235
|
+
if (leaf.parent === null) {
|
|
1236
|
+
state.root = createBranchNode([leaf, sibling], null);
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
insertChildAfter(state, leaf.parent, leaf, sibling);
|
|
1240
|
+
};
|
|
1241
|
+
var splitBranch = (state, branch) => {
|
|
1242
|
+
branchCompact(branch);
|
|
1243
|
+
const splitAt = Math.ceil(branch.children.length / 2);
|
|
1244
|
+
const sibling = createBranchNode(
|
|
1245
|
+
branch.children.splice(splitAt),
|
|
1246
|
+
branch.parent
|
|
1247
|
+
);
|
|
1248
|
+
branch.keys.splice(splitAt);
|
|
1249
|
+
if (branch.parent === null) {
|
|
1250
|
+
state.root = createBranchNode([branch, sibling], null);
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
insertChildAfter(state, branch.parent, branch, sibling);
|
|
1254
|
+
};
|
|
1255
|
+
|
|
1256
|
+
// src/btree/entry-lookup.ts
|
|
1257
|
+
var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
1258
|
+
const targetLeaf = findLeafForKey(state, userKey, sequence);
|
|
1259
|
+
const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
|
|
1260
|
+
if (index >= leafEntryCount(targetLeaf)) return null;
|
|
1261
|
+
const entry = leafEntryAt(targetLeaf, index);
|
|
1262
|
+
if (entry.entryId !== sequence) return null;
|
|
1263
|
+
return { leaf: targetLeaf, index };
|
|
1264
|
+
};
|
|
1265
|
+
var peekEntryById = (state, entryId) => {
|
|
1266
|
+
if (state.entryKeys === null) {
|
|
1267
|
+
throw new BTreeInvariantError(
|
|
1268
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1269
|
+
);
|
|
1270
|
+
}
|
|
1271
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1272
|
+
if (userKey === void 0) return null;
|
|
1273
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1274
|
+
if (found === null) return null;
|
|
1275
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1276
|
+
return entry;
|
|
1277
|
+
};
|
|
1278
|
+
var updateEntryById = (state, entryId, newValue) => {
|
|
1279
|
+
if (state.entryKeys === null) {
|
|
1280
|
+
throw new BTreeInvariantError(
|
|
1281
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1285
|
+
if (userKey === void 0) return null;
|
|
1286
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1287
|
+
if (found === null) return null;
|
|
1288
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1289
|
+
const updated = createEntry(entry.key, entry.entryId, newValue);
|
|
1290
|
+
found.leaf.entries[found.leaf.entryOffset + found.index] = updated;
|
|
1291
|
+
return updated;
|
|
1292
|
+
};
|
|
1293
|
+
var removeEntryById = (state, entryId) => {
|
|
1294
|
+
if (state.entryKeys === null) {
|
|
1295
|
+
throw new BTreeInvariantError(
|
|
1296
|
+
"entryKeys lookup map is not enabled on this tree."
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
const userKey = state.entryKeys.get(entryId);
|
|
1300
|
+
if (userKey === void 0) return null;
|
|
1301
|
+
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1302
|
+
if (found === null) return null;
|
|
1303
|
+
const entry = leafEntryAt(found.leaf, found.index);
|
|
1304
|
+
leafRemoveAt(found.leaf, found.index);
|
|
1305
|
+
state.entryCount -= 1;
|
|
1306
|
+
state.entryKeys.delete(entryId);
|
|
1307
|
+
if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
|
|
1308
|
+
updateMinKeyInAncestors(found.leaf);
|
|
1309
|
+
}
|
|
1310
|
+
if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
|
|
1311
|
+
rebalanceAfterLeafRemoval(state, found.leaf);
|
|
952
1312
|
}
|
|
953
|
-
|
|
1313
|
+
return entry;
|
|
954
1314
|
};
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
state.
|
|
962
|
-
|
|
1315
|
+
|
|
1316
|
+
// src/btree/mutations.ts
|
|
1317
|
+
var findDuplicateEntry = (state, targetLeaf, key, insertAt) => {
|
|
1318
|
+
if (state.duplicateKeys === "allow") return null;
|
|
1319
|
+
if (insertAt > 0) {
|
|
1320
|
+
const candidate = leafEntryAt(targetLeaf, insertAt - 1);
|
|
1321
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1322
|
+
return {
|
|
1323
|
+
leaf: targetLeaf,
|
|
1324
|
+
physIndex: targetLeaf.entryOffset + insertAt - 1,
|
|
1325
|
+
entry: candidate
|
|
1326
|
+
};
|
|
1327
|
+
}
|
|
1328
|
+
} else if (targetLeaf.prev !== null && leafEntryCount(targetLeaf.prev) > 0) {
|
|
1329
|
+
const prevLeaf = targetLeaf.prev;
|
|
1330
|
+
const prevCount = leafEntryCount(prevLeaf);
|
|
1331
|
+
const candidate = leafEntryAt(prevLeaf, prevCount - 1);
|
|
1332
|
+
if (state.compareKeys(candidate.key, key) === 0) {
|
|
1333
|
+
return {
|
|
1334
|
+
leaf: prevLeaf,
|
|
1335
|
+
physIndex: prevLeaf.entryOffset + prevCount - 1,
|
|
1336
|
+
entry: candidate
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
963
1339
|
}
|
|
964
|
-
|
|
1340
|
+
return null;
|
|
965
1341
|
};
|
|
966
1342
|
var putEntryIntoLeaf = (state, targetLeaf, key, value) => {
|
|
967
1343
|
const sequence = state.nextSequence;
|
|
968
1344
|
const insertAt = upperBoundInLeaf(state, targetLeaf, key, sequence);
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
if (
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
existingEntry = candidate;
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
if (existingEntry !== null) {
|
|
984
|
-
if (state.duplicateKeys === "reject") {
|
|
985
|
-
throw new BTreeValidationError("Duplicate key rejected.");
|
|
986
|
-
}
|
|
987
|
-
existingEntry.value = value;
|
|
988
|
-
return existingEntry.entryId;
|
|
989
|
-
}
|
|
1345
|
+
const dup = findDuplicateEntry(state, targetLeaf, key, insertAt);
|
|
1346
|
+
if (dup !== null) {
|
|
1347
|
+
if (state.duplicateKeys === "reject") {
|
|
1348
|
+
throw new BTreeValidationError("Duplicate key rejected.");
|
|
1349
|
+
}
|
|
1350
|
+
dup.leaf.entries[dup.physIndex] = createEntry(
|
|
1351
|
+
dup.entry.key,
|
|
1352
|
+
dup.entry.entryId,
|
|
1353
|
+
value
|
|
1354
|
+
);
|
|
1355
|
+
return dup.entry.entryId;
|
|
990
1356
|
}
|
|
991
1357
|
if (state.nextSequence >= Number.MAX_SAFE_INTEGER) {
|
|
992
1358
|
throw new BTreeValidationError("Sequence overflow.");
|
|
993
1359
|
}
|
|
994
1360
|
state.nextSequence += 1;
|
|
995
|
-
leafInsertAt(
|
|
1361
|
+
leafInsertAt(
|
|
1362
|
+
targetLeaf,
|
|
1363
|
+
insertAt,
|
|
1364
|
+
createEntry(key, sequence, value)
|
|
1365
|
+
);
|
|
996
1366
|
state.entryCount += 1;
|
|
997
1367
|
if (state.entryKeys !== null) {
|
|
998
1368
|
state.entryKeys.set(sequence, key);
|
|
999
1369
|
}
|
|
1000
|
-
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1001
|
-
|
|
1370
|
+
if (insertAt === 0 && targetLeaf.parent !== null)
|
|
1371
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1372
|
+
if (leafEntryCount(targetLeaf) > state.maxLeafEntries)
|
|
1373
|
+
splitLeaf(state, targetLeaf);
|
|
1002
1374
|
maybeAutoScale(state);
|
|
1003
1375
|
return sequence;
|
|
1004
1376
|
};
|
|
@@ -1009,7 +1381,8 @@ var putEntry = (state, key, value) => {
|
|
|
1009
1381
|
var popFirstEntry = (state) => {
|
|
1010
1382
|
if (state.entryCount === 0) return null;
|
|
1011
1383
|
const firstEntry = leafShiftEntry(state.leftmostLeaf);
|
|
1012
|
-
if (firstEntry === void 0)
|
|
1384
|
+
if (firstEntry === void 0)
|
|
1385
|
+
throw new BTreeInvariantError("leftmost leaf empty but count > 0");
|
|
1013
1386
|
state.entryCount -= 1;
|
|
1014
1387
|
if (state.entryKeys !== null) {
|
|
1015
1388
|
state.entryKeys.delete(firstEntry.entryId);
|
|
@@ -1025,7 +1398,8 @@ var popFirstEntry = (state) => {
|
|
|
1025
1398
|
var popLastEntry = (state) => {
|
|
1026
1399
|
if (state.entryCount === 0) return null;
|
|
1027
1400
|
const lastEntry = leafPopEntry(state.rightmostLeaf);
|
|
1028
|
-
if (lastEntry === void 0)
|
|
1401
|
+
if (lastEntry === void 0)
|
|
1402
|
+
throw new BTreeInvariantError("rightmost leaf empty but count > 0");
|
|
1029
1403
|
state.entryCount -= 1;
|
|
1030
1404
|
if (state.entryKeys !== null) {
|
|
1031
1405
|
state.entryKeys.delete(lastEntry.entryId);
|
|
@@ -1046,62 +1420,26 @@ var removeFirstMatchingEntry = (state, key) => {
|
|
|
1046
1420
|
if (state.entryKeys !== null) {
|
|
1047
1421
|
state.entryKeys.delete(targetEntry.entryId);
|
|
1048
1422
|
}
|
|
1049
|
-
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1423
|
+
if (removeAt === 0 && leafEntryCount(targetLeaf) > 0 && targetLeaf.parent !== null)
|
|
1424
|
+
updateMinKeyInAncestors(targetLeaf);
|
|
1050
1425
|
if (targetLeaf !== state.root && leafEntryCount(targetLeaf) < state.minLeafEntries) {
|
|
1051
1426
|
rebalanceAfterLeafRemoval(state, targetLeaf);
|
|
1052
1427
|
}
|
|
1053
1428
|
return targetEntry;
|
|
1054
1429
|
};
|
|
1055
|
-
var findLeafEntryBySequence = (state, userKey, sequence) => {
|
|
1056
|
-
const targetLeaf = findLeafForKey(state, userKey, sequence);
|
|
1057
|
-
const index = lowerBoundInLeaf(state, targetLeaf, userKey, sequence);
|
|
1058
|
-
if (index >= leafEntryCount(targetLeaf)) return null;
|
|
1059
|
-
const entry = leafEntryAt(targetLeaf, index);
|
|
1060
|
-
if (entry.entryId !== sequence) return null;
|
|
1061
|
-
return { leaf: targetLeaf, index };
|
|
1062
|
-
};
|
|
1063
|
-
var removeEntryById = (state, entryId) => {
|
|
1064
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1065
|
-
if (userKey === void 0) return null;
|
|
1066
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1067
|
-
if (found === null) return null;
|
|
1068
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1069
|
-
leafRemoveAt(found.leaf, found.index);
|
|
1070
|
-
state.entryCount -= 1;
|
|
1071
|
-
state.entryKeys.delete(entryId);
|
|
1072
|
-
if (found.index === 0 && leafEntryCount(found.leaf) > 0 && found.leaf.parent !== null) {
|
|
1073
|
-
updateMinKeyInAncestors(found.leaf);
|
|
1074
|
-
}
|
|
1075
|
-
if (found.leaf !== state.root && leafEntryCount(found.leaf) < state.minLeafEntries) {
|
|
1076
|
-
rebalanceAfterLeafRemoval(state, found.leaf);
|
|
1077
|
-
}
|
|
1078
|
-
return entry;
|
|
1079
|
-
};
|
|
1080
|
-
var peekEntryById = (state, entryId) => {
|
|
1081
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1082
|
-
if (userKey === void 0) return null;
|
|
1083
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1084
|
-
if (found === null) return null;
|
|
1085
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1086
|
-
return entry;
|
|
1087
|
-
};
|
|
1088
|
-
var updateEntryById = (state, entryId, newValue) => {
|
|
1089
|
-
const userKey = state.entryKeys.get(entryId);
|
|
1090
|
-
if (userKey === void 0) return null;
|
|
1091
|
-
const found = findLeafEntryBySequence(state, userKey, entryId);
|
|
1092
|
-
if (found === null) return null;
|
|
1093
|
-
const entry = leafEntryAt(found.leaf, found.index);
|
|
1094
|
-
entry.value = newValue;
|
|
1095
|
-
return entry;
|
|
1096
|
-
};
|
|
1097
1430
|
var putManyEntries = (state, entries) => {
|
|
1098
1431
|
if (entries.length === 0) return [];
|
|
1099
1432
|
const strictlyAscending = state.duplicateKeys !== "allow";
|
|
1100
1433
|
for (let i = 1; i < entries.length; i += 1) {
|
|
1101
1434
|
const cmp = state.compareKeys(entries[i - 1].key, entries[i].key);
|
|
1102
|
-
if (
|
|
1435
|
+
if (cmp > 0) {
|
|
1436
|
+
throw new BTreeValidationError(
|
|
1437
|
+
"putMany: entries not in ascending order."
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
if (strictlyAscending && cmp === 0) {
|
|
1103
1441
|
throw new BTreeValidationError(
|
|
1104
|
-
|
|
1442
|
+
state.duplicateKeys === "reject" ? "putMany: duplicate key rejected." : "putMany: equal keys not allowed in strict mode."
|
|
1105
1443
|
);
|
|
1106
1444
|
}
|
|
1107
1445
|
}
|
|
@@ -1110,7 +1448,12 @@ var putManyEntries = (state, entries) => {
|
|
|
1110
1448
|
let hintLeaf = findLeafForKey(state, entries[0].key, state.nextSequence);
|
|
1111
1449
|
for (let i = 0; i < entries.length; i += 1) {
|
|
1112
1450
|
const entry = entries[i];
|
|
1113
|
-
const targetLeaf = findLeafFromHint(
|
|
1451
|
+
const targetLeaf = findLeafFromHint(
|
|
1452
|
+
state,
|
|
1453
|
+
hintLeaf,
|
|
1454
|
+
entry.key,
|
|
1455
|
+
state.nextSequence
|
|
1456
|
+
);
|
|
1114
1457
|
ids[i] = putEntryIntoLeaf(state, targetLeaf, entry.key, entry.value);
|
|
1115
1458
|
hintLeaf = targetLeaf;
|
|
1116
1459
|
}
|
|
@@ -1119,116 +1462,14 @@ var putManyEntries = (state, entries) => {
|
|
|
1119
1462
|
return bulkLoadEntries(state, entries);
|
|
1120
1463
|
};
|
|
1121
1464
|
|
|
1122
|
-
// src/btree/rangeQuery.ts
|
|
1123
|
-
var initRangeCursor = (state, startKey, endKey, options) => {
|
|
1124
|
-
if (state.entryCount === 0) return null;
|
|
1125
|
-
const compare = state.compareKeys;
|
|
1126
|
-
const boundCompared = compare(startKey, endKey);
|
|
1127
|
-
if (boundCompared > 0) return null;
|
|
1128
|
-
const lowerExclusive = options?.lowerBound === "exclusive";
|
|
1129
|
-
const upperExclusive = options?.upperBound === "exclusive";
|
|
1130
|
-
if (lowerExclusive && upperExclusive && boundCompared === 0) return null;
|
|
1131
|
-
const startSeq = lowerExclusive ? Number.MAX_SAFE_INTEGER : 0;
|
|
1132
|
-
const leaf = findLeafForKey(state, startKey, startSeq);
|
|
1133
|
-
const index = lowerExclusive ? upperBoundInLeaf(state, leaf, startKey, Number.MAX_SAFE_INTEGER) : lowerBoundInLeaf(state, leaf, startKey, 0);
|
|
1134
|
-
return { leaf, index, compare, upperExclusive };
|
|
1135
|
-
};
|
|
1136
|
-
var countRangeEntries = (state, startKey, endKey, options) => {
|
|
1137
|
-
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
1138
|
-
if (cursor === null) return 0;
|
|
1139
|
-
let cursorLeaf = cursor.leaf;
|
|
1140
|
-
let cursorIndex = cursor.index;
|
|
1141
|
-
const { compare, upperExclusive } = cursor;
|
|
1142
|
-
let count = 0;
|
|
1143
|
-
while (cursorLeaf !== null) {
|
|
1144
|
-
const leafCount = leafEntryCount(cursorLeaf);
|
|
1145
|
-
if (cursorIndex >= leafCount) {
|
|
1146
|
-
cursorLeaf = cursorLeaf.next;
|
|
1147
|
-
cursorIndex = 0;
|
|
1148
|
-
continue;
|
|
1149
|
-
}
|
|
1150
|
-
const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
|
|
1151
|
-
const cmpLast = compare(lastEntry.key, endKey);
|
|
1152
|
-
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
1153
|
-
count += leafCount - cursorIndex;
|
|
1154
|
-
cursorLeaf = cursorLeaf.next;
|
|
1155
|
-
cursorIndex = 0;
|
|
1156
|
-
continue;
|
|
1157
|
-
}
|
|
1158
|
-
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
1159
|
-
const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
|
|
1160
|
-
const limit = endBound < leafCount ? endBound : leafCount;
|
|
1161
|
-
count += limit - cursorIndex;
|
|
1162
|
-
return count;
|
|
1163
|
-
}
|
|
1164
|
-
return count;
|
|
1165
|
-
};
|
|
1166
|
-
var RANGE_PREALLOC_THRESHOLD = 200;
|
|
1167
|
-
var allocateRangeOutput = (state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options) => {
|
|
1168
|
-
const firstLeafCount = leafEntryCount(cursorLeaf);
|
|
1169
|
-
const firstLeafRemainder = firstLeafCount - cursorIndex;
|
|
1170
|
-
if (firstLeafRemainder >= RANGE_PREALLOC_THRESHOLD && cursorLeaf.next !== null) {
|
|
1171
|
-
const lastEntry = leafEntryAt(cursorLeaf, firstLeafCount - 1);
|
|
1172
|
-
const cmpLast = compare(lastEntry.key, endKey);
|
|
1173
|
-
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
1174
|
-
const total = countRangeEntries(state, startKey, endKey, options);
|
|
1175
|
-
return new Array(total);
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
return [];
|
|
1179
|
-
};
|
|
1180
|
-
var appendLeafSlice = (leaf, from, to, output, useIndexed, writeIdx) => {
|
|
1181
|
-
if (useIndexed) {
|
|
1182
|
-
for (let i = from; i < to; i += 1) {
|
|
1183
|
-
output[writeIdx++] = leafEntryAt(leaf, i);
|
|
1184
|
-
}
|
|
1185
|
-
} else {
|
|
1186
|
-
for (let i = from; i < to; i += 1) {
|
|
1187
|
-
output.push(leafEntryAt(leaf, i));
|
|
1188
|
-
}
|
|
1189
|
-
}
|
|
1190
|
-
return writeIdx;
|
|
1191
|
-
};
|
|
1192
|
-
var rangeQueryEntries = (state, startKey, endKey, options) => {
|
|
1193
|
-
const cursor = initRangeCursor(state, startKey, endKey, options);
|
|
1194
|
-
if (cursor === null) return [];
|
|
1195
|
-
let cursorLeaf = cursor.leaf;
|
|
1196
|
-
let cursorIndex = cursor.index;
|
|
1197
|
-
const { compare, upperExclusive } = cursor;
|
|
1198
|
-
const output = allocateRangeOutput(state, cursorLeaf, cursorIndex, compare, upperExclusive, startKey, endKey, options);
|
|
1199
|
-
let writeIdx = 0;
|
|
1200
|
-
const useIndexed = output.length > 0;
|
|
1201
|
-
while (cursorLeaf !== null) {
|
|
1202
|
-
const leafCount = leafEntryCount(cursorLeaf);
|
|
1203
|
-
if (cursorIndex >= leafCount) {
|
|
1204
|
-
cursorLeaf = cursorLeaf.next;
|
|
1205
|
-
cursorIndex = 0;
|
|
1206
|
-
continue;
|
|
1207
|
-
}
|
|
1208
|
-
const lastEntry = leafEntryAt(cursorLeaf, leafCount - 1);
|
|
1209
|
-
const cmpLast = compare(lastEntry.key, endKey);
|
|
1210
|
-
if (upperExclusive ? cmpLast < 0 : cmpLast <= 0) {
|
|
1211
|
-
writeIdx = appendLeafSlice(cursorLeaf, cursorIndex, leafCount, output, useIndexed, writeIdx);
|
|
1212
|
-
cursorLeaf = cursorLeaf.next;
|
|
1213
|
-
cursorIndex = 0;
|
|
1214
|
-
continue;
|
|
1215
|
-
}
|
|
1216
|
-
const endSeq = upperExclusive ? 0 : Number.MAX_SAFE_INTEGER;
|
|
1217
|
-
const endBound = upperExclusive ? lowerBoundInLeaf(state, cursorLeaf, endKey, endSeq) : upperBoundInLeaf(state, cursorLeaf, endKey, endSeq);
|
|
1218
|
-
const limit = endBound < leafCount ? endBound : leafCount;
|
|
1219
|
-
appendLeafSlice(cursorLeaf, cursorIndex, limit, output, useIndexed, writeIdx);
|
|
1220
|
-
return output;
|
|
1221
|
-
}
|
|
1222
|
-
return output;
|
|
1223
|
-
};
|
|
1224
|
-
|
|
1225
1465
|
// src/btree/serialization.ts
|
|
1226
1466
|
var buildConfigFromState = (state) => {
|
|
1227
1467
|
const config = {
|
|
1228
1468
|
compareKeys: state.compareKeys,
|
|
1229
1469
|
duplicateKeys: state.duplicateKeys,
|
|
1230
1470
|
enableEntryIdLookup: state.entryKeys !== null,
|
|
1231
|
-
autoScale: state.autoScale
|
|
1471
|
+
autoScale: state.autoScale,
|
|
1472
|
+
deleteRebalancePolicy: state.deleteRebalancePolicy
|
|
1232
1473
|
};
|
|
1233
1474
|
if (!state.autoScale) {
|
|
1234
1475
|
config.maxLeafEntries = state.maxLeafEntries;
|
|
@@ -1248,17 +1489,17 @@ var serializeToJSON = (state) => {
|
|
|
1248
1489
|
}
|
|
1249
1490
|
leaf = leaf.next;
|
|
1250
1491
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
enableEntryIdLookup: state.entryKeys !== null,
|
|
1258
|
-
autoScale: state.autoScale
|
|
1259
|
-
},
|
|
1260
|
-
entries
|
|
1492
|
+
const config = {
|
|
1493
|
+
maxLeafEntries: state.maxLeafEntries,
|
|
1494
|
+
maxBranchChildren: state.maxBranchChildren,
|
|
1495
|
+
duplicateKeys: state.duplicateKeys,
|
|
1496
|
+
enableEntryIdLookup: state.entryKeys !== null,
|
|
1497
|
+
autoScale: state.autoScale
|
|
1261
1498
|
};
|
|
1499
|
+
if (state.deleteRebalancePolicy !== "standard") {
|
|
1500
|
+
config.deleteRebalancePolicy = state.deleteRebalancePolicy;
|
|
1501
|
+
}
|
|
1502
|
+
return { version: 1, config, entries };
|
|
1262
1503
|
};
|
|
1263
1504
|
var MAX_SERIALIZED_ENTRIES = 1e6;
|
|
1264
1505
|
var validateStructure = (json) => {
|
|
@@ -1321,13 +1562,28 @@ var validateBTreeJSON = (json) => {
|
|
|
1321
1562
|
validateStructure(json);
|
|
1322
1563
|
validateConfig(json.config);
|
|
1323
1564
|
};
|
|
1565
|
+
var validateBTreeJSONSortOrder = (json, compareKeys) => {
|
|
1566
|
+
const strict = json.config.duplicateKeys !== "allow";
|
|
1567
|
+
for (let i = 1; i < json.entries.length; i += 1) {
|
|
1568
|
+
const cmp = compareKeys(json.entries[i - 1][0], json.entries[i][0]);
|
|
1569
|
+
if (cmp > 0) {
|
|
1570
|
+
throw new BTreeValidationError("fromJSON: entries not sorted.");
|
|
1571
|
+
}
|
|
1572
|
+
if (strict && cmp === 0) {
|
|
1573
|
+
throw new BTreeValidationError(
|
|
1574
|
+
'fromJSON: duplicate keys require duplicateKeys "allow".'
|
|
1575
|
+
);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
};
|
|
1324
1579
|
var buildConfigFromJSON = (json, compareKeys) => {
|
|
1325
1580
|
const cfg = json.config;
|
|
1326
1581
|
const config = {
|
|
1327
1582
|
compareKeys,
|
|
1328
1583
|
duplicateKeys: cfg.duplicateKeys,
|
|
1329
1584
|
enableEntryIdLookup: cfg.enableEntryIdLookup,
|
|
1330
|
-
autoScale: cfg.autoScale
|
|
1585
|
+
autoScale: cfg.autoScale,
|
|
1586
|
+
deleteRebalancePolicy: cfg.deleteRebalancePolicy
|
|
1331
1587
|
};
|
|
1332
1588
|
if (!cfg.autoScale) {
|
|
1333
1589
|
config.maxLeafEntries = cfg.maxLeafEntries;
|
|
@@ -1336,6 +1592,44 @@ var buildConfigFromJSON = (json, compareKeys) => {
|
|
|
1336
1592
|
return config;
|
|
1337
1593
|
};
|
|
1338
1594
|
|
|
1595
|
+
// src/btree/traversal.ts
|
|
1596
|
+
var snapshotEntries = (state) => {
|
|
1597
|
+
const result = new Array(state.entryCount);
|
|
1598
|
+
let leaf = state.leftmostLeaf;
|
|
1599
|
+
let writeIdx = 0;
|
|
1600
|
+
while (leaf !== null) {
|
|
1601
|
+
const count = leafEntryCount(leaf);
|
|
1602
|
+
for (let i = 0; i < count; i += 1) {
|
|
1603
|
+
result[writeIdx++] = freezeEntry(leafEntryAt(leaf, i));
|
|
1604
|
+
}
|
|
1605
|
+
leaf = leaf.next;
|
|
1606
|
+
}
|
|
1607
|
+
return result;
|
|
1608
|
+
};
|
|
1609
|
+
var collectInternalEntries = (state) => {
|
|
1610
|
+
const result = new Array(state.entryCount);
|
|
1611
|
+
let leaf = state.leftmostLeaf;
|
|
1612
|
+
let writeIdx = 0;
|
|
1613
|
+
while (leaf !== null) {
|
|
1614
|
+
const count = leafEntryCount(leaf);
|
|
1615
|
+
for (let i = 0; i < count; i += 1) {
|
|
1616
|
+
result[writeIdx++] = leafEntryAt(leaf, i);
|
|
1617
|
+
}
|
|
1618
|
+
leaf = leaf.next;
|
|
1619
|
+
}
|
|
1620
|
+
return result;
|
|
1621
|
+
};
|
|
1622
|
+
var forEachEntry = (state, callback, thisArg) => {
|
|
1623
|
+
let leaf = state.leftmostLeaf;
|
|
1624
|
+
while (leaf !== null) {
|
|
1625
|
+
const count = leafEntryCount(leaf);
|
|
1626
|
+
for (let i = 0; i < count; i += 1) {
|
|
1627
|
+
callback.call(thisArg, freezeEntry(leafEntryAt(leaf, i)));
|
|
1628
|
+
}
|
|
1629
|
+
leaf = leaf.next;
|
|
1630
|
+
}
|
|
1631
|
+
};
|
|
1632
|
+
|
|
1339
1633
|
// src/btree/integrity-helpers.ts
|
|
1340
1634
|
var nodeMinKey = (node) => {
|
|
1341
1635
|
if (isLeafNode(node)) {
|
|
@@ -1344,14 +1638,17 @@ var nodeMinKey = (node) => {
|
|
|
1344
1638
|
return { key: e.key, sequence: e.entryId };
|
|
1345
1639
|
}
|
|
1346
1640
|
if (node.childOffset >= node.keys.length) return null;
|
|
1347
|
-
return {
|
|
1641
|
+
return {
|
|
1642
|
+
key: node.keys[node.childOffset].key,
|
|
1643
|
+
sequence: node.keys[node.childOffset].sequence
|
|
1644
|
+
};
|
|
1348
1645
|
};
|
|
1349
1646
|
var compareNodeKeys = (comparator, leftKey, leftSeq, rightKey, rightSeq) => {
|
|
1350
1647
|
const cmp = comparator(leftKey, rightKey);
|
|
1351
1648
|
if (cmp !== 0) {
|
|
1352
1649
|
return cmp;
|
|
1353
1650
|
}
|
|
1354
|
-
return leftSeq - rightSeq;
|
|
1651
|
+
return leftSeq < rightSeq ? -1 : leftSeq > rightSeq ? 1 : 0;
|
|
1355
1652
|
};
|
|
1356
1653
|
var getNodeMaxKey = (node) => {
|
|
1357
1654
|
if (isLeafNode(node)) {
|
|
@@ -1366,9 +1663,7 @@ var getNodeMaxKey = (node) => {
|
|
|
1366
1663
|
};
|
|
1367
1664
|
var validateComparatorResult = (result) => {
|
|
1368
1665
|
if (!Number.isFinite(result)) {
|
|
1369
|
-
throw new BTreeValidationError(
|
|
1370
|
-
"compareKeys must return a finite number."
|
|
1371
|
-
);
|
|
1666
|
+
throw new BTreeValidationError("compareKeys must return a finite number.");
|
|
1372
1667
|
}
|
|
1373
1668
|
return result;
|
|
1374
1669
|
};
|
|
@@ -1428,6 +1723,34 @@ var assertTransitivityAsInvariant = (state, first, second, third) => {
|
|
|
1428
1723
|
throw error;
|
|
1429
1724
|
}
|
|
1430
1725
|
};
|
|
1726
|
+
var validateAdjacentLeafOrdering = (state, previous, cursor) => {
|
|
1727
|
+
const prevMax = getNodeMaxKey(previous);
|
|
1728
|
+
const currentMin = nodeMinKey(cursor);
|
|
1729
|
+
if (prevMax === null || currentMin === null) {
|
|
1730
|
+
throw new BTreeInvariantError(
|
|
1731
|
+
"Non-empty tree leaf chain contains empty leaf node."
|
|
1732
|
+
);
|
|
1733
|
+
}
|
|
1734
|
+
if (compareNodeKeys(
|
|
1735
|
+
state.compareKeys,
|
|
1736
|
+
prevMax.key,
|
|
1737
|
+
prevMax.sequence,
|
|
1738
|
+
currentMin.key,
|
|
1739
|
+
currentMin.sequence
|
|
1740
|
+
) > 0) {
|
|
1741
|
+
throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
|
|
1742
|
+
}
|
|
1743
|
+
const prevCount = leafEntryCount(previous);
|
|
1744
|
+
const curCount = leafEntryCount(cursor);
|
|
1745
|
+
if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
|
|
1746
|
+
leafEntryAt(previous, prevCount - 1).key,
|
|
1747
|
+
leafEntryAt(cursor, 0).key
|
|
1748
|
+
) === 0) {
|
|
1749
|
+
throw new BTreeInvariantError(
|
|
1750
|
+
"Duplicate user key detected across adjacent leaves with uniqueness policy."
|
|
1751
|
+
);
|
|
1752
|
+
}
|
|
1753
|
+
};
|
|
1431
1754
|
var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
1432
1755
|
if (!isLeafNode(cursor)) {
|
|
1433
1756
|
throw new BTreeInvariantError("Leaf linkage cursor reached non-leaf node.");
|
|
@@ -1439,22 +1762,7 @@ var validateLeafChainStep = (state, cursor, previous, visited) => {
|
|
|
1439
1762
|
throw new BTreeInvariantError("Leaf prev pointer mismatch.");
|
|
1440
1763
|
}
|
|
1441
1764
|
if (previous !== null && isLeafNode(previous)) {
|
|
1442
|
-
|
|
1443
|
-
const currentMin = nodeMinKey(cursor);
|
|
1444
|
-
if (prevMax === null || currentMin === null) {
|
|
1445
|
-
throw new BTreeInvariantError("Non-empty tree leaf chain contains empty leaf node.");
|
|
1446
|
-
}
|
|
1447
|
-
if (compareNodeKeys(state.compareKeys, prevMax.key, prevMax.sequence, currentMin.key, currentMin.sequence) > 0) {
|
|
1448
|
-
throw new BTreeInvariantError("Adjacent leaf key ranges are out of order.");
|
|
1449
|
-
}
|
|
1450
|
-
const prevCount = leafEntryCount(previous);
|
|
1451
|
-
const curCount = leafEntryCount(cursor);
|
|
1452
|
-
if (state.duplicateKeys !== "allow" && prevCount > 0 && curCount > 0 && state.compareKeys(
|
|
1453
|
-
leafEntryAt(previous, prevCount - 1).key,
|
|
1454
|
-
leafEntryAt(cursor, 0).key
|
|
1455
|
-
) === 0) {
|
|
1456
|
-
throw new BTreeInvariantError("Duplicate user key detected across adjacent leaves with uniqueness policy.");
|
|
1457
|
-
}
|
|
1765
|
+
validateAdjacentLeafOrdering(state, previous, cursor);
|
|
1458
1766
|
}
|
|
1459
1767
|
};
|
|
1460
1768
|
var validateLeafLinks = (state, expectedLeafCount) => {
|
|
@@ -1463,7 +1771,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1463
1771
|
throw new BTreeInvariantError("Empty tree root must be a leaf node.");
|
|
1464
1772
|
}
|
|
1465
1773
|
if (state.leftmostLeaf !== state.root || state.rightmostLeaf !== state.root) {
|
|
1466
|
-
throw new BTreeInvariantError(
|
|
1774
|
+
throw new BTreeInvariantError(
|
|
1775
|
+
"Empty tree leaf pointers must reference root leaf."
|
|
1776
|
+
);
|
|
1467
1777
|
}
|
|
1468
1778
|
return;
|
|
1469
1779
|
}
|
|
@@ -1488,7 +1798,9 @@ var validateLeafLinks = (state, expectedLeafCount) => {
|
|
|
1488
1798
|
throw new BTreeInvariantError("Rightmost leaf pointer mismatch.");
|
|
1489
1799
|
}
|
|
1490
1800
|
if (leafCount !== expectedLeafCount) {
|
|
1491
|
-
throw new BTreeInvariantError(
|
|
1801
|
+
throw new BTreeInvariantError(
|
|
1802
|
+
"Leaf chain count mismatch with tree traversal count."
|
|
1803
|
+
);
|
|
1492
1804
|
}
|
|
1493
1805
|
};
|
|
1494
1806
|
|
|
@@ -1515,7 +1827,9 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1515
1827
|
leafEntryAt(node, index - 1).key,
|
|
1516
1828
|
leafEntryAt(node, index).key
|
|
1517
1829
|
) === 0) {
|
|
1518
|
-
throw new BTreeInvariantError(
|
|
1830
|
+
throw new BTreeInvariantError(
|
|
1831
|
+
"Duplicate user key detected in tree with uniqueness policy."
|
|
1832
|
+
);
|
|
1519
1833
|
}
|
|
1520
1834
|
}
|
|
1521
1835
|
}
|
|
@@ -1523,12 +1837,7 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1523
1837
|
const first = leafEntryAt(node, index - 2);
|
|
1524
1838
|
const second = leafEntryAt(node, index - 1);
|
|
1525
1839
|
const third = leafEntryAt(node, index);
|
|
1526
|
-
assertTransitivityAsInvariant(
|
|
1527
|
-
state,
|
|
1528
|
-
first.key,
|
|
1529
|
-
second.key,
|
|
1530
|
-
third.key
|
|
1531
|
-
);
|
|
1840
|
+
assertTransitivityAsInvariant(state, first.key, second.key, third.key);
|
|
1532
1841
|
}
|
|
1533
1842
|
if (count > state.maxLeafEntries) {
|
|
1534
1843
|
throw new BTreeInvariantError("Leaf node exceeds maximum occupancy.");
|
|
@@ -1537,9 +1846,14 @@ var validateLeafNodeOrdering = (state, node) => {
|
|
|
1537
1846
|
var validateLeafNode = (state, node, depth) => {
|
|
1538
1847
|
validateLeafNodeOrdering(state, node);
|
|
1539
1848
|
const count = leafEntryCount(node);
|
|
1540
|
-
|
|
1849
|
+
let baseMinLeaf = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxLeaf / 2) : state.minLeafEntries;
|
|
1850
|
+
if (state.deleteRebalancePolicy === "lazy") {
|
|
1851
|
+
baseMinLeaf = applyLazyThreshold(baseMinLeaf);
|
|
1852
|
+
}
|
|
1541
1853
|
if (node !== state.root && count < baseMinLeaf) {
|
|
1542
|
-
throw new BTreeInvariantError(
|
|
1854
|
+
throw new BTreeInvariantError(
|
|
1855
|
+
"Non-root leaf node violates minimum occupancy."
|
|
1856
|
+
);
|
|
1543
1857
|
}
|
|
1544
1858
|
const first = count === 0 ? null : leafEntryAt(node, 0);
|
|
1545
1859
|
const last = count === 0 ? null : leafEntryAt(node, count - 1);
|
|
@@ -1561,76 +1875,110 @@ var validateBranchStructure = (state, node) => {
|
|
|
1561
1875
|
}
|
|
1562
1876
|
const baseMinBranch = state.autoScale ? Math.ceil(computeAutoScaleTier(0).maxBranch / 2) : state.minBranchChildren;
|
|
1563
1877
|
if (node !== state.root && liveCount < baseMinBranch) {
|
|
1564
|
-
throw new BTreeInvariantError(
|
|
1878
|
+
throw new BTreeInvariantError(
|
|
1879
|
+
"Non-root branch node violates minimum occupancy."
|
|
1880
|
+
);
|
|
1565
1881
|
}
|
|
1566
1882
|
if (liveCount > state.maxBranchChildren) {
|
|
1567
1883
|
throw new BTreeInvariantError("Branch node exceeds maximum occupancy.");
|
|
1568
1884
|
}
|
|
1569
1885
|
if (node.keys.length !== node.children.length) {
|
|
1570
|
-
throw new BTreeInvariantError(
|
|
1886
|
+
throw new BTreeInvariantError(
|
|
1887
|
+
"Branch keys array length does not match children array length."
|
|
1888
|
+
);
|
|
1571
1889
|
}
|
|
1572
1890
|
};
|
|
1573
1891
|
var validateBranchChild = (state, node, childIndex, depth) => {
|
|
1574
1892
|
const child = node.children[childIndex];
|
|
1575
1893
|
if (child.parent !== node) {
|
|
1576
|
-
throw new BTreeInvariantError(
|
|
1894
|
+
throw new BTreeInvariantError(
|
|
1895
|
+
"Child-parent pointer mismatch in branch node."
|
|
1896
|
+
);
|
|
1577
1897
|
}
|
|
1578
1898
|
if (child.indexInParent !== childIndex) {
|
|
1579
|
-
throw new BTreeInvariantError(
|
|
1899
|
+
throw new BTreeInvariantError(
|
|
1900
|
+
"Child indexInParent does not match actual position in parent."
|
|
1901
|
+
);
|
|
1580
1902
|
}
|
|
1581
1903
|
const childValidation = validateNode(state, child, depth + 1);
|
|
1582
1904
|
if (childValidation.minKey === null || childValidation.maxKey === null) {
|
|
1583
|
-
throw new BTreeInvariantError(
|
|
1905
|
+
throw new BTreeInvariantError(
|
|
1906
|
+
"Branch child must not be empty in non-root branch tree."
|
|
1907
|
+
);
|
|
1584
1908
|
}
|
|
1585
1909
|
const cachedMinKey = node.keys[childIndex];
|
|
1586
1910
|
const actualMinKey = nodeMinKey(child);
|
|
1587
|
-
if (actualMinKey === null || compareNodeKeys(
|
|
1588
|
-
|
|
1911
|
+
if (actualMinKey === null || compareNodeKeys(
|
|
1912
|
+
state.compareKeys,
|
|
1913
|
+
cachedMinKey.key,
|
|
1914
|
+
cachedMinKey.sequence,
|
|
1915
|
+
actualMinKey.key,
|
|
1916
|
+
actualMinKey.sequence
|
|
1917
|
+
) !== 0) {
|
|
1918
|
+
throw new BTreeInvariantError(
|
|
1919
|
+
"Branch cached key does not match actual child minimum key."
|
|
1920
|
+
);
|
|
1589
1921
|
}
|
|
1590
1922
|
return childValidation;
|
|
1591
1923
|
};
|
|
1924
|
+
var mergeChildValidation = (state, accumulated, childValidation) => {
|
|
1925
|
+
if (accumulated.leafDepth !== null && childValidation.leafDepth !== null && childValidation.leafDepth !== accumulated.leafDepth) {
|
|
1926
|
+
throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
|
|
1927
|
+
}
|
|
1928
|
+
if (accumulated.leafDepth === null && childValidation.leafDepth !== null) {
|
|
1929
|
+
accumulated.leafDepth = childValidation.leafDepth;
|
|
1930
|
+
}
|
|
1931
|
+
if (accumulated.previousChildMax !== null && compareNodeKeys(
|
|
1932
|
+
state.compareKeys,
|
|
1933
|
+
accumulated.previousChildMax.key,
|
|
1934
|
+
accumulated.previousChildMax.sequence,
|
|
1935
|
+
childValidation.minKey.key,
|
|
1936
|
+
childValidation.minKey.sequence
|
|
1937
|
+
) >= 0) {
|
|
1938
|
+
throw new BTreeInvariantError(
|
|
1939
|
+
"Branch child key ranges are not strictly ordered."
|
|
1940
|
+
);
|
|
1941
|
+
}
|
|
1942
|
+
if (accumulated.minKey === null) accumulated.minKey = childValidation.minKey;
|
|
1943
|
+
accumulated.maxKey = childValidation.maxKey;
|
|
1944
|
+
accumulated.previousChildMax = childValidation.maxKey;
|
|
1945
|
+
accumulated.leafCount += childValidation.leafCount;
|
|
1946
|
+
accumulated.branchCount += childValidation.branchCount;
|
|
1947
|
+
accumulated.entryCount += childValidation.entryCount;
|
|
1948
|
+
};
|
|
1592
1949
|
var validateNode = (state, node, depth) => {
|
|
1593
1950
|
if (isLeafNode(node)) {
|
|
1594
1951
|
return validateLeafNode(state, node, depth);
|
|
1595
1952
|
}
|
|
1596
1953
|
validateBranchStructure(state, node);
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1954
|
+
const acc = {
|
|
1955
|
+
leafDepth: null,
|
|
1956
|
+
leafCount: 0,
|
|
1957
|
+
branchCount: 1,
|
|
1958
|
+
entryCount: 0,
|
|
1959
|
+
minKey: null,
|
|
1960
|
+
maxKey: null,
|
|
1961
|
+
previousChildMax: null
|
|
1962
|
+
};
|
|
1604
1963
|
for (let childIndex = node.childOffset; childIndex < node.children.length; childIndex += 1) {
|
|
1605
1964
|
const childValidation = validateBranchChild(state, node, childIndex, depth);
|
|
1606
|
-
|
|
1607
|
-
throw new BTreeInvariantError("Leaf depth mismatch detected in tree.");
|
|
1608
|
-
}
|
|
1609
|
-
if (leafDepth === null && childValidation.leafDepth !== null) {
|
|
1610
|
-
leafDepth = childValidation.leafDepth;
|
|
1611
|
-
}
|
|
1612
|
-
if (previousChildMax !== null && compareNodeKeys(
|
|
1613
|
-
state.compareKeys,
|
|
1614
|
-
previousChildMax.key,
|
|
1615
|
-
previousChildMax.sequence,
|
|
1616
|
-
childValidation.minKey.key,
|
|
1617
|
-
childValidation.minKey.sequence
|
|
1618
|
-
) >= 0) {
|
|
1619
|
-
throw new BTreeInvariantError("Branch child key ranges are not strictly ordered.");
|
|
1620
|
-
}
|
|
1621
|
-
if (minKey === null) minKey = childValidation.minKey;
|
|
1622
|
-
maxKey = childValidation.maxKey;
|
|
1623
|
-
previousChildMax = childValidation.maxKey;
|
|
1624
|
-
leafCount += childValidation.leafCount;
|
|
1625
|
-
branchCount += childValidation.branchCount;
|
|
1626
|
-
entryCount += childValidation.entryCount;
|
|
1965
|
+
mergeChildValidation(state, acc, childValidation);
|
|
1627
1966
|
}
|
|
1628
|
-
return {
|
|
1967
|
+
return {
|
|
1968
|
+
minKey: acc.minKey,
|
|
1969
|
+
maxKey: acc.maxKey,
|
|
1970
|
+
leafDepth: acc.leafDepth,
|
|
1971
|
+
leafCount: acc.leafCount,
|
|
1972
|
+
branchCount: acc.branchCount,
|
|
1973
|
+
entryCount: acc.entryCount
|
|
1974
|
+
};
|
|
1629
1975
|
};
|
|
1630
1976
|
var assertInvariants = (state) => {
|
|
1631
1977
|
const validation = validateNode(state, state.root, 0);
|
|
1632
1978
|
if (validation.entryCount !== state.entryCount) {
|
|
1633
|
-
throw new BTreeInvariantError(
|
|
1979
|
+
throw new BTreeInvariantError(
|
|
1980
|
+
"Index entry count mismatch between tree traversal and tracked state."
|
|
1981
|
+
);
|
|
1634
1982
|
}
|
|
1635
1983
|
validateLeafLinks(state, validation.leafCount);
|
|
1636
1984
|
};
|
|
@@ -1684,44 +2032,56 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1684
2032
|
return putManyEntries(this.state, entries);
|
|
1685
2033
|
}
|
|
1686
2034
|
remove(key) {
|
|
1687
|
-
|
|
2035
|
+
const entry = removeFirstMatchingEntry(this.state, key);
|
|
2036
|
+
if (entry === null) return null;
|
|
2037
|
+
return freezeEntry(entry);
|
|
1688
2038
|
}
|
|
1689
2039
|
removeById(entryId) {
|
|
1690
2040
|
if (this.state.entryKeys === null) {
|
|
1691
2041
|
throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
|
|
1692
2042
|
}
|
|
1693
|
-
|
|
2043
|
+
const entry = removeEntryById(this.state, entryId);
|
|
2044
|
+
if (entry === null) return null;
|
|
2045
|
+
return freezeEntry(entry);
|
|
1694
2046
|
}
|
|
1695
2047
|
peekById(entryId) {
|
|
1696
2048
|
if (this.state.entryKeys === null) {
|
|
1697
2049
|
throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
|
|
1698
2050
|
}
|
|
1699
|
-
|
|
2051
|
+
const entry = peekEntryById(this.state, entryId);
|
|
2052
|
+
if (entry === null) return null;
|
|
2053
|
+
return freezeEntry(entry);
|
|
1700
2054
|
}
|
|
1701
2055
|
updateById(entryId, value) {
|
|
1702
2056
|
if (this.state.entryKeys === null) {
|
|
1703
2057
|
throw new BTreeValidationError("Requires enableEntryIdLookup: true.");
|
|
1704
2058
|
}
|
|
1705
|
-
|
|
2059
|
+
const entry = updateEntryById(this.state, entryId, value);
|
|
2060
|
+
if (entry === null) return null;
|
|
2061
|
+
return freezeEntry(entry);
|
|
1706
2062
|
}
|
|
1707
2063
|
popFirst() {
|
|
1708
|
-
|
|
2064
|
+
const entry = popFirstEntry(this.state);
|
|
2065
|
+
if (entry === null) return null;
|
|
2066
|
+
return freezeEntry(entry);
|
|
1709
2067
|
}
|
|
1710
2068
|
peekFirst() {
|
|
1711
2069
|
if (this.state.entryCount === 0) {
|
|
1712
2070
|
return null;
|
|
1713
2071
|
}
|
|
1714
|
-
return leafEntryAt(this.state.leftmostLeaf, 0);
|
|
2072
|
+
return freezeEntry(leafEntryAt(this.state.leftmostLeaf, 0));
|
|
1715
2073
|
}
|
|
1716
2074
|
peekLast() {
|
|
1717
2075
|
if (this.state.entryCount === 0) {
|
|
1718
2076
|
return null;
|
|
1719
2077
|
}
|
|
1720
2078
|
const leaf = this.state.rightmostLeaf;
|
|
1721
|
-
return leafEntryAt(leaf, leafEntryCount(leaf) - 1);
|
|
2079
|
+
return freezeEntry(leafEntryAt(leaf, leafEntryCount(leaf) - 1));
|
|
1722
2080
|
}
|
|
1723
2081
|
popLast() {
|
|
1724
|
-
|
|
2082
|
+
const entry = popLastEntry(this.state);
|
|
2083
|
+
if (entry === null) return null;
|
|
2084
|
+
return freezeEntry(entry);
|
|
1725
2085
|
}
|
|
1726
2086
|
clear() {
|
|
1727
2087
|
const emptyLeaf = createLeafNode([], null);
|
|
@@ -1731,16 +2091,9 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1731
2091
|
this.state.entryCount = 0;
|
|
1732
2092
|
this.state._cursor.leaf = emptyLeaf;
|
|
1733
2093
|
this.state._cursor.index = 0;
|
|
1734
|
-
|
|
1735
|
-
this.state.entryKeys.clear();
|
|
1736
|
-
}
|
|
2094
|
+
this.state.entryKeys?.clear();
|
|
1737
2095
|
if (this.state.autoScale) {
|
|
1738
|
-
|
|
1739
|
-
this.state.maxLeafEntries = tier.maxLeaf;
|
|
1740
|
-
this.state.maxBranchChildren = tier.maxBranch;
|
|
1741
|
-
this.state.minLeafEntries = Math.ceil(tier.maxLeaf / 2);
|
|
1742
|
-
this.state.minBranchChildren = Math.ceil(tier.maxBranch / 2);
|
|
1743
|
-
this.state._nextAutoScaleThreshold = computeNextAutoScaleThreshold(0);
|
|
2096
|
+
resetAutoScaleToTier0(this.state);
|
|
1744
2097
|
}
|
|
1745
2098
|
}
|
|
1746
2099
|
get(key) {
|
|
@@ -1754,12 +2107,12 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1754
2107
|
findFirst(key) {
|
|
1755
2108
|
const found = findFirstMatchingUserKey(this.state, key);
|
|
1756
2109
|
if (found === null) return null;
|
|
1757
|
-
return leafEntryAt(found.leaf, found.index);
|
|
2110
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1758
2111
|
}
|
|
1759
2112
|
findLast(key) {
|
|
1760
2113
|
const found = findLastMatchingUserKey(this.state, key);
|
|
1761
2114
|
if (found === null) return null;
|
|
1762
|
-
return leafEntryAt(found.leaf, found.index);
|
|
2115
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1763
2116
|
}
|
|
1764
2117
|
nextHigherKey(key) {
|
|
1765
2118
|
return findNextHigherKey(this.state, key);
|
|
@@ -1770,7 +2123,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1770
2123
|
getPairOrNextLower(key) {
|
|
1771
2124
|
const found = findPairOrNextLower(this.state, key);
|
|
1772
2125
|
if (found === null) return null;
|
|
1773
|
-
return leafEntryAt(found.leaf, found.index);
|
|
2126
|
+
return freezeEntry(leafEntryAt(found.leaf, found.index));
|
|
1774
2127
|
}
|
|
1775
2128
|
count(startKey, endKey, options) {
|
|
1776
2129
|
return countRangeEntries(this.state, startKey, endKey, options);
|
|
@@ -1779,14 +2132,14 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1779
2132
|
return deleteRangeEntries(this.state, startKey, endKey, options);
|
|
1780
2133
|
}
|
|
1781
2134
|
range(startKey, endKey, options) {
|
|
1782
|
-
return
|
|
2135
|
+
return rangeQueryPublicEntries(this.state, startKey, endKey, options);
|
|
1783
2136
|
}
|
|
1784
2137
|
*entries() {
|
|
1785
2138
|
let leaf = this.state.leftmostLeaf;
|
|
1786
2139
|
while (leaf !== null) {
|
|
1787
2140
|
const count = leafEntryCount(leaf);
|
|
1788
2141
|
for (let i = 0; i < count; i += 1) {
|
|
1789
|
-
yield leafEntryAt(leaf, i);
|
|
2142
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1790
2143
|
}
|
|
1791
2144
|
leaf = leaf.next;
|
|
1792
2145
|
}
|
|
@@ -1796,7 +2149,7 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1796
2149
|
while (leaf !== null) {
|
|
1797
2150
|
const count = leafEntryCount(leaf);
|
|
1798
2151
|
for (let i = count - 1; i >= 0; i -= 1) {
|
|
1799
|
-
yield leafEntryAt(leaf, i);
|
|
2152
|
+
yield freezeEntry(leafEntryAt(leaf, i));
|
|
1800
2153
|
}
|
|
1801
2154
|
leaf = leaf.prev;
|
|
1802
2155
|
}
|
|
@@ -1814,48 +2167,26 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1814
2167
|
[Symbol.iterator]() {
|
|
1815
2168
|
return this.entries();
|
|
1816
2169
|
}
|
|
2170
|
+
forEachRange(startKey, endKey, callback, options) {
|
|
2171
|
+
forEachRangeEntries(this.state, startKey, endKey, callback, options);
|
|
2172
|
+
}
|
|
1817
2173
|
forEach(callback, thisArg) {
|
|
1818
|
-
|
|
1819
|
-
while (leaf !== null) {
|
|
1820
|
-
const count = leafEntryCount(leaf);
|
|
1821
|
-
for (let i = 0; i < count; i += 1) {
|
|
1822
|
-
callback.call(thisArg, leafEntryAt(leaf, i));
|
|
1823
|
-
}
|
|
1824
|
-
leaf = leaf.next;
|
|
1825
|
-
}
|
|
2174
|
+
forEachEntry(this.state, callback, thisArg);
|
|
1826
2175
|
}
|
|
1827
2176
|
snapshot() {
|
|
1828
|
-
|
|
1829
|
-
let leaf = this.state.leftmostLeaf;
|
|
1830
|
-
let writeIdx = 0;
|
|
1831
|
-
while (leaf !== null) {
|
|
1832
|
-
const count = leafEntryCount(leaf);
|
|
1833
|
-
for (let i = 0; i < count; i += 1) {
|
|
1834
|
-
result[writeIdx++] = leafEntryAt(leaf, i);
|
|
1835
|
-
}
|
|
1836
|
-
leaf = leaf.next;
|
|
1837
|
-
}
|
|
1838
|
-
return result;
|
|
2177
|
+
return snapshotEntries(this.state);
|
|
1839
2178
|
}
|
|
1840
2179
|
clone() {
|
|
1841
|
-
const cloned = new _InMemoryBTree(
|
|
2180
|
+
const cloned = new _InMemoryBTree(
|
|
2181
|
+
buildConfigFromState(this.state)
|
|
2182
|
+
);
|
|
1842
2183
|
applyAutoScaleCapacitySnapshot(
|
|
1843
2184
|
cloned.state,
|
|
1844
2185
|
this.state.maxLeafEntries,
|
|
1845
2186
|
this.state.maxBranchChildren
|
|
1846
2187
|
);
|
|
1847
2188
|
if (this.state.entryCount > 0) {
|
|
1848
|
-
|
|
1849
|
-
let leaf = this.state.leftmostLeaf;
|
|
1850
|
-
let writeIdx = 0;
|
|
1851
|
-
while (leaf !== null) {
|
|
1852
|
-
const count = leafEntryCount(leaf);
|
|
1853
|
-
for (let i = 0; i < count; i += 1) {
|
|
1854
|
-
pairs[writeIdx++] = leafEntryAt(leaf, i);
|
|
1855
|
-
}
|
|
1856
|
-
leaf = leaf.next;
|
|
1857
|
-
}
|
|
1858
|
-
cloned.putMany(pairs);
|
|
2189
|
+
cloned.putMany(collectInternalEntries(this.state));
|
|
1859
2190
|
}
|
|
1860
2191
|
return cloned;
|
|
1861
2192
|
}
|
|
@@ -1864,26 +2195,19 @@ var InMemoryBTree = class _InMemoryBTree {
|
|
|
1864
2195
|
}
|
|
1865
2196
|
static fromJSON(json, compareKeys) {
|
|
1866
2197
|
validateBTreeJSON(json);
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
throw new BTreeValidationError("fromJSON: entries not sorted.");
|
|
1872
|
-
}
|
|
1873
|
-
if (strict && cmp === 0) {
|
|
1874
|
-
throw new BTreeValidationError(
|
|
1875
|
-
'fromJSON: duplicate keys require duplicateKeys "allow".'
|
|
1876
|
-
);
|
|
1877
|
-
}
|
|
1878
|
-
}
|
|
1879
|
-
const tree = new _InMemoryBTree(buildConfigFromJSON(json, compareKeys));
|
|
2198
|
+
validateBTreeJSONSortOrder(json, compareKeys);
|
|
2199
|
+
const tree = new _InMemoryBTree(
|
|
2200
|
+
buildConfigFromJSON(json, compareKeys)
|
|
2201
|
+
);
|
|
1880
2202
|
applyAutoScaleCapacitySnapshot(
|
|
1881
2203
|
tree.state,
|
|
1882
2204
|
json.config.maxLeafEntries,
|
|
1883
2205
|
json.config.maxBranchChildren
|
|
1884
2206
|
);
|
|
1885
2207
|
if (json.entries.length > 0) {
|
|
1886
|
-
const pairs = new Array(
|
|
2208
|
+
const pairs = new Array(
|
|
2209
|
+
json.entries.length
|
|
2210
|
+
);
|
|
1887
2211
|
for (let i = 0; i < json.entries.length; i += 1) {
|
|
1888
2212
|
pairs[i] = { key: json.entries[i][0], value: json.entries[i][1] };
|
|
1889
2213
|
}
|