UNPKG

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
"use strict"; 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;