@tylerbu/sorted-btree-es6
Version:
A sorted list of key-value pairs in a fast, typed in-memory B+ tree with a powerful API.
180 lines (179 loc) • 6.51 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.moveForwardOne = moveForwardOne;
exports.createCursor = createCursor;
exports.getKey = getKey;
exports.moveTo = moveTo;
exports.noop = noop;
/**
* Walks the cursor forward by one key.
* Returns true if end-of-tree was reached (cursor not structurally mutated).
* Optimized for this case over the more general `moveTo` function.
* @internal
*/
function moveForwardOne(cur, other) {
const leaf = cur.leaf;
const nextIndex = cur.leafIndex + 1;
if (nextIndex < leaf.keys.length) {
// Still within current leaf
cur.onMoveInLeaf(leaf, cur.leafPayload, cur.leafIndex, nextIndex, true);
cur.leafIndex = nextIndex;
return false;
}
// If our optimized step within leaf failed, use full moveTo logic
// Pass isInclusive=false to ensure we walk forward to the key exactly after the current
return moveTo(cur, other, getKey(cur), false, true)[0];
}
/**
* Create a cursor pointing to the leftmost key of the supplied tree.
* @internal
*/
function createCursor(tree, makePayload, onEnterLeaf, onMoveInLeaf, onExitLeaf, onStepUp, onStepDown) {
const spine = [];
let n = tree._root;
while (!n.isLeaf) {
const ni = n;
const payload = makePayload();
spine.push({ node: ni, childIndex: 0, payload });
n = ni.children[0];
}
const leafPayload = makePayload();
const cur = {
tree, leaf: n, leafIndex: 0, spine, leafPayload, makePayload: makePayload,
onEnterLeaf, onMoveInLeaf, onExitLeaf, onStepUp, onStepDown
};
return cur;
}
/**
* Gets the key at the current cursor position.
* @internal
*/
function getKey(c) {
return c.leaf.keys[c.leafIndex];
}
/**
* Move cursor strictly forward to the first key >= (inclusive) or > (exclusive) target.
* Returns a boolean indicating if end-of-tree was reached (cursor not structurally mutated).
* Also returns a boolean indicating if the target key was landed on exactly.
* @internal
*/
function moveTo(cur, other, targetKey, isInclusive, startedEqual) {
// Cache for perf
const cmp = cur.tree._compare;
const onMoveInLeaf = cur.onMoveInLeaf;
// Fast path: destination within current leaf
const leaf = cur.leaf;
const leafPayload = cur.leafPayload;
const i = leaf.indexOf(targetKey, -1, cmp);
let destInLeaf;
let targetExactlyReached;
if (i < 0) {
destInLeaf = ~i;
targetExactlyReached = false;
}
else {
if (isInclusive) {
destInLeaf = i;
targetExactlyReached = true;
}
else {
destInLeaf = i + 1;
targetExactlyReached = false;
}
}
const leafKeyCount = leaf.keys.length;
if (destInLeaf < leafKeyCount) {
onMoveInLeaf(leaf, leafPayload, cur.leafIndex, destInLeaf, startedEqual);
cur.leafIndex = destInLeaf;
return [false, targetExactlyReached];
}
// Find first ancestor with a viable right step
const spine = cur.spine;
const initialSpineLength = spine.length;
let descentLevel = -1;
let descentIndex = -1;
for (let s = initialSpineLength - 1; s >= 0; s--) {
const parent = spine[s].node;
const indexOf = parent.indexOf(targetKey, -1, cmp);
let stepDownIndex;
if (indexOf < 0) {
stepDownIndex = ~indexOf;
}
else {
stepDownIndex = isInclusive ? indexOf : indexOf + 1;
}
// Note: when key not found, indexOf with failXor=0 already returns insertion index
if (stepDownIndex < parent.keys.length) {
descentLevel = s;
descentIndex = stepDownIndex;
break;
}
}
// Exit leaf; even if no spine, we did walk out of it conceptually
const startIndex = cur.leafIndex;
cur.onExitLeaf(leaf, leafPayload, startIndex, startedEqual, cur);
const onStepUp = cur.onStepUp;
if (descentLevel < 0) {
// No descent point; step up all the way; last callback gets infinity
for (let depth = initialSpineLength - 1; depth >= 0; depth--) {
const entry = spine[depth];
const sd = depth === 0 ? Number.POSITIVE_INFINITY : Number.NaN;
onStepUp(entry.node, initialSpineLength - depth, entry.payload, entry.childIndex, depth, sd, cur, other);
}
return [true, false];
}
// Step up through ancestors above the descentLevel
for (let depth = initialSpineLength - 1; depth > descentLevel; depth--) {
const entry = spine[depth];
onStepUp(entry.node, initialSpineLength - depth, entry.payload, entry.childIndex, depth, Number.NaN, cur, other);
}
const entry = spine[descentLevel];
onStepUp(entry.node, initialSpineLength - descentLevel, entry.payload, entry.childIndex, descentLevel, descentIndex, cur, other);
entry.childIndex = descentIndex;
const onStepDown = cur.onStepDown;
const makePayload = cur.makePayload;
// Descend, invoking onStepDown and creating payloads
let height = initialSpineLength - descentLevel - 1; // calculate height before changing length
spine.length = descentLevel + 1;
let node = spine[descentLevel].node.children[descentIndex];
while (!node.isLeaf) {
const ni = node;
const keys = ni.keys;
let stepDownIndex = ni.indexOf(targetKey, 0, cmp);
if (!isInclusive && stepDownIndex < keys.length && cmp(keys[stepDownIndex], targetKey) === 0)
stepDownIndex++;
const payload = makePayload();
const spineIndex = spine.length;
spine.push({ node: ni, childIndex: stepDownIndex, payload });
onStepDown(ni, height, spineIndex, stepDownIndex, cur, other);
node = ni.children[stepDownIndex];
height -= 1;
}
// Enter destination leaf
const idx = node.indexOf(targetKey, -1, cmp);
let destIndex;
if (idx < 0) {
destIndex = ~idx;
targetExactlyReached = false;
}
else {
if (isInclusive) {
destIndex = idx;
targetExactlyReached = true;
}
else {
destIndex = idx + 1;
targetExactlyReached = false;
}
}
cur.leaf = node;
cur.leafPayload = makePayload();
cur.leafIndex = destIndex;
cur.onEnterLeaf(node, destIndex, cur, other);
return [false, targetExactlyReached];
}
/**
* A no-operation function.
* @internal
*/
function noop() { }