From 92db009066dd45233b9170bc75ab86e6a0a971eb Mon Sep 17 00:00:00 2001 From: SukkaW Date: Thu, 6 Feb 2025 22:30:34 +0800 Subject: [PATCH] Refactor: replace BFS w/ DFS [skip ci] --- Build/lib/trie.test.ts | 73 +++++++++++++++++++++--------------------- Build/lib/trie.ts | 27 ++++++++-------- 2 files changed, 49 insertions(+), 51 deletions(-) diff --git a/Build/lib/trie.test.ts b/Build/lib/trie.test.ts index 79381530..5fd64f24 100644 --- a/Build/lib/trie.test.ts +++ b/Build/lib/trie.test.ts @@ -125,12 +125,12 @@ describe('Trie', () => { trie.add('cdn.example.com'); trie.add('example.org'); - expect(trie.find('example.com')).toStrictEqual(['example.com', 'blog.example.com', 'cdn.example.com']); - expect(trie.find('com')).toStrictEqual(['example.com', 'blog.example.com', 'cdn.example.com']); - expect(trie.find('.example.com')).toStrictEqual(['blog.example.com', 'cdn.example.com']); + expect(trie.find('example.com')).toStrictEqual(['example.com', 'cdn.example.com', 'blog.example.com']); + expect(trie.find('com')).toStrictEqual(['example.com', 'cdn.example.com', 'blog.example.com']); + expect(trie.find('.example.com')).toStrictEqual(['cdn.example.com', 'blog.example.com']); expect(trie.find('org')).toStrictEqual(['example.org']); expect(trie.find('example.net')).toStrictEqual([]); - expect(trie.dump()).toStrictEqual(['example.com', 'example.org', 'blog.example.com', 'cdn.example.com']); + expect(trie.dump()).toStrictEqual(['example.org', 'example.com', 'cdn.example.com', 'blog.example.com']); }); it('should be possible to retrieve items matching the given prefix even with a smol trie', () => { @@ -147,7 +147,7 @@ describe('Trie', () => { expect(trie.find('.example.com')).toStrictEqual(['.example.com']); expect(trie.find('org')).toStrictEqual(['example.org']); expect(trie.find('example.net')).toStrictEqual([]); - expect(trie.dump()).toStrictEqual(['.example.com', 'example.org']); + expect(trie.dump()).toStrictEqual(['example.org', '.example.com']); }); it('should be possible to create a trie from an arbitrary iterable.', () => { @@ -173,14 +173,14 @@ describe('surge domainset dedupe', () => { it('should match subdomain - 1', () => { const trie = createTrie(['www.noc.one', 'www.sukkaw.com', 'blog.skk.moe', 'image.cdn.skk.moe', 'cdn.sukkaw.net'], false); - expect(trie.find('.skk.moe')).toStrictEqual(['blog.skk.moe', 'image.cdn.skk.moe']); + expect(trie.find('.skk.moe')).toStrictEqual(['image.cdn.skk.moe', 'blog.skk.moe']); expect(trie.find('.sukkaw.com')).toStrictEqual(['www.sukkaw.com']); }); it('should match subdomain - 2', () => { const trie = createTrie(['www.noc.one', 'www.sukkaw.com', '.skk.moe', 'blog.skk.moe', 'image.cdn.skk.moe', 'cdn.sukkaw.net'], false); - expect(trie.find('.skk.moe')).toStrictEqual(['.skk.moe', 'blog.skk.moe', 'image.cdn.skk.moe']); + expect(trie.find('.skk.moe')).toStrictEqual(['.skk.moe', 'image.cdn.skk.moe', 'blog.skk.moe']); expect(trie.find('.sukkaw.com')).toStrictEqual(['www.sukkaw.com']); }); @@ -203,13 +203,13 @@ describe('smol tree', () => { ], true); expect(trie.dump()).toStrictEqual([ - 'skk.moe', - 'anotherskk.moe', - '.cdn.local', - 'blog.skk.moe', - 'blog.anotherskk.moe', 'img.skk.local', - 'blog.img.skk.local' + 'blog.img.skk.local', + '.cdn.local', + 'anotherskk.moe', + 'blog.anotherskk.moe', + 'skk.moe', + 'blog.skk.moe' ]); }); @@ -221,10 +221,10 @@ describe('smol tree', () => { ], true); expect(trie.dump()).toStrictEqual([ - '.skk.moe', - 'www.noc.one', + '.sub.example.com', 'cdn.noc.one', - '.sub.example.com' + 'www.noc.one', + '.skk.moe' ]); }); @@ -262,10 +262,10 @@ describe('smol tree', () => { ], true); expect(trie.dump()).toStrictEqual([ - 'commercial.shouji.360.cn', 'cdn.creative.medialytics.com', - 'act.commercial.shouji.360.cn', - 'px.cdn.creative.medialytics.com' + 'px.cdn.creative.medialytics.com', + 'commercial.shouji.360.cn', + 'act.commercial.shouji.360.cn' ]); }); @@ -278,10 +278,10 @@ describe('smol tree', () => { ], true); expect(trie.dump()).toStrictEqual([ - 'skk.moe', 'anotherskk.moe', - 'blog.skk.moe', - 'blog.anotherskk.moe' + 'blog.anotherskk.moe', + 'skk.moe', + 'blog.skk.moe' ]); }); @@ -299,34 +299,34 @@ describe('smol tree', () => { trie.whitelist('.skk.moe'); expect(trie.dump()).toStrictEqual([ - 'anotherskk.moe', - '.cdn.local', - 'blog.anotherskk.moe', 'img.skk.local', - 'blog.img.skk.local' + 'blog.img.skk.local', + '.cdn.local', + 'anotherskk.moe', + 'blog.anotherskk.moe' ]); trie.whitelist('anotherskk.moe'); expect(trie.dump()).toStrictEqual([ - '.cdn.local', - 'blog.anotherskk.moe', 'img.skk.local', - 'blog.img.skk.local' + 'blog.img.skk.local', + '.cdn.local', + 'blog.anotherskk.moe' ]); trie.add('anotherskk.moe'); trie.whitelist('.anotherskk.moe'); expect(trie.dump()).toStrictEqual([ - '.cdn.local', 'img.skk.local', - 'blog.img.skk.local' + 'blog.img.skk.local', + '.cdn.local' ]); trie.whitelist('img.skk.local'); expect(trie.dump()).toStrictEqual([ - '.cdn.local', - 'blog.img.skk.local' + 'blog.img.skk.local', + '.cdn.local' ]); trie.whitelist('cdn.local'); @@ -349,15 +349,14 @@ describe('smol tree', () => { ], true); expect(trie.dump()).toStrictEqual([ - '.t.co', + 'cdn.example.com', 'blog.cdn.example.com', '.skk.moe', - 'cdn.example.com', 'blog.cdn.example.com' + '.t.co' ]); trie.whitelist('.t.co'); expect(trie.dump()).toStrictEqual([ - '.skk.moe', - 'cdn.example.com', 'blog.cdn.example.com' + 'cdn.example.com', 'blog.cdn.example.com', '.skk.moe' ]); trie.whitelist('skk.moe'); diff --git a/Build/lib/trie.ts b/Build/lib/trie.ts index 0f8d17eb..6ed0a095 100644 --- a/Build/lib/trie.ts +++ b/Build/lib/trie.ts @@ -6,7 +6,6 @@ import { fastStringCompare } from './misc'; import util from 'node:util'; import { noop } from 'foxts/noop'; import { fastStringArrayJoin } from 'foxts/fast-string-array-join'; -import FIFO from 'fast-fifo'; import { deleteBit, getBit, missingBit, setBit } from 'foxts/bitwise'; @@ -193,9 +192,9 @@ abstract class Triebase { private static bfsResults: [node: TrieNode | null, suffix: string[]] = [null, []]; - private static bfs(this: void, nodeStack: FIFO>, suffixStack: FIFO) { - const node = nodeStack.shift()!; - const suffix = suffixStack.shift()!; + private static dfs(this: void, nodeStack: Array>, suffixStack: string[][]) { + const node = nodeStack.pop()!; + const suffix = suffixStack.pop()!; node[2].forEach((childNode, k) => { // Pushing the child node to the stack for next iteration of DFS @@ -210,9 +209,9 @@ abstract class Triebase { return Triebase.bfsResults; } - private static bfsWithSort(this: void, nodeStack: FIFO>, suffixStack: FIFO) { - const node = nodeStack.shift()!; - const suffix = suffixStack.shift()!; + private static dfsWithSort(this: void, nodeStack: Array>, suffixStack: string[][]) { + const node = nodeStack.pop()!; + const suffix = suffixStack.pop()!; const child = node[2]; @@ -241,13 +240,13 @@ abstract class Triebase { initialSuffix: string[] = [], withSort = false ) { - const bfsImpl = withSort ? Triebase.bfsWithSort : Triebase.bfs; + const bfsImpl = withSort ? Triebase.dfsWithSort : Triebase.dfs; - const nodeStack = new FIFO>(); + const nodeStack: Array> = []; nodeStack.push(initialNode); // Resolving initial string (begin the start of the stack) - const suffixStack = new FIFO(); + const suffixStack: string[][] = []; suffixStack.push(initialSuffix); let node: TrieNode = initialNode; @@ -275,19 +274,19 @@ abstract class Triebase { initialNode = this.$root, initialSuffix: string[] = [] ) { - const nodeStack = new FIFO>(); + const nodeStack: Array> = []; nodeStack.push(initialNode); // Resolving initial string (begin the start of the stack) - const suffixStack = new FIFO(); + const suffixStack: string[][] = []; suffixStack.push(initialSuffix); let node: TrieNode = initialNode; let child: Map> = node[2]; do { - node = nodeStack.shift()!; - const suffix = suffixStack.shift()!; + node = nodeStack.pop()!; + const suffix = suffixStack.pop()!; child = node[2]; if (child.size) { const keys = Array.from(child.keys()).sort(Triebase.compare);