sorted-btree
Version:
A sorted list of key-value pairs in a fast, typed in-memory B+ tree with a powerful API.
114 lines (113 loc) • 5.36 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.bulkLoadRoot = exports.bulkLoad = void 0;
var b_tree_1 = __importStar(require("../b+tree"));
var shared_1 = require("./shared");
/**
* Loads a B-Tree from a sorted list of entries in bulk. This is faster than inserting
* entries one at a time, and produces a more optimally balanced tree.
* Time and space complexity: O(n).
* @param keys Keys to load, sorted in strictly ascending order.
* @param values Values corresponding to each key.
* @param maxNodeSize The branching factor (maximum node size) for the resulting tree.
* @param compare Function to compare keys.
* @param loadFactor Desired load factor for created leaves. Must be between 0.5 and 1.0.
* @returns A new BTree containing the given entries.
* @throws Error if the entries are not sorted by key in strictly ascending order (duplicates disallowed) or if the load factor is out of the allowed range.
*/
function bulkLoad(keys, values, maxNodeSize, compare, loadFactor) {
if (loadFactor === void 0) { loadFactor = 0.8; }
var root = bulkLoadRoot(keys, values, maxNodeSize, compare, loadFactor);
var tree = new b_tree_1.default(undefined, compare, maxNodeSize);
var target = tree;
target._root = root;
return tree;
}
exports.bulkLoad = bulkLoad;
/**
* Bulk loads, returns the root node of the resulting tree.
* @internal
*/
function bulkLoadRoot(keys, values, maxNodeSize, compare, loadFactor) {
if (loadFactor === void 0) { loadFactor = 0.8; }
if (loadFactor < 0.5 || loadFactor > 1.0)
throw new Error("bulkLoad: loadFactor must be between 0.5 and 1.0");
if (keys.length !== values.length)
throw new Error("bulkLoad: keys and values arrays must be the same length");
maxNodeSize = (0, b_tree_1.fixMaxSize)(maxNodeSize);
// Verify keys are sorted
var totalPairs = keys.length;
if (totalPairs > 1) {
var previousKey = keys[0];
for (var i = 1; i < totalPairs; i++) {
var key = keys[i];
if (compare(previousKey, key) >= 0)
throw new Error("bulkLoad: keys must be sorted in strictly ascending order");
previousKey = key;
}
}
// Get ALL the leaf nodes with which the tree will be populated
var currentNodes = [];
(0, shared_1.makeLeavesFrom)(keys, values, maxNodeSize, loadFactor, currentNodes.push.bind(currentNodes));
if (currentNodes.length === 0)
return new b_tree_1.BNode();
var targetNodeSize = Math.ceil(maxNodeSize * loadFactor);
var isExactlyHalf = targetNodeSize === maxNodeSize / 2;
var minSize = Math.floor(maxNodeSize / 2);
for (var nextLevel = void 0; currentNodes.length > 1; currentNodes = nextLevel) {
var nodeCount = currentNodes.length;
if (nodeCount <= maxNodeSize && (nodeCount !== maxNodeSize || !isExactlyHalf)) {
currentNodes = [new b_tree_1.BNodeInternal(currentNodes, (0, b_tree_1.sumChildSizes)(currentNodes))];
break;
}
var nextLevelCount = Math.ceil(nodeCount / targetNodeSize);
(0, b_tree_1.check)(nextLevelCount > 1);
nextLevel = new Array(nextLevelCount);
var remainingNodes = nodeCount;
var remainingParents = nextLevelCount;
var childIndex = 0;
for (var i = 0; i < nextLevelCount; i++) {
var chunkSize = Math.ceil(remainingNodes / remainingParents);
var children = new Array(chunkSize);
var size = 0;
for (var j = 0; j < chunkSize; j++) {
var child = currentNodes[childIndex++];
children[j] = child;
size += child.size();
}
remainingNodes -= chunkSize;
remainingParents--;
nextLevel[i] = new b_tree_1.BNodeInternal(children, size);
}
// If last node is underfilled, balance with left sibling
var secondLastNode = nextLevel[nextLevelCount - 2];
var lastNode = nextLevel[nextLevelCount - 1];
while (lastNode.children.length < minSize)
lastNode.takeFromLeft(secondLastNode);
}
return currentNodes[0];
}
exports.bulkLoadRoot = bulkLoadRoot;