mirror of
https://github.com/SukkaW/Surge.git
synced 2025-12-13 01:30:37 +08:00
Perf: avoid mutate string in ruleset base
This commit is contained in:
parent
5c85a0c504
commit
9c82e5346c
@ -100,14 +100,14 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
|
|||||||
for (let i = 0, len = domains.length; i < len; i++) {
|
for (let i = 0, len = domains.length; i < len; i++) {
|
||||||
d = domains[i];
|
d = domains[i];
|
||||||
if (d !== null) {
|
if (d !== null) {
|
||||||
this.addDomain(d);
|
this.domainTrie.add(d, false, null, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
addDomainSuffix(domain: string) {
|
addDomainSuffix(domain: string, lineFromDot = domain[0] === '.') {
|
||||||
this.domainTrie.add(domain, true);
|
this.domainTrie.add(domain, true, lineFromDot ? 1 : 0);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,9 +126,9 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
|
|||||||
private async addFromDomainsetPromise(source: AsyncIterable<string> | Iterable<string> | string[]) {
|
private async addFromDomainsetPromise(source: AsyncIterable<string> | Iterable<string> | string[]) {
|
||||||
for await (const line of source) {
|
for await (const line of source) {
|
||||||
if (line[0] === '.') {
|
if (line[0] === '.') {
|
||||||
this.addDomainSuffix(line);
|
this.addDomainSuffix(line, true);
|
||||||
} else {
|
} else {
|
||||||
this.addDomain(line);
|
this.domainTrie.add(line, false, null, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,10 +147,10 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'DOMAIN':
|
case 'DOMAIN':
|
||||||
this.addDomain(value);
|
this.domainTrie.add(value, false, null, 0);
|
||||||
break;
|
break;
|
||||||
case 'DOMAIN-SUFFIX':
|
case 'DOMAIN-SUFFIX':
|
||||||
this.addDomainSuffix(value);
|
this.addDomainSuffix(value, false);
|
||||||
break;
|
break;
|
||||||
case 'DOMAIN-KEYWORD':
|
case 'DOMAIN-KEYWORD':
|
||||||
this.addDomainKeyword(value);
|
this.addDomainKeyword(value);
|
||||||
|
|||||||
@ -8,55 +8,47 @@ import { looseTldtsOpt } from '../../constants/loose-tldts-opt';
|
|||||||
import { fastStringCompare } from '../misc';
|
import { fastStringCompare } from '../misc';
|
||||||
import escapeStringRegexp from 'escape-string-regexp-node';
|
import escapeStringRegexp from 'escape-string-regexp-node';
|
||||||
|
|
||||||
type Preprocessed = string[];
|
export class DomainsetOutput extends RuleOutput<string[]> {
|
||||||
|
|
||||||
export class DomainsetOutput extends RuleOutput<Preprocessed> {
|
|
||||||
protected type = 'domainset' as const;
|
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() {
|
preprocess() {
|
||||||
const kwfilter = createKeywordFilter(this.domainKeywords);
|
const kwfilter = createKeywordFilter(this.domainKeywords);
|
||||||
|
|
||||||
const results: string[] = [];
|
const results: string[] = [];
|
||||||
|
|
||||||
this.domainTrie.dump((domain) => {
|
this.domainTrie.dumpWithoutDot((domain, subdomain) => {
|
||||||
if (kwfilter(domain)) {
|
if (kwfilter(domain)) {
|
||||||
return;
|
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);
|
results.push(domain);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
const sorted = results;
|
return results;
|
||||||
sorted.push('this_ruleset_is_made_by_sukkaw.ruleset.skk.moe');
|
|
||||||
|
|
||||||
return sorted;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
surge(): string[] {
|
surge(): string[] {
|
||||||
return this.$preprocessed;
|
return this.$surge;
|
||||||
}
|
}
|
||||||
|
|
||||||
clash(): string[] {
|
clash(): string[] {
|
||||||
return this.$preprocessed.map(i => (i[0] === '.' ? `+${i}` : i));
|
return this.$clash;
|
||||||
}
|
}
|
||||||
|
|
||||||
singbox(): string[] {
|
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({
|
return RuleOutput.jsonToLines({
|
||||||
version: 2,
|
version: 2,
|
||||||
rules: [{
|
rules: [{
|
||||||
domain: domains,
|
domain: this.$singbox_domains,
|
||||||
domain_suffix: domainSuffixes
|
domain_suffix: this.$singbox_domains_suffixes
|
||||||
}]
|
}]
|
||||||
} satisfies SingboxSourceFormat);
|
} satisfies SingboxSourceFormat);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,13 +23,13 @@ export class RulesetOutput extends RuleOutput<Preprocessed> {
|
|||||||
const domainSuffixes: string[] = [];
|
const domainSuffixes: string[] = [];
|
||||||
const sortedDomainRules: string[] = [];
|
const sortedDomainRules: string[] = [];
|
||||||
|
|
||||||
this.domainTrie.dump((domain) => {
|
this.domainTrie.dumpWithoutDot((domain, includeAllSubdomain) => {
|
||||||
if (kwfilter(domain)) {
|
if (kwfilter(domain)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (domain[0] === '.') {
|
if (includeAllSubdomain) {
|
||||||
domainSuffixes.push(domain.slice(1));
|
domainSuffixes.push(domain);
|
||||||
sortedDomainRules.push(`DOMAIN-SUFFIX,${domain.slice(1)}`);
|
sortedDomainRules.push(`DOMAIN-SUFFIX,${domain}`);
|
||||||
} else {
|
} else {
|
||||||
domains.push(domain);
|
domains.push(domain);
|
||||||
sortedDomainRules.push(`DOMAIN,${domain}`);
|
sortedDomainRules.push(`DOMAIN,${domain}`);
|
||||||
|
|||||||
@ -1,6 +1,15 @@
|
|||||||
import { createTrie } from './trie';
|
|
||||||
import { describe, it } from 'mocha';
|
import { describe, it } from 'mocha';
|
||||||
import { expect } from 'expect';
|
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', () => {
|
// describe('hostname to tokens', () => {
|
||||||
// it('should split hostname into tokens.', () => {
|
// it('should split hostname into tokens.', () => {
|
||||||
|
|||||||
@ -170,10 +170,8 @@ abstract class Triebase<Meta = any> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
public contains(suffix: string, includeAllSubdomain = suffix[0] === '.'): boolean {
|
public contains(suffix: string, includeAllSubdomain = suffix[0] === '.'): boolean {
|
||||||
let hostnameFromIndex = 0;
|
const hostnameFromIndex = suffix[0] === '.' ? 1 : 0;
|
||||||
if (suffix[0] === '.') {
|
|
||||||
hostnameFromIndex = 1;
|
|
||||||
}
|
|
||||||
const res = this.walkIntoLeafWithSuffix(suffix, hostnameFromIndex);
|
const res = this.walkIntoLeafWithSuffix(suffix, hostnameFromIndex);
|
||||||
if (!res) return false;
|
if (!res) return false;
|
||||||
if (includeAllSubdomain) return res.node[1];
|
if (includeAllSubdomain) return res.node[1];
|
||||||
@ -333,13 +331,9 @@ abstract class Triebase<Meta = any> {
|
|||||||
public find(
|
public find(
|
||||||
inputSuffix: string,
|
inputSuffix: string,
|
||||||
subdomainOnly = inputSuffix[0] === '.',
|
subdomainOnly = inputSuffix[0] === '.',
|
||||||
hostnameFromIndex = 0
|
hostnameFromIndex = inputSuffix[0] === '.' ? 1 : 0
|
||||||
// /** @default true */ includeEqualWithSuffix = true
|
// /** @default true */ includeEqualWithSuffix = true
|
||||||
): string[] {
|
): string[] {
|
||||||
if (inputSuffix[0] === '.') {
|
|
||||||
hostnameFromIndex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const inputTokens = hostnameToTokens(inputSuffix, hostnameFromIndex);
|
const inputTokens = hostnameToTokens(inputSuffix, hostnameFromIndex);
|
||||||
const res = this.walkIntoLeafWithTokens(inputTokens);
|
const res = this.walkIntoLeafWithTokens(inputTokens);
|
||||||
if (res === null) return [];
|
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.
|
* Method used to assert whether the given prefix exists in the Trie.
|
||||||
*/
|
*/
|
||||||
public has(suffix: string, includeAllSubdomain = suffix[0] === '.'): boolean {
|
public has(suffix: string, includeAllSubdomain = suffix[0] === '.'): boolean {
|
||||||
let hostnameFromIndex = 0;
|
const hostnameFromIndex = suffix[0] === '.' ? 1 : 0;
|
||||||
|
|
||||||
if (suffix[0] === '.') {
|
|
||||||
hostnameFromIndex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = this.walkIntoLeafWithSuffix(suffix, hostnameFromIndex);
|
const res = this.walkIntoLeafWithSuffix(suffix, hostnameFromIndex);
|
||||||
|
|
||||||
@ -409,6 +399,18 @@ abstract class Triebase<Meta = any> {
|
|||||||
return true;
|
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: (suffix: string) => void, withSort?: boolean): void;
|
||||||
public dump(onSuffix?: null, withSort?: boolean): string[];
|
public dump(onSuffix?: null, withSort?: boolean): string[];
|
||||||
public dump(onSuffix?: ((suffix: string) => void) | null, withSort = false): string[] | void {
|
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> {
|
export class HostnameSmolTrie<Meta = any> extends Triebase<Meta> {
|
||||||
public smolTree = true;
|
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 node: TrieNode<Meta> = this.$root;
|
||||||
let curNodeChildren: Map<string, TrieNode<Meta>> = node[3];
|
let curNodeChildren: Map<string, TrieNode<Meta>> = node[3];
|
||||||
|
|
||||||
if (hostnameFromIndex === 0 && suffix[0] === '.') {
|
|
||||||
hostnameFromIndex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const onToken = (token: string) => {
|
const onToken = (token: string) => {
|
||||||
curNodeChildren = node[3];
|
curNodeChildren = node[3];
|
||||||
if (curNodeChildren.has(token)) {
|
if (curNodeChildren.has(token)) {
|
||||||
@ -544,11 +542,7 @@ export class HostnameSmolTrie<Meta = any> extends Triebase<Meta> {
|
|||||||
node[4] = meta!;
|
node[4] = meta!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public whitelist(suffix: string, includeAllSubdomain = suffix[0] === '.', hostnameFromIndex = 0) {
|
public whitelist(suffix: string, includeAllSubdomain = suffix[0] === '.', hostnameFromIndex = suffix[0] === '.' ? 1 : 0) {
|
||||||
if (suffix[0] === '.') {
|
|
||||||
hostnameFromIndex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokens = hostnameToTokens(suffix, hostnameFromIndex);
|
const tokens = hostnameToTokens(suffix, hostnameFromIndex);
|
||||||
const res = this.getSingleChildLeaf(tokens);
|
const res = this.getSingleChildLeaf(tokens);
|
||||||
|
|
||||||
@ -584,7 +578,7 @@ export class HostnameTrie<Meta = any> extends Triebase<Meta> {
|
|||||||
return this.$size;
|
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;
|
let node: TrieNode<Meta> = this.$root;
|
||||||
|
|
||||||
const onToken = (token: string) => {
|
const onToken = (token: string) => {
|
||||||
@ -599,10 +593,6 @@ export class HostnameTrie<Meta = any> extends Triebase<Meta> {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (hostnameFromIndex === 0 && suffix[0] === '.') {
|
|
||||||
hostnameFromIndex = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When walkHostnameTokens returns true, we should skip the rest
|
// When walkHostnameTokens returns true, we should skip the rest
|
||||||
if (walkHostnameTokens(suffix, onToken, hostnameFromIndex)) {
|
if (walkHostnameTokens(suffix, onToken, hostnameFromIndex)) {
|
||||||
return;
|
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[]) {
|
// function deepEqualArray(a: string[], b: string[]) {
|
||||||
// let len = a.length;
|
// let len = a.length;
|
||||||
// if (len !== b.length) return false;
|
// if (len !== b.length) return false;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user