UNPKG

sonic-forest

Version:

High-performance (binary) tree and sorted map implementation (AVL, Splay, Radix, Red-Black)

187 lines (186 loc) 6.89 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.print = exports.toRecord = exports.remove = exports.findWithParents = exports.find = exports.insert = void 0; const BinaryTrieNode_1 = require("./BinaryTrieNode"); const Slice_1 = require("./Slice"); const util_1 = require("../util"); const insert = (root, path, value) => { let curr = root; let k = Slice_1.Slice.fromUint8Array(path); main: while (curr) { let child = curr.children; if (!child) { curr.children = new BinaryTrieNode_1.BinaryTrieNode(k, value); return 1; } const firstByte = k.length > 0 ? k.at(0) : -1; let prevChild = undefined; let cmp = false; child: while (child) { prevChild = child; const childFirstByte = child.k.length > 0 ? child.k.at(0) : -1; if (childFirstByte === firstByte) { const commonPrefixLength = child.k.getCommonPrefixLength(k); const isChildKContained = commonPrefixLength === child.k.length; const isKContained = commonPrefixLength === k.length; const areKeysEqual = isChildKContained && isKContained; if (areKeysEqual) { child.v = value; return 0; } if (isChildKContained) { k = k.substring(commonPrefixLength); curr = child; continue main; } if (isKContained) { const newChild = new BinaryTrieNode_1.BinaryTrieNode(child.k.substring(commonPrefixLength), child.v); newChild.children = child.children; child.k = k.substring(0, commonPrefixLength); child.v = value; child.children = newChild; return 1; } if (commonPrefixLength) { const newChild = new BinaryTrieNode_1.BinaryTrieNode(child.k.substring(commonPrefixLength), child.v); newChild.children = child.children; child.k = child.k.substring(0, commonPrefixLength); child.v = undefined; child.children = newChild; curr = child; k = k.substring(commonPrefixLength); continue main; } } cmp = childFirstByte > firstByte; if (cmp) child = child.l; else child = child.r; } if (prevChild) { const node = new BinaryTrieNode_1.BinaryTrieNode(k, value); if (cmp) (0, util_1.insertLeft)(node, prevChild); else (0, util_1.insertRight)(node, prevChild); return 1; } break; } return 0; }; exports.insert = insert; const find = (node, key) => { if (key.length === 0) return node; const keySlice = Slice_1.Slice.fromUint8Array(key); let offset = 0; while (node) { const remainingKey = keySlice.substring(offset); if (remainingKey.length === 0) return node; const child = (0, util_1.findOrNextLower)(node.children, remainingKey, (a, b) => { const aByte = a.length > 0 ? a.at(0) : -1; const bByte = b.length > 0 ? b.at(0) : -1; return aByte > bByte ? 1 : -1; }); if (!child) return undefined; const childKey = child.k; const childKeyLength = childKey.length; let commonPrefixLength = 0; const limit = Math.min(childKeyLength, remainingKey.length); for (; commonPrefixLength < limit && childKey.at(commonPrefixLength) === remainingKey.at(commonPrefixLength); commonPrefixLength++) ; if (!commonPrefixLength) return undefined; offset += commonPrefixLength; if (offset === key.length) return child; if (commonPrefixLength < childKeyLength) return undefined; node = child; } return undefined; }; exports.find = find; const findWithParents = (node, key) => { if (key.length === 0) return undefined; const list = [node]; const keySlice = Slice_1.Slice.fromUint8Array(key); let offset = 0; while (node) { const remainingKey = keySlice.substring(offset); const child = (0, util_1.findOrNextLower)(node.children, remainingKey, (a, b) => { const aByte = a.length > 0 ? a.at(0) : -1; const bByte = b.length > 0 ? b.at(0) : -1; return aByte > bByte ? 1 : -1; }); if (!child) return undefined; const childKey = child.k; const childKeyLength = childKey.length; let commonPrefixLength = 0; const limit = Math.min(childKeyLength, remainingKey.length); for (; commonPrefixLength < limit && childKey.at(commonPrefixLength) === remainingKey.at(commonPrefixLength); commonPrefixLength++) ; if (!commonPrefixLength) return undefined; offset += commonPrefixLength; if (commonPrefixLength < childKeyLength) return undefined; list.push(child); if (offset === key.length) return list; node = child; } return undefined; }; exports.findWithParents = findWithParents; const remove = (root, key) => { if (key.length === 0) { const deleted = root.v !== undefined; root.v = undefined; return deleted; } const list = (0, exports.findWithParents)(root, key); if (!list) return false; const length = list.length; const lastIndex = length - 1; const last = list[lastIndex]; const deleted = last.v !== undefined; last.v = undefined; for (let i = lastIndex; i >= 1; i--) { const child = list[i]; const parent = list[i - 1]; if (child.v || child.children) break; parent.children = (0, util_1.remove)(parent.children, child); } return deleted; }; exports.remove = remove; const toRecord = (node, prefix = new Uint8Array(), record = {}) => { if (!node) return record; const currentPrefix = new Uint8Array([...prefix, ...node.k.toUint8Array()]); if (node.v !== undefined) { const key = Array.from(currentPrefix).join(','); record[key] = node.v; } let child = (0, util_1.first)(node.children); if (!child) return record; do (0, exports.toRecord)(child, currentPrefix, record); while ((child = (0, util_1.next)(child))); return record; }; exports.toRecord = toRecord; const print = (node, tab = '') => { return node.toString(tab); }; exports.print = print;