diff --git a/glob.go b/glob.go index 35b2071..5995597 100644 --- a/glob.go +++ b/glob.go @@ -2,8 +2,8 @@ package glob import ( "strings" - "errors" "github.com/gobwas/glob/match" + "fmt" ) const ( @@ -96,7 +96,7 @@ func parse(str string, sep string, st state) ([]token, error) { case range_open: closed := indexByteNonEscaped(str, range_close, escape, 0) if closed == -1 { - return nil, errors.New("invalid format") + return nil, fmt.Errorf("'%s' should be closed with '%s'", string(range_open), string(range_close)) } r := str[i+1:closed] @@ -135,6 +135,7 @@ func parseRange(def string) (match.Matcher, error) { not bool esc bool minus bool + minusIndex int b []byte ) @@ -152,26 +153,31 @@ func parseRange(def string) (match.Matcher, error) { } case escape: if i == len(def) - 1 { - return nil, errors.New("escape character without follower") + return nil, fmt.Errorf("there should be any character after '%s'", string(escape)) } esc = true case inside_range_minus: minus = true + minusIndex = len(b) default: b = append(b, c) } } + if len(b) == 0 { + return nil, fmt.Errorf("range could not be empty") + } + def = string(b) if minus { r := []rune(def) - if len(r) != 3 || r[1] != inside_range_minus { - return nil, errors.New("invalid range syntax") + if len(r) != 2 || minusIndex != 1 { + return nil, fmt.Errorf("invalid range syntax: '%s' should be between two characters", string(inside_range_minus)) } - return &match.Between{r[0], r[2], not}, nil + return &match.Between{r[0], r[1], not}, nil } return &match.RangeList{def, not}, nil @@ -185,4 +191,4 @@ type token struct { type state struct { escape bool tokens []token -} +} \ No newline at end of file diff --git a/glob_test.go b/glob_test.go index f385a26..8e36ae1 100644 --- a/glob_test.go +++ b/glob_test.go @@ -1,11 +1,26 @@ package glob import ( - rGlob "github.com/ryanuber/go-glob" - "regexp" "testing" ) +const ( + pattern_all = "[a-z][!a-x]*cat*[h][!b]*eyes*" + fixture_all = "my cat has very bright eyes" + + pattern_plain = "google.com" + fixture_plain = "google.com" + + pattern_multiple = "https://*.google.*" + fixture_multiple = "https://account.google.com" + + pattern_prefix = "abc*" + pattern_suffix = "*def" + pattern_prefix_suffix = "ab*ef" + fixture_prefix_suffix = "abcdef" +) + + type test struct { pattern, match string should bool @@ -16,7 +31,7 @@ func glob(s bool, p, m string, d ...string) test { return test{p, m, s, d} } -func TestIndexOfNonEscaped(t *testing.T) { +func TestIndexByteNonEscaped(t *testing.T) { for _, test := range []struct { s string n, e byte @@ -46,6 +61,12 @@ func TestIndexOfNonEscaped(t *testing.T) { '\\', -1, }, + { + "\\b", + 'b', + '\\', + -1, + }, } { i := indexByteNonEscaped(test.s, test.n, test.e, 0) if i != test.i { @@ -88,10 +109,18 @@ func TestGlob(t *testing.T) { glob(false, "*is", "this is a test"), glob(false, "*no*", "this is a test"), + glob(true, "[!a]*", "this is a test"), + + glob(true, pattern_all, fixture_all), + glob(true, pattern_plain, fixture_plain), + glob(true, pattern_multiple, fixture_multiple), + glob(true, pattern_prefix, fixture_prefix_suffix), + glob(true, pattern_suffix, fixture_prefix_suffix), + glob(true, pattern_prefix_suffix, fixture_prefix_suffix), } { g, err := New(test.pattern, test.delimiters...) if err != nil { - t.Error(err) + t.Errorf("parsing pattern %q error: %s", test.pattern, err) continue } @@ -102,94 +131,53 @@ func TestGlob(t *testing.T) { } } -const Pattern = "*cat*eyes*" -const ExpPattern = ".*cat.*eyes.*" -const String = "my cat has very bright eyes" - -const ProfPattern = "* ?at * eyes" -const ProfString = "my cat has very bright eyes" - -//const Pattern = "*.google.com" -//const ExpPattern = ".*google\\.com" -//const String = "mail.google.com" -const PlainPattern = "google.com" -const PlainExpPattern = "google\\.com" -const PlainString = "google.com" - -const PSPattern = "https://*.google.com" -const PSExpPattern = `https:\/\/[a-z]+\.google\\.com` -const PSString = "https://account.google.com" - -func BenchmarkProf(b *testing.B) { - m, _ := New(Pattern) +func BenchmarkParse(b *testing.B) { for i := 0; i < b.N; i++ { - _ = m.Match(String) + New(pattern_all) } } -func BenchmarkGobwas(b *testing.B) { - m, _ := New(Pattern) +func BenchmarkAll(b *testing.B) { + m, _ := New(pattern_all) for i := 0; i < b.N; i++ { - _ = m.Match(String) - } -} -func BenchmarkGobwasPlain(b *testing.B) { - m, _ := New(PlainPattern) - - for i := 0; i < b.N; i++ { - _ = m.Match(PlainString) - } -} -func BenchmarkGobwasPrefix(b *testing.B) { - m, _ := New("abc*") - - for i := 0; i < b.N; i++ { - _ = m.Match("abcdef") - } -} -func BenchmarkGobwasSuffix(b *testing.B) { - m, _ := New("*def") - - for i := 0; i < b.N; i++ { - _ = m.Match("abcdef") - } -} -func BenchmarkGobwasPrefixSuffix(b *testing.B) { - m, _ := New("ab*ef") - - for i := 0; i < b.N; i++ { - _ = m.Match("abcdef") + _ = m.Match(fixture_all) } } -func BenchmarkRyanuber(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = rGlob.Glob(Pattern, String) - } -} -func BenchmarkRyanuberPlain(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = rGlob.Glob(PlainPattern, PlainString) - } -} -func BenchmarkRyanuberPrefixSuffix(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = rGlob.Glob(PSPattern, PSString) - } -} +func BenchmarkMultiple(b *testing.B) { + m, _ := New(pattern_multiple) - -func BenchmarkRegExp(b *testing.B) { - r := regexp.MustCompile(ExpPattern) for i := 0; i < b.N; i++ { - _ = r.Match([]byte(String)) + _ = m.Match(fixture_multiple) } } -func BenchmarkRegExpPrefixSuffix(b *testing.B) { - r := regexp.MustCompile(PSExpPattern) +func BenchmarkPlain(b *testing.B) { + m, _ := New(pattern_plain) + for i := 0; i < b.N; i++ { - _ = r.Match([]byte(PSString)) + _ = m.Match(fixture_plain) + } +} +func BenchmarkPrefix(b *testing.B) { + m, _ := New(pattern_prefix) + + for i := 0; i < b.N; i++ { + _ = m.Match(fixture_prefix_suffix) + } +} +func BenchmarkSuffix(b *testing.B) { + m, _ := New(pattern_suffix) + + for i := 0; i < b.N; i++ { + _ = m.Match(fixture_prefix_suffix) + } +} +func BenchmarkPrefixSuffix(b *testing.B) { + m, _ := New(pattern_prefix_suffix) + + for i := 0; i < b.N; i++ { + _ = m.Match(fixture_prefix_suffix) } } \ No newline at end of file diff --git a/util.go b/util.go index 87b0119..0be4dcf 100644 --- a/util.go +++ b/util.go @@ -7,8 +7,11 @@ import ( func indexByteNonEscaped(source string, needle, escape byte, shift int) int { i := strings.IndexByte(source, needle) - if i <= 0 { - return i + shift + if i == -1 { + return -1 + } + if i == 0 { + return shift } if source[i-1] != escape {