Skip to content

Commit

Permalink
Perf: avoid mutate string in ruleset base
Browse files Browse the repository at this point in the history
  • Loading branch information
SukkaW committed Dec 14, 2024
1 parent 5c85a0c commit 9c82e53
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 77 deletions.
14 changes: 7 additions & 7 deletions Build/lib/rules/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand All @@ -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);
}
}
}
Expand All @@ -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);
Expand Down
42 changes: 17 additions & 25 deletions Build/lib/rules/domainset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
8 changes: 4 additions & 4 deletions Build/lib/rules/ruleset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`);
Expand Down
11 changes: 10 additions & 1 deletion Build/lib/trie.test.ts
Original file line number Diff line number Diff line change
@@ -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.', () => {
Expand Down
59 changes: 19 additions & 40 deletions Build/lib/trie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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 [];
Expand Down Expand Up @@ -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);

Expand All @@ -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 {
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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) => {
Expand All @@ -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;
Expand All @@ -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;
Expand Down

0 comments on commit 9c82e53

Please sign in to comment.