ソースを参照

Perf: avoid mutate string in ruleset base

SukkaW 1 年間 前
コミット
9c82e5346c

+ 7 - 7
Build/lib/rules/base.ts

@@ -100,14 +100,14 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
     for (let i = 0, len = domains.length; i < len; i++) {
       d = domains[i];
       if (d !== null) {
-        this.addDomain(d);
+        this.domainTrie.add(d, false, null, 0);
       }
     }
     return this;
   }
 
-  addDomainSuffix(domain: string) {
-    this.domainTrie.add(domain, true);
+  addDomainSuffix(domain: string, lineFromDot = domain[0] === '.') {
+    this.domainTrie.add(domain, true, lineFromDot ? 1 : 0);
     return this;
   }
 
@@ -126,9 +126,9 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
   private async addFromDomainsetPromise(source: AsyncIterable<string> | Iterable<string> | string[]) {
     for await (const line of source) {
       if (line[0] === '.') {
-        this.addDomainSuffix(line);
+        this.addDomainSuffix(line, true);
       } else {
-        this.addDomain(line);
+        this.domainTrie.add(line, false, null, 0);
       }
     }
   }
@@ -147,10 +147,10 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
 
       switch (type) {
         case 'DOMAIN':
-          this.addDomain(value);
+          this.domainTrie.add(value, false, null, 0);
           break;
         case 'DOMAIN-SUFFIX':
-          this.addDomainSuffix(value);
+          this.addDomainSuffix(value, false);
           break;
         case 'DOMAIN-KEYWORD':
           this.addDomainKeyword(value);

+ 17 - 25
Build/lib/rules/domainset.ts

@@ -8,55 +8,47 @@ import { looseTldtsOpt } from '../../constants/loose-tldts-opt';
 import { fastStringCompare } from '../misc';
 import escapeStringRegexp from 'escape-string-regexp-node';
 
-type Preprocessed = string[];
-
-export class DomainsetOutput extends RuleOutput<Preprocessed> {
+export class DomainsetOutput extends RuleOutput<string[]> {
   protected type = 'domainset' as const;
 
+  private $surge: string[] = ['this_ruleset_is_made_by_sukkaw.ruleset.skk.moe'];
+  private $clash: string[] = ['this_ruleset_is_made_by_sukkaw.ruleset.skk.moe'];
+  private $singbox_domains: string[] = ['this_ruleset_is_made_by_sukkaw.ruleset.skk.moe'];
+  private $singbox_domains_suffixes: string[] = ['this_ruleset_is_made_by_sukkaw.ruleset.skk.moe'];
+
   preprocess() {
     const kwfilter = createKeywordFilter(this.domainKeywords);
-
     const results: string[] = [];
 
-    this.domainTrie.dump((domain) => {
+    this.domainTrie.dumpWithoutDot((domain, subdomain) => {
       if (kwfilter(domain)) {
         return;
       }
+
+      this.$surge.push(subdomain ? '.' + domain : domain);
+      this.$clash.push(subdomain ? `+.${domain}` : domain);
+      (subdomain ? this.$singbox_domains : this.$singbox_domains_suffixes).push(domain);
+
       results.push(domain);
     }, true);
 
-    const sorted = results;
-    sorted.push('this_ruleset_is_made_by_sukkaw.ruleset.skk.moe');
-
-    return sorted;
+    return results;
   }
 
   surge(): string[] {
-    return this.$preprocessed;
+    return this.$surge;
   }
 
   clash(): string[] {
-    return this.$preprocessed.map(i => (i[0] === '.' ? `+${i}` : i));
+    return this.$clash;
   }
 
   singbox(): string[] {
-    const domains: string[] = [];
-    const domainSuffixes: string[] = [];
-
-    for (let i = 0, len = this.$preprocessed.length; i < len; i++) {
-      const domain = this.$preprocessed[i];
-      if (domain[0] === '.') {
-        domainSuffixes.push(domain.slice(1));
-      } else {
-        domains.push(domain);
-      }
-    }
-
     return RuleOutput.jsonToLines({
       version: 2,
       rules: [{
-        domain: domains,
-        domain_suffix: domainSuffixes
+        domain: this.$singbox_domains,
+        domain_suffix: this.$singbox_domains_suffixes
       }]
     } satisfies SingboxSourceFormat);
   }

+ 4 - 4
Build/lib/rules/ruleset.ts

@@ -23,13 +23,13 @@ export class RulesetOutput extends RuleOutput<Preprocessed> {
     const domainSuffixes: string[] = [];
     const sortedDomainRules: string[] = [];
 
-    this.domainTrie.dump((domain) => {
+    this.domainTrie.dumpWithoutDot((domain, includeAllSubdomain) => {
       if (kwfilter(domain)) {
         return;
       }
-      if (domain[0] === '.') {
-        domainSuffixes.push(domain.slice(1));
-        sortedDomainRules.push(`DOMAIN-SUFFIX,${domain.slice(1)}`);
+      if (includeAllSubdomain) {
+        domainSuffixes.push(domain);
+        sortedDomainRules.push(`DOMAIN-SUFFIX,${domain}`);
       } else {
         domains.push(domain);
         sortedDomainRules.push(`DOMAIN,${domain}`);

+ 10 - 1
Build/lib/trie.test.ts

@@ -1,6 +1,15 @@
-import { createTrie } from './trie';
 import { describe, it } from 'mocha';
 import { expect } from 'expect';
+import { HostnameSmolTrie, HostnameTrie } from './trie';
+
+function createTrie<Meta = any>(from: string[] | Set<string> | null, smolTree: true): HostnameSmolTrie<Meta>;
+function createTrie<Meta = any>(from?: string[] | Set<string> | null, smolTree?: false): HostnameTrie<Meta>;
+function createTrie<_Meta = any>(from?: string[] | Set<string> | null, smolTree = true) {
+  if (smolTree) {
+    return new HostnameSmolTrie(from);
+  }
+  return new HostnameTrie(from);
+};
 
 // describe('hostname to tokens', () => {
 //   it('should split hostname into tokens.', () => {

+ 19 - 40
Build/lib/trie.ts

@@ -170,10 +170,8 @@ abstract class Triebase<Meta = any> {
   };
 
   public contains(suffix: string, includeAllSubdomain = suffix[0] === '.'): boolean {
-    let hostnameFromIndex = 0;
-    if (suffix[0] === '.') {
-      hostnameFromIndex = 1;
-    }
+    const hostnameFromIndex = suffix[0] === '.' ? 1 : 0;
+
     const res = this.walkIntoLeafWithSuffix(suffix, hostnameFromIndex);
     if (!res) return false;
     if (includeAllSubdomain) return res.node[1];
@@ -333,13 +331,9 @@ abstract class Triebase<Meta = any> {
   public find(
     inputSuffix: string,
     subdomainOnly = inputSuffix[0] === '.',
-    hostnameFromIndex = 0
+    hostnameFromIndex = inputSuffix[0] === '.' ? 1 : 0
     // /** @default true */ includeEqualWithSuffix = true
   ): string[] {
-    if (inputSuffix[0] === '.') {
-      hostnameFromIndex = 1;
-    }
-
     const inputTokens = hostnameToTokens(inputSuffix, hostnameFromIndex);
     const res = this.walkIntoLeafWithTokens(inputTokens);
     if (res === null) return [];
@@ -395,11 +389,7 @@ abstract class Triebase<Meta = any> {
    * Method used to assert whether the given prefix exists in the Trie.
    */
   public has(suffix: string, includeAllSubdomain = suffix[0] === '.'): boolean {
-    let hostnameFromIndex = 0;
-
-    if (suffix[0] === '.') {
-      hostnameFromIndex = 1;
-    }
+    const hostnameFromIndex = suffix[0] === '.' ? 1 : 0;
 
     const res = this.walkIntoLeafWithSuffix(suffix, hostnameFromIndex);
 
@@ -409,6 +399,18 @@ abstract class Triebase<Meta = any> {
     return true;
   };
 
+  public dumpWithoutDot(onSuffix: (suffix: string, subdomain: boolean) => void, withSort = false) {
+    const handleSuffix = (suffix: string[], subdomain: boolean) => {
+      onSuffix(fastStringArrayJoin(suffix, '.'), subdomain);
+    };
+
+    if (withSort) {
+      this.walkWithSort(handleSuffix);
+    } else {
+      this.walk(handleSuffix);
+    }
+  }
+
   public dump(onSuffix: (suffix: string) => void, withSort?: boolean): void;
   public dump(onSuffix?: null, withSort?: boolean): string[];
   public dump(onSuffix?: ((suffix: string) => void) | null, withSort = false): string[] | void {
@@ -490,14 +492,10 @@ abstract class Triebase<Meta = any> {
 export class HostnameSmolTrie<Meta = any> extends Triebase<Meta> {
   public smolTree = true;
 
-  add(suffix: string, includeAllSubdomain = suffix[0] === '.', meta?: Meta, hostnameFromIndex = 0): void {
+  add(suffix: string, includeAllSubdomain = suffix[0] === '.', meta?: Meta, hostnameFromIndex = suffix[0] === '.' ? 1 : 0): void {
     let node: TrieNode<Meta> = this.$root;
     let curNodeChildren: Map<string, TrieNode<Meta>> = node[3];
 
-    if (hostnameFromIndex === 0 && suffix[0] === '.') {
-      hostnameFromIndex = 1;
-    }
-
     const onToken = (token: string) => {
       curNodeChildren = node[3];
       if (curNodeChildren.has(token)) {
@@ -544,11 +542,7 @@ export class HostnameSmolTrie<Meta = any> extends Triebase<Meta> {
     node[4] = meta!;
   }
 
-  public whitelist(suffix: string, includeAllSubdomain = suffix[0] === '.', hostnameFromIndex = 0) {
-    if (suffix[0] === '.') {
-      hostnameFromIndex = 1;
-    }
-
+  public whitelist(suffix: string, includeAllSubdomain = suffix[0] === '.', hostnameFromIndex = suffix[0] === '.' ? 1 : 0) {
     const tokens = hostnameToTokens(suffix, hostnameFromIndex);
     const res = this.getSingleChildLeaf(tokens);
 
@@ -584,7 +578,7 @@ export class HostnameTrie<Meta = any> extends Triebase<Meta> {
     return this.$size;
   }
 
-  add(suffix: string, includeAllSubdomain = suffix[0] === '.', meta?: Meta, hostnameFromIndex = 0): void {
+  add(suffix: string, includeAllSubdomain = suffix[0] === '.', meta?: Meta, hostnameFromIndex = suffix[0] === '.' ? 1 : 0): void {
     let node: TrieNode<Meta> = this.$root;
 
     const onToken = (token: string) => {
@@ -599,10 +593,6 @@ export class HostnameTrie<Meta = any> extends Triebase<Meta> {
       return false;
     };
 
-    if (hostnameFromIndex === 0 && suffix[0] === '.') {
-      hostnameFromIndex = 1;
-    }
-
     // When walkHostnameTokens returns true, we should skip the rest
     if (walkHostnameTokens(suffix, onToken, hostnameFromIndex)) {
       return;
@@ -620,17 +610,6 @@ export class HostnameTrie<Meta = any> extends Triebase<Meta> {
   }
 }
 
-export function createTrie<Meta = any>(from: string[] | Set<string> | null, smolTree: true): HostnameSmolTrie<Meta>;
-export function createTrie<Meta = any>(from?: string[] | Set<string> | null, smolTree?: false): HostnameTrie<Meta>;
-export function createTrie<_Meta = any>(from?: string[] | Set<string> | null, smolTree = true) {
-  if (smolTree) {
-    return new HostnameSmolTrie(from);
-  }
-  return new HostnameTrie(from);
-};
-
-export type Trie = ReturnType<typeof createTrie>;
-
 // function deepEqualArray(a: string[], b: string[]) {
 //   let len = a.length;
 //   if (len !== b.length) return false;