Perf: Aho-Corasick node uses extends

This commit is contained in:
SukkaW 2024-11-05 20:31:00 +08:00
parent 5c3b86bce0
commit 7b6b14b1dc

View File

@ -1,20 +1,14 @@
const WORDEND = Symbol('wordEnd'); class Node extends Map<string, Node> {
const FAIL = Symbol('fail'); constructor(
public wordEnd: boolean,
type Node = Map<string, Node> & { public fail: Node | undefined
[WORDEND]: boolean, ) {
[FAIL]: Node | undefined super();
}; }
function createNode(): Node {
const node = new Map<string, Node | undefined>() as Node;
node[WORDEND] = false;
node[FAIL] = undefined;
return node;
} }
function createKeywordFilter(keys: string[] | Set<string>) { function createKeywordFilter(keys: string[] | Set<string>) {
const root = createNode(); const root = new Node(false, undefined);
// Create a trie with extra fields and information // Create a trie with extra fields and information
const put = (key: string) => { const put = (key: string) => {
@ -28,7 +22,7 @@ function createKeywordFilter(keys: string[] | Set<string>) {
if (node.has(char)) { if (node.has(char)) {
node = node.get(char)!; node = node.get(char)!;
} else { } else {
const newNode = createNode(); const newNode = new Node(false, undefined);
node.set(char, newNode); node.set(char, newNode);
node = newNode; node = newNode;
} }
@ -36,7 +30,7 @@ function createKeywordFilter(keys: string[] | Set<string>) {
// If a new node is created, mark it as a word end when loop finish // If a new node is created, mark it as a word end when loop finish
if (node !== root) { if (node !== root) {
node[WORDEND] = true; node.wordEnd = true;
} }
}; };
@ -49,13 +43,13 @@ function createKeywordFilter(keys: string[] | Set<string>) {
const beginNode = queue.pop()!; const beginNode = queue.pop()!;
beginNode.forEach((node, char) => { beginNode.forEach((node, char) => {
let failNode = beginNode[FAIL]; let failNode = beginNode.fail;
while (failNode && !failNode.has(char)) { while (failNode && !failNode.has(char)) {
failNode = failNode[FAIL]; failNode = failNode.fail;
} }
node[FAIL] = failNode ? failNode.get(char) : root; node.fail = failNode ? failNode.get(char) : root;
queue.push(node); queue.push(node);
}); });
@ -70,12 +64,12 @@ function createKeywordFilter(keys: string[] | Set<string>) {
const char = text[i]; const char = text[i];
while (node && !node.has(char)) { while (node && !node.has(char)) {
node = node[FAIL]; node = node.fail;
} }
node = node ? node.get(char)! : root; node = node ? node.get(char)! : root;
if (node[WORDEND]) { if (node.wordEnd) {
return true; return true;
} }
} }