From 0acc3c592347db1ea65bae980e5c2b92406163c6 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Wed, 7 Oct 2020 20:55:43 +0200 Subject: [PATCH] filter: special case patterns without globbing characters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case a part of a path is a simple string, we can just check for equality without complex parsing in filepath.Match. name old time/op new time/op delta FilterLines-4 34.8ms ±17% 41.2ms ±23% +18.36% (p=0.000 n=10+10) FilterPatterns/Relative-4 21.7ms ± 6% 12.1ms ±23% -44.46% (p=0.000 n=10+10) FilterPatterns/Absolute-4 10.0ms ± 5% 9.1ms ±11% -9.80% (p=0.006 n=10+9) FilterPatterns/Wildcard-4 47.0ms ± 7% 42.2ms ± 5% -10.19% (p=0.000 n=9+10) FilterPatterns/ManyNoMatch-4 190ms ± 1% 131ms ±20% -31.47% (p=0.000 n=8+10) name old alloc/op new alloc/op delta FilterPatterns/Relative-4 3.57MB ± 0% 3.57MB ± 0% ~ (p=0.870 n=9+9) FilterPatterns/Absolute-4 3.57MB ± 0% 3.57MB ± 0% ~ (p=0.145 n=10+10) FilterPatterns/Wildcard-4 14.3MB ± 0% 19.7MB ± 0% +37.91% (p=0.000 n=10+10) FilterPatterns/ManyNoMatch-4 3.57MB ± 0% 3.57MB ± 0% ~ (p=0.421 n=10+9) name old allocs/op new allocs/op delta FilterPatterns/Relative-4 22.2k ± 0% 22.2k ± 0% ~ (all equal) FilterPatterns/Absolute-4 22.2k ± 0% 22.2k ± 0% ~ (all equal) FilterPatterns/Wildcard-4 88.7k ± 0% 88.7k ± 0% ~ (all equal) FilterPatterns/ManyNoMatch-4 22.2k ± 0% 22.2k ± 0% ~ (all equal) --- internal/filter/filter.go | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/internal/filter/filter.go b/internal/filter/filter.go index 90526ddcc..f1aa20e94 100644 --- a/internal/filter/filter.go +++ b/internal/filter/filter.go @@ -11,8 +11,13 @@ import ( // second argument. var ErrBadString = errors.New("filter.Match: string is empty") +type patternPart struct { + pattern string + isSimple bool +} + // Pattern represents a preparsed filter pattern -type Pattern []string +type Pattern []patternPart func prepareStr(str string) ([]string, error) { if str == "" { @@ -35,7 +40,14 @@ func preparePattern(pattern string) Pattern { pattern = strings.Replace(pattern, string(filepath.Separator), "/", -1) } - return strings.Split(pattern, "/") + parts := strings.Split(pattern, "/") + patterns := make([]patternPart, len(parts)) + for i, part := range parts { + isSimple := !strings.ContainsAny(part, "\\[]*?") + patterns[i] = patternPart{part, isSimple} + } + + return patterns } // Match returns true if str matches the pattern. When the pattern is @@ -89,7 +101,7 @@ func ChildMatch(pattern, str string) (matched bool, err error) { } func childMatch(patterns Pattern, strs []string) (matched bool, err error) { - if patterns[0] != "" { + if patterns[0].pattern != "" { // relative pattern can always be nested down return true, nil } @@ -112,7 +124,7 @@ func childMatch(patterns Pattern, strs []string) (matched bool, err error) { func hasDoubleWildcard(list Pattern) (ok bool, pos int) { for i, item := range list { - if item == "**" { + if item.pattern == "**" { return true, i } } @@ -131,7 +143,7 @@ func match(patterns Pattern, strs []string) (matched bool, err error) { newPat := newPat[:pos+i] // in the first iteration the wildcard expands to nothing if i > 0 { - newPat[pos+i-1] = "*" + newPat[pos+i-1] = patternPart{"*", false} } newPat = append(newPat, patterns[pos+1:]...) @@ -155,16 +167,21 @@ func match(patterns Pattern, strs []string) (matched bool, err error) { if len(patterns) <= len(strs) { maxOffset := len(strs) - len(patterns) // special case absolute patterns - if patterns[0] == "" { + if patterns[0].pattern == "" { maxOffset = 0 } outer: for offset := maxOffset; offset >= 0; offset-- { for i := len(patterns) - 1; i >= 0; i-- { - ok, err := filepath.Match(patterns[i], strs[offset+i]) - if err != nil { - return false, errors.Wrap(err, "Match") + var ok bool + if patterns[i].isSimple { + ok = patterns[i].pattern == strs[offset+i] + } else { + ok, err = filepath.Match(patterns[i].pattern, strs[offset+i]) + if err != nil { + return false, errors.Wrap(err, "Match") + } } if !ok {