d11 theme
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

196 lines
6.1 KiB

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TrieBuilder = exports.buildTrie = void 0;
const trie_1 = require("./trie");
const consolidate_1 = require("./consolidate");
function buildTrie(words) {
return new TrieBuilder(words).build();
}
exports.buildTrie = buildTrie;
class TrieBuilder {
constructor(words) {
this.count = 0;
this.signatures = new Map();
this.cached = new Map();
this.transforms = new Map();
this._eow = Object.freeze({ f: 1 });
/** position 0 of lastPath is always the root */
this.lastPath = [{ s: '', n: { f: undefined, c: undefined } }];
this.tails = new Map([['', this._eow]]);
this._canBeCached(this._eow); // this line is just for coverage reasons
this.signatures.set(this.signature(this._eow), this._eow);
this.cached.set(this._eow, this.count++);
if (words) {
this.insert(words);
}
}
set _root(n) {
this.lastPath[0].n = n;
}
get _root() {
return this.lastPath[0].n;
}
signature(n) {
const isWord = n.f ? '*' : '';
const ref = n.c ? JSON.stringify([...n.c.entries()].map(([k, n]) => [k, this.cached.get(n)])) : '';
return isWord + ref;
}
_canBeCached(n) {
if (!n.c)
return true;
for (const v of n.c) {
if (!this.cached.has(v[1]))
return false;
}
return true;
}
tryCacheFrozen(n) {
if (this.cached.has(n))
return n;
this.cached.set(n, this.count++);
return n;
}
freeze(n) {
if (Object.isFrozen(n))
return n;
// istanbul ignore else
if (n.c) {
const c = [...n.c]
.sort((a, b) => (a[0] < b[0] ? -1 : 1))
.map(([k, n]) => [k, this.freeze(n)]);
n.c = new Map(c);
Object.freeze(n.c);
}
return Object.freeze(n);
}
tryToCache(n) {
if (!this._canBeCached(n)) {
return n;
}
const sig = this.signature(n);
const ref = this.signatures.get(sig);
if (ref !== undefined) {
return this.tryCacheFrozen(ref);
}
this.signatures.set(sig, this.freeze(n));
return n;
}
storeTransform(src, s, result) {
var _a;
if (!Object.isFrozen(result) || !Object.isFrozen(src))
return;
const t = (_a = this.transforms.get(src)) !== null && _a !== void 0 ? _a : new Map();
t.set(s, result);
this.transforms.set(src, t);
}
addChild(node, head, child) {
var _a, _b;
if (((_a = node.c) === null || _a === void 0 ? void 0 : _a.get(head)) !== child) {
if (!node.c || Object.isFrozen(node)) {
node = { ...node, c: new Map((_b = node.c) !== null && _b !== void 0 ? _b : []) };
}
node.c.set(head, child);
}
return Object.isFrozen(child) ? this.tryToCache(node) : node;
}
buildTail(s) {
if (this.tails.has(s)) {
return this.tails.get(s);
}
const head = s[0];
const tail = s.slice(1);
const t = this.tails.get(tail);
const c = t || this.buildTail(tail);
const n = this.addChild({ f: undefined, c: undefined }, head, c);
if (!t) {
return n;
}
const cachedNode = this.tryCacheFrozen(Object.freeze(n));
this.tails.set(s, cachedNode);
return cachedNode;
}
_insert(node, s, d) {
var _a, _b;
const orig = node;
if (Object.isFrozen(node)) {
const n = (_a = this.transforms.get(node)) === null || _a === void 0 ? void 0 : _a.get(s);
if (n) {
return this.tryCacheFrozen(n);
}
}
if (!s) {
if (!node.c) {
return this._eow;
}
else {
node = copyIfFrozen(node);
node.f = this._eow.f;
return node;
}
}
const head = s[0];
const tail = s.slice(1);
const cNode = (_b = node.c) === null || _b === void 0 ? void 0 : _b.get(head);
const child = cNode ? this._insert(cNode, tail, d + 1) : this.buildTail(tail);
node = this.addChild(node, head, child);
this.storeTransform(orig, s, node);
this.lastPath[d] = { s: head, n: child };
return node;
}
insertWord(word) {
let d = 1;
for (const s of word.split('')) {
const p = this.lastPath[d];
if ((p === null || p === void 0 ? void 0 : p.s) !== s)
break;
d++;
}
// remove the remaining part of the path because it doesn't match this word.
if (word.length < d) {
d = word.length;
}
this.lastPath.length = d;
d -= 1;
const { n } = this.lastPath[d];
const tail = word.slice(d);
this.lastPath[d].n = this._insert(n, tail, d + 1);
while (d > 0) {
const { s, n } = this.lastPath[d];
d -= 1;
const parent = this.lastPath[d];
const pn = parent.n;
parent.n = this.addChild(pn, s, n);
if (pn === parent.n)
break;
const tail = word.slice(d);
this.storeTransform(pn, tail, parent.n);
}
}
insert(words) {
for (const w of words) {
w && this.insertWord(w);
}
}
/**
* Resets the builder
*/
reset() {
this._root = { f: undefined, c: undefined };
this.cached.clear();
this.signatures.clear();
}
build(consolidateSuffixes = false) {
const root = this._root;
// Reset the builder to prevent updating the trie in the background.
this.reset();
return new trie_1.Trie(consolidateSuffixes ? consolidate_1.consolidate(root) : root);
}
}
exports.TrieBuilder = TrieBuilder;
function copyIfFrozen(n) {
if (!Object.isFrozen(n))
return n;
const c = n.c ? new Map(n.c) : undefined;
return { f: n.f, c };
}
//# sourceMappingURL=TrieBuilder.js.map