forked from TrueCloudLab/restic
Optimize filter pattern matching
By replacing "**" with "", checking for this special path component can be reduced to a length-zero check. name old time/op new time/op delta FilterLines-8 44.7ms ± 5% 44.9ms ± 5% ~ (p=0.631 n=10+10) FilterPatterns/Relative-8 13.6ms ± 4% 13.4ms ± 5% ~ (p=0.165 n=10+10) FilterPatterns/Absolute-8 10.9ms ± 5% 10.7ms ± 4% ~ (p=0.052 n=10+10) FilterPatterns/Wildcard-8 53.7ms ± 5% 50.4ms ± 5% -6.00% (p=0.000 n=10+10) FilterPatterns/ManyNoMatch-8 128ms ± 2% 95ms ± 1% -25.54% (p=0.000 n=10+10) name old alloc/op new alloc/op delta FilterPatterns/Relative-8 3.57MB ± 0% 3.57MB ± 0% ~ (p=1.000 n=9+8) FilterPatterns/Absolute-8 3.57MB ± 0% 3.57MB ± 0% ~ (p=0.903 n=9+8) FilterPatterns/Wildcard-8 19.7MB ± 0% 19.7MB ± 0% -0.00% (p=0.022 n=10+9) FilterPatterns/ManyNoMatch-8 3.57MB ± 0% 3.57MB ± 0% ~ (all equal) name old allocs/op new allocs/op delta FilterPatterns/Relative-8 22.2k ± 0% 22.2k ± 0% ~ (all equal) FilterPatterns/Absolute-8 22.2k ± 0% 22.2k ± 0% ~ (all equal) FilterPatterns/Wildcard-8 88.7k ± 0% 88.7k ± 0% ~ (all equal) FilterPatterns/ManyNoMatch-8 22.2k ± 0% 22.2k ± 0% ~ (all equal)
This commit is contained in:
parent
8388e67c4b
commit
740758a5fa
1 changed files with 21 additions and 11 deletions
|
@ -12,7 +12,7 @@ import (
|
||||||
var ErrBadString = errors.New("filter.Match: string is empty")
|
var ErrBadString = errors.New("filter.Match: string is empty")
|
||||||
|
|
||||||
type patternPart struct {
|
type patternPart struct {
|
||||||
pattern string
|
pattern string // First is "/" for absolute pattern; "" for "**".
|
||||||
isSimple bool
|
isSimple bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,25 +23,35 @@ func prepareStr(str string) ([]string, error) {
|
||||||
if str == "" {
|
if str == "" {
|
||||||
return nil, ErrBadString
|
return nil, ErrBadString
|
||||||
}
|
}
|
||||||
|
return splitPath(str), nil
|
||||||
str = filepath.ToSlash(str)
|
|
||||||
return strings.Split(str, "/"), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func preparePattern(pattern string) Pattern {
|
func preparePattern(pattern string) Pattern {
|
||||||
pattern = filepath.Clean(pattern)
|
parts := splitPath(filepath.Clean(pattern))
|
||||||
pattern = filepath.ToSlash(pattern)
|
|
||||||
|
|
||||||
parts := strings.Split(pattern, "/")
|
|
||||||
patterns := make([]patternPart, len(parts))
|
patterns := make([]patternPart, len(parts))
|
||||||
for i, part := range parts {
|
for i, part := range parts {
|
||||||
isSimple := !strings.ContainsAny(part, "\\[]*?")
|
isSimple := !strings.ContainsAny(part, "\\[]*?")
|
||||||
|
// Replace "**" with the empty string to get faster comparisons
|
||||||
|
// (length-check only) in hasDoubleWildcard.
|
||||||
|
if part == "**" {
|
||||||
|
part = ""
|
||||||
|
}
|
||||||
patterns[i] = patternPart{part, isSimple}
|
patterns[i] = patternPart{part, isSimple}
|
||||||
}
|
}
|
||||||
|
|
||||||
return patterns
|
return patterns
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Split p into path components. Assuming p has been Cleaned, no component
|
||||||
|
// will be empty. For absolute paths, the first component is "/".
|
||||||
|
func splitPath(p string) []string {
|
||||||
|
parts := strings.Split(filepath.ToSlash(p), "/")
|
||||||
|
if parts[0] == "" {
|
||||||
|
parts[0] = "/"
|
||||||
|
}
|
||||||
|
return parts
|
||||||
|
}
|
||||||
|
|
||||||
// Match returns true if str matches the pattern. When the pattern is
|
// Match returns true if str matches the pattern. When the pattern is
|
||||||
// malformed, filepath.ErrBadPattern is returned. The empty pattern matches
|
// malformed, filepath.ErrBadPattern is returned. The empty pattern matches
|
||||||
// everything, when str is the empty string ErrBadString is returned.
|
// everything, when str is the empty string ErrBadString is returned.
|
||||||
|
@ -93,7 +103,7 @@ func ChildMatch(pattern, str string) (matched bool, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func childMatch(patterns Pattern, strs []string) (matched bool, err error) {
|
func childMatch(patterns Pattern, strs []string) (matched bool, err error) {
|
||||||
if patterns[0].pattern != "" {
|
if patterns[0].pattern != "/" {
|
||||||
// relative pattern can always be nested down
|
// relative pattern can always be nested down
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
@ -116,7 +126,7 @@ func childMatch(patterns Pattern, strs []string) (matched bool, err error) {
|
||||||
|
|
||||||
func hasDoubleWildcard(list Pattern) (ok bool, pos int) {
|
func hasDoubleWildcard(list Pattern) (ok bool, pos int) {
|
||||||
for i, item := range list {
|
for i, item := range list {
|
||||||
if item.pattern == "**" {
|
if item.pattern == "" {
|
||||||
return true, i
|
return true, i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -159,7 +169,7 @@ func match(patterns Pattern, strs []string) (matched bool, err error) {
|
||||||
if len(patterns) <= len(strs) {
|
if len(patterns) <= len(strs) {
|
||||||
maxOffset := len(strs) - len(patterns)
|
maxOffset := len(strs) - len(patterns)
|
||||||
// special case absolute patterns
|
// special case absolute patterns
|
||||||
if patterns[0].pattern == "" {
|
if patterns[0].pattern == "/" {
|
||||||
maxOffset = 0
|
maxOffset = 0
|
||||||
}
|
}
|
||||||
outer:
|
outer:
|
||||||
|
|
Loading…
Reference in a new issue