mirror of
https://github.com/SukkaW/Surge.git
synced 2025-12-15 02:30:37 +08:00
Replace sortDomains w/ trie dump sort
This commit is contained in:
parent
f6eb8b0a4c
commit
2bbc122b85
@ -103,9 +103,9 @@ export function withBannerArray(title: string, description: string[] | readonly
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
export function mergeHeaders<T extends RequestInit['headers'] | HeadersInit>(headersA: T | undefined, headersB: T): T {
|
export function mergeHeaders<T extends RequestInit['headers'] | HeadersInit>(headersA: T | undefined, headersB: T | undefined): T {
|
||||||
if (headersA == null) {
|
if (headersA == null) {
|
||||||
return headersB;
|
return headersB!;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(headersB)) {
|
if (Array.isArray(headersB)) {
|
||||||
|
|||||||
@ -88,14 +88,6 @@ export abstract class RuleOutput<TPreprocessed = unknown> {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected apexDomainMap: Map<string, string> | null = null;
|
|
||||||
protected subDomainMap: Map<string, string> | null = null;
|
|
||||||
withDomainMap(apexDomainMap: Map<string, string>, subDomainMap: Map<string, string>) {
|
|
||||||
this.apexDomainMap = apexDomainMap;
|
|
||||||
this.subDomainMap = subDomainMap;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
addDomain(domain: string) {
|
addDomain(domain: string) {
|
||||||
this.domainTrie.add(domain);
|
this.domainTrie.add(domain);
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@ -20,13 +20,8 @@ export class DomainsetOutput extends RuleOutput<Preprocessed> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
results.push(domain);
|
results.push(domain);
|
||||||
});
|
}, true);
|
||||||
|
|
||||||
if (!this.apexDomainMap || !this.subDomainMap) {
|
|
||||||
const { domainMap, subdomainMap } = buildParseDomainMap(results);
|
|
||||||
this.apexDomainMap = domainMap;
|
|
||||||
this.subDomainMap = subdomainMap;
|
|
||||||
}
|
|
||||||
const sorted = results;
|
const sorted = results;
|
||||||
sorted.push('this_ruleset_is_made_by_sukkaw.ruleset.skk.moe');
|
sorted.push('this_ruleset_is_made_by_sukkaw.ruleset.skk.moe');
|
||||||
|
|
||||||
@ -63,9 +58,21 @@ export class DomainsetOutput extends RuleOutput<Preprocessed> {
|
|||||||
} satisfies SingboxSourceFormat);
|
} satisfies SingboxSourceFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected apexDomainMap: Map<string, string> | null = null;
|
||||||
|
protected subDomainMap: Map<string, string> | null = null;
|
||||||
|
withDomainMap(apexDomainMap: Map<string, string>, subDomainMap: Map<string, string>) {
|
||||||
|
this.apexDomainMap = apexDomainMap;
|
||||||
|
this.subDomainMap = subDomainMap;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
getStatMap() {
|
getStatMap() {
|
||||||
invariant(this.$preprocessed, 'Non dumped yet');
|
invariant(this.$preprocessed, 'Non dumped yet');
|
||||||
invariant(this.apexDomainMap, 'Missing apex domain map');
|
|
||||||
|
if (!this.apexDomainMap || !this.subDomainMap) {
|
||||||
|
const { domainMap } = buildParseDomainMap(this.$preprocessed);
|
||||||
|
this.apexDomainMap = domainMap;
|
||||||
|
}
|
||||||
|
|
||||||
return Array.from(this.$preprocessed
|
return Array.from(this.$preprocessed
|
||||||
.reduce<Map<string, number>>(
|
.reduce<Map<string, number>>(
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import createKeywordFilter from '../aho-corasick';
|
|||||||
import { appendArrayInPlace } from '../append-array-in-place';
|
import { appendArrayInPlace } from '../append-array-in-place';
|
||||||
import { appendArrayFromSet } from '../misc';
|
import { appendArrayFromSet } from '../misc';
|
||||||
import type { SingboxSourceFormat } from '../singbox';
|
import type { SingboxSourceFormat } from '../singbox';
|
||||||
import { sortDomains } from '../stable-sort-domain';
|
|
||||||
import { RuleOutput } from './base';
|
import { RuleOutput } from './base';
|
||||||
import picocolors from 'picocolors';
|
import picocolors from 'picocolors';
|
||||||
import { normalizeDomain } from '../normalize-domain';
|
import { normalizeDomain } from '../normalize-domain';
|
||||||
@ -24,9 +23,9 @@ export class RulesetOutput extends RuleOutput<Preprocessed> {
|
|||||||
const domainSuffixes: string[] = [];
|
const domainSuffixes: string[] = [];
|
||||||
const sortedDomainRules: string[] = [];
|
const sortedDomainRules: string[] = [];
|
||||||
|
|
||||||
for (const domain of sortDomains(this.domainTrie.dump(), this.apexDomainMap, this.subDomainMap)) {
|
this.domainTrie.dump((domain) => {
|
||||||
if (kwfilter(domain)) {
|
if (kwfilter(domain)) {
|
||||||
continue;
|
return;
|
||||||
}
|
}
|
||||||
if (domain[0] === '.') {
|
if (domain[0] === '.') {
|
||||||
domainSuffixes.push(domain.slice(1));
|
domainSuffixes.push(domain.slice(1));
|
||||||
@ -35,7 +34,7 @@ export class RulesetOutput extends RuleOutput<Preprocessed> {
|
|||||||
domains.push(domain);
|
domains.push(domain);
|
||||||
sortedDomainRules.push(`DOMAIN,${domain}`);
|
sortedDomainRules.push(`DOMAIN,${domain}`);
|
||||||
}
|
}
|
||||||
}
|
}, true);
|
||||||
|
|
||||||
return [domains, domainSuffixes, sortedDomainRules] satisfies Preprocessed;
|
return [domains, domainSuffixes, sortedDomainRules] satisfies Preprocessed;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -177,33 +177,76 @@ abstract class Triebase<Meta = any> {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static bfsResults: [node: TrieNode | null, suffix: string[]] = [null, []];
|
||||||
|
|
||||||
|
private static bfs<Meta>(this: void, nodeStack: FIFO<TrieNode<Meta>>, suffixStack: FIFO<string[]>) {
|
||||||
|
const node = nodeStack.dequeue()!;
|
||||||
|
const suffix = suffixStack.dequeue()!;
|
||||||
|
|
||||||
|
node[3].forEach((childNode, k) => {
|
||||||
|
// Pushing the child node to the stack for next iteration of DFS
|
||||||
|
nodeStack.enqueue(childNode);
|
||||||
|
|
||||||
|
suffixStack.enqueue([k, ...suffix]);
|
||||||
|
});
|
||||||
|
|
||||||
|
Triebase.bfsResults[0] = node;
|
||||||
|
Triebase.bfsResults[1] = suffix;
|
||||||
|
|
||||||
|
return Triebase.bfsResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bfsWithSort<Meta>(this: void, nodeStack: FIFO<TrieNode<Meta>>, suffixStack: FIFO<string[]>) {
|
||||||
|
const 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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Triebase.bfsResults[0] = node;
|
||||||
|
Triebase.bfsResults[1] = suffix;
|
||||||
|
|
||||||
|
return Triebase.bfsResults;
|
||||||
|
}
|
||||||
|
|
||||||
private walk(
|
private walk(
|
||||||
onMatches: (suffix: string[], subdomain: boolean, meta: Meta) => void,
|
onMatches: (suffix: string[], subdomain: boolean, meta: Meta) => void,
|
||||||
initialNode = this.$root,
|
initialNode = this.$root,
|
||||||
initialSuffix: string[] = []
|
initialSuffix: string[] = [],
|
||||||
|
withSort = false
|
||||||
) {
|
) {
|
||||||
const nodeStack: Array<TrieNode<Meta>> = [initialNode];
|
const bfsImpl = withSort ? Triebase.bfsWithSort : Triebase.bfs;
|
||||||
|
|
||||||
|
const nodeStack = new FIFO<TrieNode<Meta>>();
|
||||||
|
nodeStack.enqueue(initialNode);
|
||||||
|
|
||||||
// Resolving initial string (begin the start of the stack)
|
// Resolving initial string (begin the start of the stack)
|
||||||
const suffixStack: string[][] = [initialSuffix];
|
const suffixStack = new FIFO<string[]>();
|
||||||
|
suffixStack.enqueue(initialSuffix);
|
||||||
|
|
||||||
let node: TrieNode<Meta> = initialNode;
|
let node: TrieNode<Meta> = initialNode;
|
||||||
|
let r;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
node = nodeStack.pop()!;
|
r = bfsImpl(nodeStack, suffixStack);
|
||||||
const suffix = suffixStack.pop()!;
|
node = r[0]!;
|
||||||
|
const suffix = r[1];
|
||||||
node[3].forEach((childNode, k) => {
|
|
||||||
// Pushing the child node to the stack for next iteration of DFS
|
|
||||||
nodeStack.push(childNode);
|
|
||||||
|
|
||||||
suffixStack.push([k, ...suffix]);
|
|
||||||
});
|
|
||||||
|
|
||||||
// If the node is a sentinel, we push the suffix to the results
|
// If the node is a sentinel, we push the suffix to the results
|
||||||
if (node[0]) {
|
if (node[0]) {
|
||||||
onMatches(suffix, node[1], node[4]);
|
onMatches(suffix, node[1], node[4]);
|
||||||
}
|
}
|
||||||
} while (nodeStack.length);
|
} while (nodeStack.size);
|
||||||
};
|
};
|
||||||
|
|
||||||
static compare(this: void, a: string, b: string) {
|
static compare(this: void, a: string, b: string) {
|
||||||
@ -360,9 +403,9 @@ abstract class Triebase<Meta = any> {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
public dump(onSuffix: (suffix: string) => void): void;
|
public dump(onSuffix: (suffix: string) => void, withSort?: boolean): void;
|
||||||
public dump(): string[];
|
public dump(onSuffix?: null, withSort?: boolean): string[];
|
||||||
public dump(onSuffix?: (suffix: string) => void): string[] | void {
|
public dump(onSuffix?: ((suffix: string) => void) | null, withSort = false): string[] | void {
|
||||||
const results: string[] = [];
|
const results: string[] = [];
|
||||||
|
|
||||||
const handleSuffix = onSuffix
|
const handleSuffix = onSuffix
|
||||||
@ -375,28 +418,36 @@ abstract class Triebase<Meta = any> {
|
|||||||
results.push(subdomain ? '.' + d : d);
|
results.push(subdomain ? '.' + d : d);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.walkWithSort(handleSuffix);
|
if (withSort) {
|
||||||
|
this.walkWithSort(handleSuffix);
|
||||||
|
} else {
|
||||||
|
this.walk(handleSuffix);
|
||||||
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|
||||||
public dumpMeta(onMeta: (meta: Meta) => void): void;
|
public dumpMeta(onMeta: (meta: Meta) => void, withSort?: boolean): void;
|
||||||
public dumpMeta(): Meta[];
|
public dumpMeta(onMeta?: null, withSort?: boolean): Meta[];
|
||||||
public dumpMeta(onMeta?: (meta: Meta) => void): Meta[] | void {
|
public dumpMeta(onMeta?: ((meta: Meta) => void) | null, withSort = false): Meta[] | void {
|
||||||
const results: Meta[] = [];
|
const results: Meta[] = [];
|
||||||
|
|
||||||
const handleMeta = onMeta
|
const handleMeta = onMeta
|
||||||
? (_suffix: string[], _subdomain: boolean, meta: Meta) => onMeta(meta)
|
? (_suffix: string[], _subdomain: boolean, meta: Meta) => onMeta(meta)
|
||||||
: (_suffix: string[], _subdomain: boolean, meta: Meta) => results.push(meta);
|
: (_suffix: string[], _subdomain: boolean, meta: Meta) => results.push(meta);
|
||||||
|
|
||||||
this.walk(handleMeta);
|
if (withSort) {
|
||||||
|
this.walkWithSort(handleMeta);
|
||||||
|
} else {
|
||||||
|
this.walk(handleMeta);
|
||||||
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|
||||||
public dumpWithMeta(onSuffix: (suffix: string, meta: Meta | undefined) => void): void;
|
public dumpWithMeta(onSuffix: (suffix: string, meta: Meta | undefined) => void, withSort?: boolean): void;
|
||||||
public dumpWithMeta(): Array<[string, Meta | undefined]>;
|
public dumpWithMeta(onMeta?: null, withSort?: boolean): Array<[string, Meta | undefined]>;
|
||||||
public dumpWithMeta(onSuffix?: (suffix: string, meta: Meta | undefined) => void): Array<[string, Meta | undefined]> | void {
|
public dumpWithMeta(onSuffix?: ((suffix: string, meta: Meta | undefined) => void) | null, withSort = false): Array<[string, Meta | undefined]> | void {
|
||||||
const results: Array<[string, Meta | undefined]> = [];
|
const results: Array<[string, Meta | undefined]> = [];
|
||||||
|
|
||||||
const handleSuffix = onSuffix
|
const handleSuffix = onSuffix
|
||||||
@ -409,7 +460,11 @@ abstract class Triebase<Meta = any> {
|
|||||||
results.push([subdomain ? '.' + d : d, meta]);
|
results.push([subdomain ? '.' + d : d, meta]);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.walk(handleSuffix);
|
if (withSort) {
|
||||||
|
this.walkWithSort(handleSuffix);
|
||||||
|
} else {
|
||||||
|
this.walk(handleSuffix);
|
||||||
|
}
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user