Skip to content

Glob filter *-*-*-*-*-*-*x takes ~6 s per item against an 80-dash line (56 s with one more *-): glob matching is exponential and, unlike regex, has no timeout #131

Description

@matt-edmondson

What's wrong

IsGlobMatch (TextFilter/TextFilter.cs ~line 415 at c4eec7b) hands every text token to DotNet.Glob's glob.IsMatch. It is called from DoesMatchGlob for the excluded, optional and required tokens (~lines 341, 352, 363). DotNet.Glob backtracks recursively over *, so a pattern with several *s that fails near the end costs exponential time in the number of *s.

The regex path is protected by RegexMatchTimeout (~line 124), so that "a caller-supplied pattern cannot hang the calling thread". The glob path has no equivalent limit.

Reproduction (observed on net10.0, default ByWordAny)

TextFilter.IsMatch(new string('-', 80), pattern, TextFilterType.Glob):

pattern result time
*-*-*-*-*-*x false 825 ms
*-*-*-*-*-*-*x false 5,909 ms
*-*-*-*-*-*-*-*x false 55,733 ms

Against a token of 40 as, the pattern "*a"×10 + "*b" took 12,071 ms, and ×12 took 84,563 ms.

Expected: a few milliseconds. A wildcard match with only * and ? can run in O(n·m), or linear with the standard two-pointer star-backtrack.

Why it matters

An 80-dash line is an ordinary markdown or log separator. Filter() pays this cost for every item and every word, typically on the UI thread and on every keystroke as the user types. A filter box over log lines can freeze the app for minutes from a pattern a user could plausibly type.

Suggested fix / acceptance criteria

  • Collapse consecutive *s, and evaluate globs with a non-recursive wildcard matcher (two-pointer, backtracking only to the last *). Alternatively, translate the glob to a Regex compiled with RegexOptions.NonBacktracking, or with the existing RegexMatchTimeout.
  • The patterns above against an 80-character dash token complete in well under 100 ms.
  • Existing glob semantics are unchanged: case sensitivity, character classes, and slashes matched as ordinary characters (Glob * and ? never match across / or \, so any item containing a slash is filtered out #114).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions