Perf: a few improvements to trie

This commit is contained in:
SukkaW 2024-09-18 14:05:07 +08:00
parent a004ffb960
commit 3bd0ebe36e
2 changed files with 31 additions and 29 deletions

View File

@ -160,11 +160,11 @@ const processRuleSet = (ruleSet: string[]) => {
}
}
const dumped = trie.dumpWithMeta();
const dumped = trie.dumpMeta();
for (let i = 0, len = dumped.length; i < len; i++) {
const originalIndex = unpackFirst(dumped[i][1]);
const flag = unpackSecond(dumped[i][1]);
const originalIndex = unpackFirst(dumped[i]);
const flag = unpackSecond(dumped[i]);
const type = flag === flagDomain ? 'DOMAIN' : 'DOMAIN-SUFFIX';

View File

@ -63,7 +63,7 @@ const walkHostnameTokens = (hostname: string, onToken: (token: string) => boolea
const l = tokens.length - 1;
for (let i = l; i >= 0; i--) {
if (
i < l
i < l // when i === l, we are at the first of hostname, no splitor there
// when onToken returns true, we should skip the rest of the loop
&& onToken('.')
) {
@ -304,7 +304,10 @@ export const createTrie = <Meta = any>(from?: string[] | Set<string> | null, smo
/**
* Method used to retrieve every item in the trie with the given prefix.
*/
const find = (inputSuffix: string, /** @default true */ includeEqualWithSuffix = true): string[] => {
const find = (
inputSuffix: string,
/** @default true */ includeEqualWithSuffix = true
): string[] => {
if (smolTree) {
throw new Error('A Trie with smolTree enabled cannot perform find!');
}
@ -316,9 +319,11 @@ export const createTrie = <Meta = any>(from?: string[] | Set<string> | null, smo
const matches: string[][] = [];
const onMatches = includeEqualWithSuffix
// fast path (default option)
? (suffix: string[]) => matches.push(suffix)
// slow path
: (suffix: string[]) => {
if (suffix.some((t, i) => t !== inputTokens[i])) {
if (!deepEqualArray(suffix, inputTokens)) {
matches.push(suffix);
}
};
@ -332,28 +337,6 @@ export const createTrie = <Meta = any>(from?: string[] | Set<string> | null, smo
return matches.map((m) => fastStringArrayJoin(m, ''));
};
/**
* Works like trie.find, but instead of returning the matches as an array, it removes them from the given set in-place.
*/
const substractSetInPlaceFromFound = (inputSuffix: string, set: Set<string>) => {
if (smolTree) {
throw new Error('A Trie with smolTree enabled cannot perform substractSetInPlaceFromFound!');
}
const inputTokens = hostnameToTokens(inputSuffix);
const res = walkIntoLeafWithTokens(inputTokens);
if (res === null) return;
const onMatches = (suffix: string[]) => set.delete(fastStringArrayJoin(suffix, ''));
walk(
onMatches,
res.node, // Performing DFS from prefix
inputTokens
);
};
/**
* Method used to delete a prefix from the trie.
*/
@ -396,6 +379,16 @@ export const createTrie = <Meta = any>(from?: string[] | Set<string> | null, smo
return results;
};
const dumpMeta = () => {
const results: Meta[] = [];
walk((suffix, meta) => {
results.push(meta);
});
return results;
};
const dumpWithMeta = () => {
const results: Array<[string, Meta]> = [];
@ -463,11 +456,11 @@ export const createTrie = <Meta = any>(from?: string[] | Set<string> | null, smo
add,
contains,
find,
substractSetInPlaceFromFound,
remove,
delete: remove,
has,
dump,
dumpMeta,
dumpWithMeta,
get size() {
if (smolTree) {
@ -486,3 +479,12 @@ export const createTrie = <Meta = any>(from?: string[] | Set<string> | null, smo
};
export type Trie = ReturnType<typeof createTrie>;
function deepEqualArray(a: string[], b: string[]) {
let len = a.length;
if (len !== b.length) return false;
while (len--) {
if (a[len] !== b[len]) return false;
}
return true;
};