filter: special case patterns without globbing characters

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)
This commit is contained in:
Michael Eischer 2020-10-07 20:55:43 +02:00
parent 54a124de3b
commit 0acc3c5923
1 changed files with 26 additions and 9 deletions

View File

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