UNPKG

sorted-btree

Version:

A sorted list of key-value pairs in a fast, typed in-memory B+ tree with a powerful API.

189 lines (188 loc) 6.94 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.noop = exports.moveTo = exports.getKey = exports.createCursor = exports.moveForwardOne = void 0; /** * 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) { var leaf = cur.leaf; var 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]; } exports.moveForwardOne = moveForwardOne; /** * Create a cursor pointing to the leftmost key of the supplied tree. * @internal */ function createCursor(tree, makePayload, onEnterLeaf, onMoveInLeaf, onExitLeaf, onStepUp, onStepDown) { var spine = []; var n = tree._root; while (!n.isLeaf) { var ni = n; var payload = makePayload(); spine.push({ node: ni, childIndex: 0, payload: payload }); n = ni.children[0]; } var leafPayload = makePayload(); var cur = { tree: tree, leaf: n, leafIndex: 0, spine: spine, leafPayload: leafPayload, makePayload: makePayload, onEnterLeaf: onEnterLeaf, onMoveInLeaf: onMoveInLeaf, onExitLeaf: onExitLeaf, onStepUp: onStepUp, onStepDown: onStepDown }; return cur; } exports.createCursor = createCursor; /** * Gets the key at the current cursor position. * @internal */ function getKey(c) { return c.leaf.keys[c.leafIndex]; } exports.getKey = getKey; /** * 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 var cmp = cur.tree._compare; var onMoveInLeaf = cur.onMoveInLeaf; // Fast path: destination within current leaf var leaf = cur.leaf; var leafPayload = cur.leafPayload; var i = leaf.indexOf(targetKey, -1, cmp); var destInLeaf; var targetExactlyReached; if (i < 0) { destInLeaf = ~i; targetExactlyReached = false; } else { if (isInclusive) { destInLeaf = i; targetExactlyReached = true; } else { destInLeaf = i + 1; targetExactlyReached = false; } } var 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 var spine = cur.spine; var initialSpineLength = spine.length; var descentLevel = -1; var descentIndex = -1; for (var s = initialSpineLength - 1; s >= 0; s--) { var parent = spine[s].node; var indexOf = parent.indexOf(targetKey, -1, cmp); var stepDownIndex = void 0; 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 var startIndex = cur.leafIndex; cur.onExitLeaf(leaf, leafPayload, startIndex, startedEqual, cur); var onStepUp = cur.onStepUp; if (descentLevel < 0) { // No descent point; step up all the way; last callback gets infinity for (var depth = initialSpineLength - 1; depth >= 0; depth--) { var entry_1 = spine[depth]; var sd = depth === 0 ? Number.POSITIVE_INFINITY : Number.NaN; onStepUp(entry_1.node, initialSpineLength - depth, entry_1.payload, entry_1.childIndex, depth, sd, cur, other); } return [true, false]; } // Step up through ancestors above the descentLevel for (var depth = initialSpineLength - 1; depth > descentLevel; depth--) { var entry_2 = spine[depth]; onStepUp(entry_2.node, initialSpineLength - depth, entry_2.payload, entry_2.childIndex, depth, Number.NaN, cur, other); } var entry = spine[descentLevel]; onStepUp(entry.node, initialSpineLength - descentLevel, entry.payload, entry.childIndex, descentLevel, descentIndex, cur, other); entry.childIndex = descentIndex; var onStepDown = cur.onStepDown; var makePayload = cur.makePayload; // Descend, invoking onStepDown and creating payloads var height = initialSpineLength - descentLevel - 1; // calculate height before changing length spine.length = descentLevel + 1; var node = spine[descentLevel].node.children[descentIndex]; while (!node.isLeaf) { var ni = node; var keys = ni.keys; var stepDownIndex = ni.indexOf(targetKey, 0, cmp); if (!isInclusive && stepDownIndex < keys.length && cmp(keys[stepDownIndex], targetKey) === 0) stepDownIndex++; var payload = makePayload(); var spineIndex = spine.length; spine.push({ node: ni, childIndex: stepDownIndex, payload: payload }); onStepDown(ni, height, spineIndex, stepDownIndex, cur, other); node = ni.children[stepDownIndex]; height -= 1; } // Enter destination leaf var idx = node.indexOf(targetKey, -1, cmp); var 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]; } exports.moveTo = moveTo; /** * A no-operation function. * @internal */ function noop() { } exports.noop = noop;