filter: Reduce redundant path and pattern splitting

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)
This commit is contained in:
Michael Eischer 2020-10-07 14:27:59 +02:00
parent e73c281142
commit b8eacd1364

View file

@ -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
}