Skip to content

Commit 75f94cf

Browse files
author
Harry Marr
authored
Merge pull request #11 from hmarr/optimise-prefix-matches
Simple left-anchored patterns load and match faster
2 parents e69a556 + fa87071 commit 75f94cf

File tree

1 file changed

+41
-9
lines changed

1 file changed

+41
-9
lines changed

match.go

+41-9
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,57 @@ import (
88
)
99

1010
type pattern struct {
11-
pattern string
12-
regex *regexp.Regexp
11+
pattern string
12+
regex *regexp.Regexp
13+
leftAnchoredLiteral bool
1314
}
1415

1516
// newPattern creates a new pattern struct from a gitignore-style pattern string
1617
func newPattern(patternStr string) (pattern, error) {
17-
patternRegex, err := buildPatternRegex(patternStr)
18-
if err != nil {
19-
return pattern{}, err
18+
pat := pattern{pattern: patternStr}
19+
20+
if !strings.ContainsAny(patternStr, "*?\\") && patternStr[0] == os.PathSeparator {
21+
pat.leftAnchoredLiteral = true
22+
} else {
23+
patternRegex, err := buildPatternRegex(patternStr)
24+
if err != nil {
25+
return pattern{}, err
26+
}
27+
pat.regex = patternRegex
2028
}
2129

22-
return pattern{
23-
pattern: patternStr,
24-
regex: patternRegex,
25-
}, nil
30+
return pat, nil
2631
}
2732

2833
// match tests if the path provided matches the pattern
2934
func (p pattern) match(testPath string) (bool, error) {
35+
if p.leftAnchoredLiteral {
36+
prefix := p.pattern
37+
38+
// Strip the leading slash as we're anchored to the root already
39+
if prefix[0] == os.PathSeparator {
40+
prefix = prefix[1:]
41+
}
42+
43+
// If the pattern ends with a slash we can do a simple prefix match
44+
if prefix[len(prefix)-1] == os.PathSeparator {
45+
return strings.HasPrefix(testPath, prefix), nil
46+
}
47+
48+
// If the strings are the same length, check for an exact match
49+
if len(testPath) == len(prefix) {
50+
return testPath == prefix, nil
51+
}
52+
53+
// Otherwise check if the test path is a subdirectory of the pattern
54+
if len(testPath) > len(prefix) && testPath[len(prefix)] == os.PathSeparator {
55+
return testPath[:len(prefix)] == prefix, nil
56+
}
57+
58+
// Otherwise the test path must be shorter than the pattern, so it can't match
59+
return false, nil
60+
}
61+
3062
return p.regex.MatchString(testPath), nil
3163
}
3264

0 commit comments

Comments
 (0)