sonic-forest
Version:
High-performance (binary) tree and sorted map implementation (AVL, Splay, Radix, Red-Black)
187 lines (186 loc) • 6.89 kB
JavaScript
"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;