mirror of
https://github.com/SukkaW/Surge.git
synced 2025-12-13 01:30:37 +08:00
Feat: trie dump with sort & FIFO
This commit is contained in:
parent
8ea31fc208
commit
f6eb8b0a4c
63
Build/lib/fifo.ts
Normal file
63
Build/lib/fifo.ts
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { invariant } from 'foxact/invariant';
|
import { invariant } from 'foxact/invariant';
|
||||||
import createKeywordFilter from '../aho-corasick';
|
import createKeywordFilter from '../aho-corasick';
|
||||||
import { buildParseDomainMap, sortDomains } from '../stable-sort-domain';
|
import { buildParseDomainMap } from '../stable-sort-domain';
|
||||||
import { RuleOutput } from './base';
|
import { RuleOutput } from './base';
|
||||||
import type { SingboxSourceFormat } from '../singbox';
|
import type { SingboxSourceFormat } from '../singbox';
|
||||||
import { appendArrayFromSet } from '../misc';
|
import { appendArrayFromSet } from '../misc';
|
||||||
@ -27,7 +27,7 @@ export class DomainsetOutput extends RuleOutput<Preprocessed> {
|
|||||||
this.apexDomainMap = domainMap;
|
this.apexDomainMap = domainMap;
|
||||||
this.subDomainMap = subdomainMap;
|
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');
|
sorted.push('this_ruleset_is_made_by_sukkaw.ruleset.skk.moe');
|
||||||
|
|
||||||
return sorted;
|
return sorted;
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
import { fastStringArrayJoin } from './misc';
|
import { fastStringArrayJoin } from './misc';
|
||||||
import util from 'node:util';
|
import util from 'node:util';
|
||||||
import { noop } from 'foxact/noop';
|
import { noop } from 'foxact/noop';
|
||||||
|
import FIFO from './fifo';
|
||||||
|
|
||||||
type TrieNode<Meta = any> = [
|
type TrieNode<Meta = any> = [
|
||||||
boolean, /** end */
|
boolean, /** end */
|
||||||
@ -205,6 +206,49 @@ abstract class Triebase<Meta = any> {
|
|||||||
} while (nodeStack.length);
|
} 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 {
|
protected getSingleChildLeaf(tokens: string[]): FindSingleChildLeafResult<Meta> | null {
|
||||||
let toPrune: TrieNode | null = null;
|
let toPrune: TrieNode | null = null;
|
||||||
let tokenToPrune: string | null = null;
|
let tokenToPrune: string | null = null;
|
||||||
@ -331,7 +375,7 @@ abstract class Triebase<Meta = any> {
|
|||||||
results.push(subdomain ? '.' + d : d);
|
results.push(subdomain ? '.' + d : d);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.walk(handleSuffix);
|
this.walkWithSort(handleSuffix);
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user