What's wrong
TextFilter.cs:84-85 declares two process-lifetime static caches:
private static readonly ConcurrentDictionary<string, Regex> RegexCache = new();
private static readonly ConcurrentDictionary<string, Glob> GlobCache = new();
They're populated in ResolveGlob (lines 376-386) and DoesMatchRegex (lines 409-427), keyed by the raw filter/token text plus a 2-character case-sensitivity prefix (CacheKey). There is no eviction, expiry, size cap, or Clear/TryRemove call anywhere in the file — every distinct pattern ever passed in stays cached for the lifetime of the process.
Concrete failure scenario
The library's own commit history describes its intended use case as a live, keystroke-driven filter box (e.g. commit b249339, "a list that throws mid-keystroke on a pathological pattern"). In that use case, each keystroke of an incremental search — "h", "he", "hel", "hell", "hello", … — produces a distinct cache key that's never evicted. Adding the case-sensitivity feature (CacheKey's "i:"/"s:" prefix) further doubled the key space for the same pattern text. In a long-lived desktop/UI process — this library is consumed by ktsu's ImGui-based tools, which run indefinitely — cache size grows directly with how much text a user has typed into filter boxes over the app's entire lifetime, with no upper bound. This is unbounded memory growth, not bounded by the size of the data being filtered.
Suggested fix
Bound the caches — e.g. a max-entry cap with simple eviction (LRU, or clear-and-restart once a threshold is hit), or a time-based expiry window. Even a generously-sized fixed cap (a few thousand entries) would close this without materially affecting the hot-path hit cost.
What's wrong
TextFilter.cs:84-85declares two process-lifetime static caches:They're populated in
ResolveGlob(lines 376-386) andDoesMatchRegex(lines 409-427), keyed by the raw filter/token text plus a 2-character case-sensitivity prefix (CacheKey). There is no eviction, expiry, size cap, orClear/TryRemovecall anywhere in the file — every distinct pattern ever passed in stays cached for the lifetime of the process.Concrete failure scenario
The library's own commit history describes its intended use case as a live, keystroke-driven filter box (e.g. commit
b249339, "a list that throws mid-keystroke on a pathological pattern"). In that use case, each keystroke of an incremental search —"h","he","hel","hell","hello", … — produces a distinct cache key that's never evicted. Adding the case-sensitivity feature (CacheKey's"i:"/"s:"prefix) further doubled the key space for the same pattern text. In a long-lived desktop/UI process — this library is consumed by ktsu's ImGui-based tools, which run indefinitely — cache size grows directly with how much text a user has typed into filter boxes over the app's entire lifetime, with no upper bound. This is unbounded memory growth, not bounded by the size of the data being filtered.Suggested fix
Bound the caches — e.g. a max-entry cap with simple eviction (LRU, or clear-and-restart once a threshold is hit), or a time-based expiry window. Even a generously-sized fixed cap (a few thousand entries) would close this without materially affecting the hot-path hit cost.