Documentation
¶
Overview ¶
Package match compiles search patterns (regex or fixed strings) into a Matcher: rg's "grep-regex" equivalent. A Matcher extracts required literals from the pattern (prefix/inner/suffix), runs a SIMD-backed literal prefilter over whole buffers via bytes.Index/bytes.IndexByte, and falls through to a real regex engine only to confirm candidate hits on a single line. Plain literal patterns skip the regex engine entirely.
Every hot-path method takes []byte, never string, and is designed to run allocation-free in steady state once a Matcher is constructed.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CandidateKind ¶
type CandidateKind uint8
CandidateKind distinguishes a genuine match from a literal-prefilter hit that still needs full-regex confirmation on its enclosing line.
const ( // Confirmed means FindCandidate ran the real pattern directly (no // separate prefilter regex exists, e.g. a pure-literal pattern); the // hit is a genuine match and needs no further verification. Confirmed CandidateKind = iota // Candidate means FindCandidate matched only a literal prefilter; // the caller must locate the enclosing line and call Verify on it // before treating this as a real match. Candidate )
type CaseMode ¶
type CaseMode uint8
CaseMode selects how case is handled while matching.
const ( // CaseSensitive matches patterns exactly as written. CaseSensitive CaseMode = iota // CaseInsensitive folds case on both pattern and haystack. CaseInsensitive // CaseSmart is case-insensitive unless the pattern contains an // uppercase literal character, in which case it is case-sensitive. CaseSmart )
type Config ¶
type Config struct {
// Patterns are combined as an alternation (like ripgrep's -e).
Patterns []string
CaseMode CaseMode
// Word wraps each pattern in word-boundary looks (rg's -w).
Word bool
// Fixed treats Patterns as literal strings rather than regexes (-F).
Fixed bool
// LineRegexp anchors the combined pattern to whole lines (rg's -x):
// equivalent to wrapping it in ^(?:...)$ with per-line (not
// per-text) anchor semantics. Callers must never set both Word and
// LineRegexp -- they mirror rg's single shared BoundaryMode field,
// where the last of -w/-x given wins outright (see strategy.go's New
// doc for how this is implemented).
LineRegexp bool
// MultiLine compiles the pattern with Go's (?m) flag so `^`/`$` bind to
// '\n' line boundaries anywhere within a search window, not just its
// very start/end (rg's --null-data behavior: a record may span '\n', so
// `foo$` must anchor before an interior '\n', while `.` still does NOT
// match '\n'). No effect on -F/literal patterns (they carry no
// anchors) and redundant under LineRegexp (which already wraps in
// (?m)^...$). See strategy.go's newRegexMatcher.
MultiLine bool
}
Config describes how to compile one or more patterns into a Matcher. It is the sole construction-time input to New; Matcher implementations expose no runtime setters for case/word/fixed-string behavior, only query methods (see NonMatchingLineTerm).
type Matcher ¶
type Matcher interface {
// FindCandidate scans buf starting at byte offset start for the next
// possible match and reports its offset plus whether it is Confirmed
// or merely a Candidate. ok is false once no further candidates
// exist in buf[start:]. Implementations must not allocate in steady
// state — this is the whole-buffer hot-path scan (rg's
// find_candidate_line).
FindCandidate(buf []byte, start int) (off int, kind CandidateKind, ok bool)
// Verify reports whether the full pattern matches anywhere within
// line. Used to confirm a Candidate hit against exactly the one
// line that contains it.
Verify(line []byte) bool
// Find returns the leftmost match's byte bounds [s, e) within line.
// Callers that only need a yes/no + line (the common "path:line:text"
// case) should prefer Verify and skip Find entirely to avoid the
// extra work of locating exact bounds.
Find(line []byte) (s, e int, ok bool)
// NonMatchingLineTerm reports whether the compiled pattern is
// provably unable to match across a line-terminator byte ('\n').
// When true, a search.Searcher may use the fast whole-buffer
// candidate path (FindCandidate over the whole buffer, then expand
// to line boundaries); when false it must fall back to scanning
// line-by-line. This is the only capability a Searcher queries on a
// Matcher at runtime — all other behavior (case, word, fixed) is
// baked in at construction via Config.
NonMatchingLineTerm() bool
}
Matcher is a compiled pattern ready to search []byte haystacks. Every method operates on []byte only — implementations and callers must never convert to string on a hot path. A Matcher's compiled state is read-only after construction, so a single Matcher may be shared and called concurrently by multiple goroutines; any per-call scratch space an implementation needs must not be stored on the Matcher itself (pool it in the caller, e.g. per search.Searcher worker).
func New ¶
New compiles cfg into a Matcher.
Pipeline: parse patterns (or take them as literal strings under -F) -> resolve smart case -> combine into one alternation -> try the pure-literal fast path (Strategy 1) -> else run inner-literal extraction for a prefiltered-regex path (Strategy 2) -> else fall back to running the engine over the whole buffer (Strategy 3). Word wrapping (-w) is never baked into the compiled pattern; it is applied uniformly as a post-match boundary check (see word.go) regardless of which strategy compiled, since Go's regexp/syntax has no equivalent of rg's asymmetric half-word-boundary look used for -w.
LineRegexp (-x), unlike -w, IS baked directly into the compiled pattern text as a real ^(?:...)$ anchor pair, using Go's (?m) multi-line mode so ^/$ bind to LINE boundaries within a whole multi-line search buffer (rather than (?)-default text boundaries, which would only ever match at the very start/end of an entire file) -- see newRegexMatcher's doc. Wrapping the pattern text itself, rather than post-filtering match bounds the way -w does, is deliberate: it lets the ordinary regex engine (or FindAllIndex's anchor-aware scan, already used for any anchored pattern) enforce correctness even for patterns whose match depends on trying multiple alternatives per start position (e.g. `-x -e 'a|aa'` against "aa"), which a simple "restart at s+1 if the bounds don't span the whole line" retry loop -- as word.go uses for -w -- cannot get right in general.