mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-03 05:45:04 +03:00
fix: cache compiled RegExp in RTK compression hot path (#2553)
Integrated into release/v3.8.2
This commit is contained in:
@@ -10,6 +10,24 @@ let cache: RtkFilterDefinition[] | null = null;
|
||||
let cacheKey: string | null = null;
|
||||
let diagnostics: RtkFilterLoadDiagnostic[] = [];
|
||||
|
||||
// RegExp cache for matchPatterns — same pattern strings are tested against
|
||||
// many different lines across requests; avoid compiling them every time.
|
||||
const regexCache = new Map<string, RegExp>();
|
||||
|
||||
function cachedMatchPattern(pattern: string, value: string): boolean {
|
||||
const key = `${pattern}::im`;
|
||||
let re = regexCache.get(key);
|
||||
if (!re) {
|
||||
try {
|
||||
re = new RegExp(pattern, "im");
|
||||
regexCache.set(key, re);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return re.test(value);
|
||||
}
|
||||
|
||||
export interface RtkFilterLoadDiagnostic {
|
||||
source: "project" | "global" | "builtin";
|
||||
path?: string;
|
||||
@@ -196,23 +214,16 @@ export function matchRtkFilter(
|
||||
): RtkFilterDefinition | null {
|
||||
const detection = detectCommandType(text, command);
|
||||
const detectedCommand = detection.command ?? command ?? "";
|
||||
const matchesPattern = (pattern: string, value: string): boolean => {
|
||||
try {
|
||||
return new RegExp(pattern, "im").test(value);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
const filters = loadRtkFilters(options);
|
||||
return (
|
||||
filters.find((filter) => filter.commandTypes.includes(detection.type)) ??
|
||||
filters.find(
|
||||
(filter) =>
|
||||
detectedCommand &&
|
||||
filter.commandPatterns.some((pattern) => matchesPattern(pattern, detectedCommand))
|
||||
filter.commandPatterns.some((pattern) => cachedMatchPattern(pattern, detectedCommand))
|
||||
) ??
|
||||
filters.find((filter) =>
|
||||
filter.matchPatterns.some((pattern) => matchesPattern(pattern, text))
|
||||
filter.matchPatterns.some((pattern) => cachedMatchPattern(pattern, text))
|
||||
) ??
|
||||
filters.find((filter) => filter.commandTypes.includes("generic-output")) ??
|
||||
null
|
||||
|
||||
@@ -8,30 +8,41 @@ export interface LineFilterResult {
|
||||
appliedRules: string[];
|
||||
}
|
||||
|
||||
// ──────────────── RegExp cache ────────────────
|
||||
//
|
||||
// Patterns are static (loaded from filter JSON files on boot) but were being
|
||||
// compiled via `new RegExp(...)` on every call to applyLineFilter(). For a
|
||||
// busy proxy this means thousands of redundant RegExp instantiations per
|
||||
// second. Cache them here once.
|
||||
|
||||
const regexCache = new Map<string, RegExp>();
|
||||
|
||||
function cachedRegExp(pattern: string, flags: string): RegExp | null {
|
||||
const key = `${pattern}::${flags}`;
|
||||
const cached = regexCache.get(key);
|
||||
if (cached) return cached;
|
||||
try {
|
||||
const re = new RegExp(pattern, flags);
|
||||
regexCache.set(key, re);
|
||||
return re;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function compilePatterns(patterns: string[]): RegExp[] {
|
||||
return patterns.flatMap((pattern) => {
|
||||
try {
|
||||
return [new RegExp(pattern, "i")];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
const re = cachedRegExp(pattern, "i");
|
||||
return re ? [re] : [];
|
||||
});
|
||||
}
|
||||
|
||||
function compileGlobalPattern(pattern: string): RegExp | null {
|
||||
try {
|
||||
return new RegExp(pattern, "g");
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return cachedRegExp(pattern, "g");
|
||||
}
|
||||
|
||||
function compileBlobPattern(pattern: string): RegExp | null {
|
||||
try {
|
||||
return new RegExp(pattern, "im");
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return cachedRegExp(pattern, "im");
|
||||
}
|
||||
|
||||
function stripAnsi(text: string): string {
|
||||
|
||||
Reference in New Issue
Block a user