瀏覽代碼

Feat: trie dump with sort & FIFO

SukkaW 1 年之前
父節點
當前提交
f6eb8b0a4c
共有 3 個文件被更改,包括 110 次插入3 次删除
  1. 63 0
      Build/lib/fifo.ts
  2. 2 2
      Build/lib/rules/domainset.ts
  3. 45 1
      Build/lib/trie.ts

+ 63 - 0
Build/lib/fifo.ts

@@ -0,0 +1,63 @@
+class Node<T> {
+  next?: Node<T>;
+
+  constructor(public readonly value: T) {}
+}
+
+export default class FIFO<T> {
+  private head?: Node<T>;
+  private tail?: Node<T>;
+  public $size = 0;
+
+  constructor() {
+    this.clear();
+  }
+
+  enqueue(value: T) {
+    const node = new Node<T>(value);
+
+    if (this.head) {
+      this.tail!.next = node;
+      this.tail = node;
+    } else {
+      this.head = node;
+      this.tail = node;
+    }
+
+    this.$size++;
+  }
+
+  dequeue() {
+    const current = this.head;
+    if (!current) {
+      return;
+    }
+
+    this.head = this.head!.next;
+    this.$size--;
+    return current.value;
+  }
+
+  peek() {
+    return this.head?.value;
+  }
+
+  clear() {
+    this.head = undefined;
+    this.tail = undefined;
+    this.$size = 0;
+  }
+
+  get size() {
+    return this.$size;
+  }
+
+  *[Symbol.iterator]() {
+    let current = this.head;
+
+    while (current) {
+      yield current.value;
+      current = current.next;
+    }
+  }
+}

+ 2 - 2
Build/lib/rules/domainset.ts

@@ -1,6 +1,6 @@
 import { invariant } from 'foxact/invariant';
 import createKeywordFilter from '../aho-corasick';
-import { buildParseDomainMap, sortDomains } from '../stable-sort-domain';
+import { buildParseDomainMap } from '../stable-sort-domain';
 import { RuleOutput } from './base';
 import type { SingboxSourceFormat } from '../singbox';
 import { appendArrayFromSet } from '../misc';
@@ -27,7 +27,7 @@ export class DomainsetOutput extends RuleOutput<Preprocessed> {
       this.apexDomainMap = domainMap;
       this.subDomainMap = subdomainMap;
     }
-    const sorted = sortDomains(results, this.apexDomainMap, this.subDomainMap);
+    const sorted = results;
     sorted.push('this_ruleset_is_made_by_sukkaw.ruleset.skk.moe');
 
     return sorted;

+ 45 - 1
Build/lib/trie.ts

@@ -5,6 +5,7 @@
 import { fastStringArrayJoin } from './misc';
 import util from 'node:util';
 import { noop } from 'foxact/noop';
+import FIFO from './fifo';
 
 type TrieNode<Meta = any> = [
   boolean, /** end */
@@ -205,6 +206,49 @@ abstract class Triebase<Meta = any> {
     } while (nodeStack.length);
   };
 
+  static compare(this: void, a: string, b: string) {
+    if (a === b) return 0;
+    return (a.length - b.length) || a.localeCompare(b);
+  }
+
+  private walkWithSort(
+    onMatches: (suffix: string[], subdomain: boolean, meta: Meta) => void,
+    initialNode = this.$root,
+    initialSuffix: string[] = []
+  ) {
+    const nodeStack = new FIFO<TrieNode<Meta>>();
+    nodeStack.enqueue(initialNode);
+
+    // Resolving initial string (begin the start of the stack)
+    const suffixStack = new FIFO<string[]>();
+    suffixStack.enqueue(initialSuffix);
+
+    let node: TrieNode<Meta> = initialNode;
+
+    do {
+      node = nodeStack.dequeue()!;
+      const suffix = suffixStack.dequeue()!;
+
+      if (node[3].size) {
+        const keys = Array.from(node[3].keys()).sort(Triebase.compare);
+
+        for (let i = 0, l = keys.length; i < l; i++) {
+          const key = keys[i];
+          const childNode = node[3].get(key)!;
+
+          // Pushing the child node to the stack for next iteration of DFS
+          nodeStack.enqueue(childNode);
+          suffixStack.enqueue([key, ...suffix]);
+        }
+      }
+
+      // If the node is a sentinel, we push the suffix to the results
+      if (node[0]) {
+        onMatches(suffix, node[1], node[4]);
+      }
+    } while (nodeStack.size);
+  };
+
   protected getSingleChildLeaf(tokens: string[]): FindSingleChildLeafResult<Meta> | null {
     let toPrune: TrieNode | null = null;
     let tokenToPrune: string | null = null;
@@ -331,7 +375,7 @@ abstract class Triebase<Meta = any> {
         results.push(subdomain ? '.' + d : d);
       };
 
-    this.walk(handleSuffix);
+    this.walkWithSort(handleSuffix);
 
     return results;
   };