Feat: trie dump with sort & FIFO

This commit is contained in:
SukkaW 2024-10-15 18:10:45 +08:00
parent 8ea31fc208
commit f6eb8b0a4c
3 changed files with 110 additions and 3 deletions

63
Build/lib/fifo.ts Normal file
View 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;
}
}
}

View File

@ -1,6 +1,6 @@
import { invariant } from 'foxact/invariant';
import createKeywordFilter from '../aho-corasick';
import { buildParseDomainMap, sortDomains } from '../stable-sort-domain';
import { buildParseDomainMap } from '../stable-sort-domain';
import { RuleOutput } from './base';
import type { SingboxSourceFormat } from '../singbox';
import { appendArrayFromSet } from '../misc';
@ -27,7 +27,7 @@ export class DomainsetOutput extends RuleOutput<Preprocessed> {
this.apexDomainMap = domainMap;
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');
return sorted;

View File

@ -5,6 +5,7 @@
import { fastStringArrayJoin } from './misc';
import util from 'node:util';
import { noop } from 'foxact/noop';
import FIFO from './fifo';
type TrieNode<Meta = any> = [
boolean, /** end */
@ -205,6 +206,49 @@ abstract class Triebase<Meta = any> {
} 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 {
let toPrune: TrieNode | null = null;
let tokenToPrune: string | null = null;
@ -331,7 +375,7 @@ abstract class Triebase<Meta = any> {
results.push(subdomain ? '.' + d : d);
};
this.walk(handleSuffix);
this.walkWithSort(handleSuffix);
return results;
};