From b8eacd13641525d939200dcee5749abf928c9ec9 Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Wed, 7 Oct 2020 14:27:59 +0200 Subject: [PATCH] filter: Reduce redundant path and pattern splitting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A single call to filter.List will split the path only once and also split each search pattern only once and use it for both match and childMatch. name old time/op new time/op delta FilterPatterns/Relative-4 62.1ms ±15% 30.3ms ±10% -51.22% (p=0.000 n=9+10) FilterPatterns/Absolute-4 111ms ±10% 49ms ± 3% -56.08% (p=0.000 n=10+8) FilterPatterns/Wildcard-4 393ms ±15% 345ms ± 9% -12.30% (p=0.000 n=10+10) FilterPatterns/ManyNoMatch-4 10.0s ± 3% 3.9s ± 2% -60.53% (p=0.000 n=10+9) name old alloc/op new alloc/op delta FilterPatterns/Relative-4 16.4MB ± 0% 4.6MB ± 0% -71.76% (p=0.000 n=10+9) FilterPatterns/Absolute-4 31.4MB ± 0% 8.5MB ± 0% -72.77% (p=0.000 n=9+10) FilterPatterns/Wildcard-4 168MB ± 0% 146MB ± 0% -13.19% (p=0.000 n=10+9) FilterPatterns/ManyNoMatch-4 3.23GB ± 0% 0.91GB ± 0% -71.96% (p=0.000 n=10+9) name old allocs/op new allocs/op delta FilterPatterns/Relative-4 178k ± 0% 67k ± 0% -62.50% (p=0.000 n=10+10) FilterPatterns/Absolute-4 266k ± 0% 89k ± 0% -66.67% (p=0.000 n=10+10) FilterPatterns/Wildcard-4 1.87M ± 0% 1.70M ± 0% -9.47% (p=0.000 n=10+10) FilterPatterns/ManyNoMatch-4 17.7M ± 0% 4.5M ± 0% -74.87% (p=0.000 n=9+10) --- internal/filter/filter.go | 71 +++++++++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/internal/filter/filter.go b/internal/filter/filter.go index 74deddb03..795afd179 100644 --- a/internal/filter/filter.go +++ b/internal/filter/filter.go @@ -11,6 +11,32 @@ import ( // second argument. var ErrBadString = errors.New("filter.Match: string is empty") +type filterPattern []string + +func prepareStr(str string) ([]string, error) { + if str == "" { + return nil, ErrBadString + } + + // convert file path separator to '/' + if filepath.Separator != '/' { + str = strings.Replace(str, string(filepath.Separator), "/", -1) + } + + return strings.Split(str, "/"), nil +} + +func preparePattern(pattern string) filterPattern { + pattern = filepath.Clean(pattern) + + // convert file path separator to '/' + if filepath.Separator != '/' { + pattern = strings.Replace(pattern, string(filepath.Separator), "/", -1) + } + + return strings.Split(pattern, "/") +} + // Match returns true if str matches the pattern. When the pattern is // malformed, filepath.ErrBadPattern is returned. The empty pattern matches // everything, when str is the empty string ErrBadString is returned. @@ -26,21 +52,13 @@ func Match(pattern, str string) (matched bool, err error) { return true, nil } - pattern = filepath.Clean(pattern) + patterns := preparePattern(pattern) + strs, err := prepareStr(str) - if str == "" { - return false, ErrBadString + if err != nil { + return false, err } - // convert file path separator to '/' - if filepath.Separator != '/' { - pattern = strings.Replace(pattern, string(filepath.Separator), "/", -1) - str = strings.Replace(str, string(filepath.Separator), "/", -1) - } - - patterns := strings.Split(pattern, "/") - strs := strings.Split(str, "/") - return match(patterns, strs) } @@ -59,21 +77,13 @@ func ChildMatch(pattern, str string) (matched bool, err error) { return true, nil } - pattern = filepath.Clean(pattern) + patterns := preparePattern(pattern) + strs, err := prepareStr(str) - if str == "" { - return false, ErrBadString + if err != nil { + return false, err } - // convert file path separator to '/' - if filepath.Separator != '/' { - pattern = strings.Replace(pattern, string(filepath.Separator), "/", -1) - str = strings.Replace(str, string(filepath.Separator), "/", -1) - } - - patterns := strings.Split(pattern, "/") - strs := strings.Split(str, "/") - return childMatch(patterns, strs) } @@ -162,17 +172,26 @@ func match(patterns, strs []string) (matched bool, err error) { // List returns true if str matches one of the patterns. Empty patterns are // ignored. func List(patterns []string, str string) (matched bool, childMayMatch bool, err error) { + if len(patterns) == 0 { + return false, false, nil + } + + strs, err := prepareStr(str) + if err != nil { + return false, false, err + } for _, pat := range patterns { if pat == "" { continue } - m, err := Match(pat, str) + pats := preparePattern(pat) + m, err := match(pats, strs) if err != nil { return false, false, err } - c, err := ChildMatch(pat, str) + c, err := childMatch(pats, strs) if err != nil { return false, false, err }