From 3bd0ebe36eec8ab9c95df5416dd9e3433f2d1162 Mon Sep 17 00:00:00 2001 From: SukkaW Date: Wed, 18 Sep 2024 14:05:07 +0800 Subject: [PATCH] Perf: a few improvements to trie --- Build/lib/create-file.ts | 6 ++--- Build/lib/trie.ts | 54 +++++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/Build/lib/create-file.ts b/Build/lib/create-file.ts index 7250f931..b8acee2b 100644 --- a/Build/lib/create-file.ts +++ b/Build/lib/create-file.ts @@ -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'; diff --git a/Build/lib/trie.ts b/Build/lib/trie.ts index e53469c9..fbff4f10 100644 --- a/Build/lib/trie.ts +++ b/Build/lib/trie.ts @@ -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 = (from?: string[] | Set | 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 = (from?: string[] | Set | 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 = (from?: string[] | Set | 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) => { - 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 = (from?: string[] | Set | 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 = (from?: string[] | Set | null, smo add, contains, find, - substractSetInPlaceFromFound, remove, delete: remove, has, dump, + dumpMeta, dumpWithMeta, get size() { if (smolTree) { @@ -486,3 +479,12 @@ export const createTrie = (from?: string[] | Set | null, smo }; export type Trie = ReturnType; + +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; +};