lib/ignore: Replace lib/fnmatch with github.com/gobwas/glob

Because it's literally ten times faster:



	benchmark                  old ns/op     new ns/op     delta

	BenchmarkMatch-8           13842         1200          -91.33%

	BenchmarkMatchCached-8     139           147           +5.76%



	benchmark                  old allocs     new allocs     delta

	BenchmarkMatch-8           0              0              +0.00%

	BenchmarkMatchCached-8     0              0              +0.00%



	benchmark                  old bytes     new bytes     delta

	BenchmarkMatch-8           12            0             -100.00%

	BenchmarkMatchCached-8     0             0             +0.00%
This commit is contained in:
Jakob Borg 2016-04-02 21:03:24 +02:00 committed by Audrius Butkevicius
parent 46e913dc23
commit 4c3cd4c9e3
61 changed files with 6121 additions and 203 deletions

View File

@ -934,7 +934,7 @@ func (s *apiService) getDBIgnores(w http.ResponseWriter, r *http.Request) {
sendJSON(w, map[string][]string{ sendJSON(w, map[string][]string{
"ignore": ignores, "ignore": ignores,
"patterns": patterns, "expanded": patterns,
}) })
} }

View File

@ -1,79 +0,0 @@
// Copyright (C) 2014 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
package fnmatch
import (
"path/filepath"
"regexp"
"runtime"
"strings"
)
const (
NoEscape = (1 << iota)
PathName
CaseFold
)
func Convert(pattern string, flags int) (*regexp.Regexp, error) {
any := "."
switch runtime.GOOS {
case "windows":
flags |= NoEscape | CaseFold
pattern = filepath.FromSlash(pattern)
if flags&PathName != 0 {
any = `[^\\]`
}
case "darwin":
flags |= CaseFold
fallthrough
default:
if flags&PathName != 0 {
any = `[^/]`
}
}
if flags&NoEscape != 0 {
pattern = strings.Replace(pattern, `\`, `\\`, -1)
} else {
pattern = strings.Replace(pattern, `\*`, "[:escapedstar:]", -1)
pattern = strings.Replace(pattern, `\?`, "[:escapedques:]", -1)
pattern = strings.Replace(pattern, `\.`, "[:escapeddot:]", -1)
}
// Characters that are special in regexps but not in glob, must be
// escaped.
for _, char := range []string{`.`, `+`, `$`, `^`, `(`, `)`, `|`} {
pattern = strings.Replace(pattern, char, `\`+char, -1)
}
pattern = strings.Replace(pattern, `**`, `[:doublestar:]`, -1)
pattern = strings.Replace(pattern, `*`, any+`*`, -1)
pattern = strings.Replace(pattern, `[:doublestar:]`, `.*`, -1)
pattern = strings.Replace(pattern, `?`, any, -1)
pattern = strings.Replace(pattern, `[:escapedstar:]`, `\*`, -1)
pattern = strings.Replace(pattern, `[:escapedques:]`, `\?`, -1)
pattern = strings.Replace(pattern, `[:escapeddot:]`, `\.`, -1)
pattern = `^` + pattern + `$`
if flags&CaseFold != 0 {
pattern = `(?i)` + pattern
}
return regexp.Compile(pattern)
}
// Match matches the pattern against the string, with the given flags, and
// returns true if the match is successful.
func Match(pattern, s string, flags int) (bool, error) {
exp, err := Convert(pattern, flags)
if err != nil {
return false, err
}
return exp.MatchString(s), nil
}

View File

@ -1,98 +0,0 @@
// Copyright (C) 2014 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
package fnmatch
import (
"path/filepath"
"runtime"
"testing"
)
type testcase struct {
pat string
name string
flags int
match bool
}
var testcases = []testcase{
{"", "", 0, true},
{"*", "", 0, true},
{"*", "foo", 0, true},
{"*", "bar", 0, true},
{"*", "*", 0, true},
{"**", "f", 0, true},
{"**", "foo.txt", 0, true},
{"*.*", "foo.txt", 0, true},
{"foo*.txt", "foobar.txt", 0, true},
{"foo.txt", "foo.txt", 0, true},
{"foo.txt", "bar/foo.txt", 0, false},
{"*/foo.txt", "bar/foo.txt", 0, true},
{"f?o.txt", "foo.txt", 0, true},
{"f?o.txt", "fooo.txt", 0, false},
{"f[ab]o.txt", "foo.txt", 0, false},
{"f[ab]o.txt", "fao.txt", 0, true},
{"f[ab]o.txt", "fbo.txt", 0, true},
{"f[ab]o.txt", "fco.txt", 0, false},
{"f[ab]o.txt", "fabo.txt", 0, false},
{"f[ab]o.txt", "f[ab]o.txt", 0, false},
{"f\\[ab\\]o.txt", "f[ab]o.txt", NoEscape, false},
{"*foo.txt", "bar/foo.txt", 0, true},
{"*foo.txt", "bar/foo.txt", PathName, false},
{"*/foo.txt", "bar/foo.txt", 0, true},
{"*/foo.txt", "bar/foo.txt", PathName, true},
{"*/foo.txt", "bar/baz/foo.txt", 0, true},
{"*/foo.txt", "bar/baz/foo.txt", PathName, false},
{"**/foo.txt", "bar/baz/foo.txt", 0, true},
{"**/foo.txt", "bar/baz/foo.txt", PathName, true},
{"foo.txt", "foo.TXT", CaseFold, true},
// These characters are literals in glob, but not in regexp.
{"hey$hello", "hey$hello", 0, true},
{"hey^hello", "hey^hello", 0, true},
{"hey{hello", "hey{hello", 0, true},
{"hey}hello", "hey}hello", 0, true},
{"hey(hello", "hey(hello", 0, true},
{"hey)hello", "hey)hello", 0, true},
{"hey|hello", "hey|hello", 0, true},
{"hey|hello", "hey|other", 0, false},
}
func TestMatch(t *testing.T) {
switch runtime.GOOS {
case "windows":
testcases = append(testcases, testcase{"foo.txt", "foo.TXT", 0, true})
case "darwin":
testcases = append(testcases, testcase{"foo.txt", "foo.TXT", 0, true})
fallthrough
default:
testcases = append(testcases, testcase{"f\\[ab\\]o.txt", "f[ab]o.txt", 0, true})
testcases = append(testcases, testcase{"foo\\.txt", "foo.txt", 0, true})
testcases = append(testcases, testcase{"foo\\*.txt", "foo*.txt", 0, true})
testcases = append(testcases, testcase{"foo\\.txt", "foo.txt", NoEscape, false})
testcases = append(testcases, testcase{"f\\\\\\[ab\\\\\\]o.txt", "f\\[ab\\]o.txt", 0, true})
}
for _, tc := range testcases {
if m, err := Match(tc.pat, filepath.FromSlash(tc.name), tc.flags); m != tc.match {
if err != nil {
t.Error(err)
} else {
t.Errorf("Match(%q, %q, %d) != %v", tc.pat, tc.name, tc.flags, tc.match)
}
}
}
}
func TestInvalid(t *testing.T) {
if _, err := Match("foo[bar", "...", 0); err == nil {
t.Error("Unexpected nil error")
}
}

View File

@ -14,24 +14,30 @@ import (
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"regexp" "runtime"
"strings" "strings"
"time" "time"
"github.com/syncthing/syncthing/lib/fnmatch" "github.com/gobwas/glob"
"github.com/syncthing/syncthing/lib/sync" "github.com/syncthing/syncthing/lib/sync"
) )
type Pattern struct { type Pattern struct {
match *regexp.Regexp pattern string
match glob.Glob
include bool include bool
foldCase bool
} }
func (p Pattern) String() string { func (p Pattern) String() string {
if p.include { ret := p.pattern
return p.match.String() if !p.include {
ret = "!" + ret
} }
return "(?exclude)" + p.match.String() if p.foldCase {
ret = "(?i)" + ret
}
return ret
} }
type Matcher struct { type Matcher struct {
@ -119,17 +125,28 @@ func (m *Matcher) Match(file string) (result bool) {
} }
// Check all the patterns for a match. // Check all the patterns for a match.
file = filepath.ToSlash(file)
var lowercaseFile string
for _, pattern := range m.patterns { for _, pattern := range m.patterns {
if pattern.match.MatchString(file) { if pattern.foldCase {
if lowercaseFile == "" {
lowercaseFile = strings.ToLower(file)
}
if pattern.match.Match(lowercaseFile) {
return pattern.include return pattern.include
} }
} else {
if pattern.match.Match(file) {
return pattern.include
}
}
} }
// Default to false. // Default to false.
return false return false
} }
// Patterns return a list of the loaded regexp patterns, as strings // Patterns return a list of the loaded patterns, as they've been parsed
func (m *Matcher) Patterns() []string { func (m *Matcher) Patterns() []string {
if m == nil { if m == nil {
return nil return nil
@ -200,38 +217,43 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
var patterns []Pattern var patterns []Pattern
addPattern := func(line string) error { addPattern := func(line string) error {
include := true pattern := Pattern{
pattern: line,
include: true,
foldCase: runtime.GOOS == "darwin" || runtime.GOOS == "windows",
}
if strings.HasPrefix(line, "!") { if strings.HasPrefix(line, "!") {
line = line[1:] line = line[1:]
include = false pattern.include = false
} }
flags := fnmatch.PathName
if strings.HasPrefix(line, "(?i)") { if strings.HasPrefix(line, "(?i)") {
line = line[4:] line = strings.ToLower(line[4:])
flags |= fnmatch.CaseFold pattern.foldCase = true
} }
var err error
if strings.HasPrefix(line, "/") { if strings.HasPrefix(line, "/") {
// Pattern is rooted in the current dir only // Pattern is rooted in the current dir only
exp, err := fnmatch.Convert(line[1:], flags) pattern.match, err = glob.Compile(line[1:])
if err != nil { if err != nil {
return fmt.Errorf("invalid pattern %q in ignore file", line) return fmt.Errorf("invalid pattern %q in ignore file", line)
} }
patterns = append(patterns, Pattern{exp, include}) patterns = append(patterns, pattern)
} else if strings.HasPrefix(line, "**/") { } else if strings.HasPrefix(line, "**/") {
// Add the pattern as is, and without **/ so it matches in current dir // Add the pattern as is, and without **/ so it matches in current dir
exp, err := fnmatch.Convert(line, flags) pattern.match, err = glob.Compile(line)
if err != nil { if err != nil {
return fmt.Errorf("invalid pattern %q in ignore file", line) return fmt.Errorf("invalid pattern %q in ignore file", line)
} }
patterns = append(patterns, Pattern{exp, include}) patterns = append(patterns, pattern)
exp, err = fnmatch.Convert(line[3:], flags) pattern.match, err = glob.Compile(line[3:])
if err != nil { if err != nil {
return fmt.Errorf("invalid pattern %q in ignore file", line) return fmt.Errorf("invalid pattern %q in ignore file", line)
} }
patterns = append(patterns, Pattern{exp, include}) patterns = append(patterns, pattern)
} else if strings.HasPrefix(line, "#include ") { } else if strings.HasPrefix(line, "#include ") {
includeRel := line[len("#include "):] includeRel := line[len("#include "):]
includeFile := filepath.Join(filepath.Dir(currentFile), includeRel) includeFile := filepath.Join(filepath.Dir(currentFile), includeRel)
@ -243,17 +265,17 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
} else { } else {
// Path name or pattern, add it so it matches files both in // Path name or pattern, add it so it matches files both in
// current directory and subdirs. // current directory and subdirs.
exp, err := fnmatch.Convert(line, flags) pattern.match, err = glob.Compile(line)
if err != nil { if err != nil {
return fmt.Errorf("invalid pattern %q in ignore file", line) return fmt.Errorf("invalid pattern %q in ignore file", line)
} }
patterns = append(patterns, Pattern{exp, include}) patterns = append(patterns, pattern)
exp, err = fnmatch.Convert("**/"+line, flags) pattern.match, err = glob.Compile("**/" + line)
if err != nil { if err != nil {
return fmt.Errorf("invalid pattern %q in ignore file", line) return fmt.Errorf("invalid pattern %q in ignore file", line)
} }
patterns = append(patterns, Pattern{exp, include}) patterns = append(patterns, pattern)
} }
return nil return nil
} }
@ -267,6 +289,10 @@ func parseIgnoreFile(fd io.Reader, currentFile string, seen map[string]bool) ([]
continue continue
case strings.HasPrefix(line, "//"): case strings.HasPrefix(line, "//"):
continue continue
}
line = filepath.ToSlash(line)
switch {
case strings.HasPrefix(line, "#"): case strings.HasPrefix(line, "#"):
err = addPattern(line) err = addPattern(line)
case strings.HasSuffix(line, "/**"): case strings.HasSuffix(line, "/**"):

View File

@ -509,3 +509,29 @@ func TestHashOfEmpty(t *testing.T) {
t.Error("there are more than zero patterns") t.Error("there are more than zero patterns")
} }
} }
func TestWindowsPatterns(t *testing.T) {
// We should accept patterns as both a/b and a\b and match that against
// both kinds of slash as well.
if runtime.GOOS != "windows" {
t.Skip("Windows specific test")
return
}
stignore := `
a/b
c\d
`
pats := New(true)
err := pats.Parse(bytes.NewBufferString(stignore), ".stignore")
if err != nil {
t.Fatal(err)
}
tests := []string{`a\b`, `c\d`}
for _, pat := range tests {
if !pats.Match(pat) {
t.Errorf("Should match %s", pat)
}
}
}

21
vendor/github.com/gobwas/glob/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Sergey Kamardin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

26
vendor/github.com/gobwas/glob/bench.sh generated vendored Normal file
View File

@ -0,0 +1,26 @@
#! /bin/bash
bench() {
filename="/tmp/$1-$2.bench"
if test -e "${filename}";
then
echo "Already exists ${filename}"
else
backup=`git rev-parse --abbrev-ref HEAD`
git checkout $1
echo -n "Creating ${filename}... "
go test ./... -run=NONE -bench=$2 > "${filename}" -benchmem
echo "OK"
git checkout ${backup}
sleep 5
fi
}
to=$1
current=`git rev-parse --abbrev-ref HEAD`
bench ${to} $2
bench ${current} $2
benchcmp $3 "/tmp/${to}-$2.bench" "/tmp/${current}-$2.bench"

44
vendor/github.com/gobwas/glob/cmd/globdraw/main.go generated vendored Normal file
View File

@ -0,0 +1,44 @@
package main
import (
"flag"
"fmt"
"github.com/gobwas/glob"
"github.com/gobwas/glob/match"
"github.com/gobwas/glob/match/debug"
"os"
"strings"
"unicode/utf8"
)
func main() {
pattern := flag.String("p", "", "pattern to draw")
sep := flag.String("s", "", "comma separated list of separators characters")
flag.Parse()
if *pattern == "" {
flag.Usage()
os.Exit(1)
}
var separators []rune
if len(*sep) > 0 {
for _, c := range strings.Split(*sep, ",") {
if r, w := utf8.DecodeRuneInString(c); len(c) > w {
fmt.Println("only single charactered separators are allowed")
os.Exit(1)
} else {
separators = append(separators, r)
}
}
}
glob, err := glob.Compile(*pattern, separators...)
if err != nil {
fmt.Println("could not compile pattern:", err)
os.Exit(1)
}
matcher := glob.(match.Matcher)
fmt.Fprint(os.Stdout, debug.Graphviz(*pattern, matcher))
}

82
vendor/github.com/gobwas/glob/cmd/globtest/main.go generated vendored Normal file
View File

@ -0,0 +1,82 @@
package main
import (
"flag"
"fmt"
"github.com/gobwas/glob"
"os"
"strings"
"testing"
"unicode/utf8"
)
func benchString(r testing.BenchmarkResult) string {
nsop := r.NsPerOp()
ns := fmt.Sprintf("%10d ns/op", nsop)
allocs := "0"
if r.N > 0 {
if nsop < 100 {
// The format specifiers here make sure that
// the ones digits line up for all three possible formats.
if nsop < 10 {
ns = fmt.Sprintf("%13.2f ns/op", float64(r.T.Nanoseconds())/float64(r.N))
} else {
ns = fmt.Sprintf("%12.1f ns/op", float64(r.T.Nanoseconds())/float64(r.N))
}
}
allocs = fmt.Sprintf("%d", r.MemAllocs/uint64(r.N))
}
return fmt.Sprintf("%8d\t%s\t%s allocs", r.N, ns, allocs)
}
func main() {
pattern := flag.String("p", "", "pattern to draw")
sep := flag.String("s", "", "comma separated list of separators")
fixture := flag.String("f", "", "fixture")
verbose := flag.Bool("v", false, "verbose")
flag.Parse()
if *pattern == "" {
flag.Usage()
os.Exit(1)
}
var separators []rune
for _, c := range strings.Split(*sep, ",") {
if r, w := utf8.DecodeRuneInString(c); len(c) > w {
fmt.Println("only single charactered separators are allowed")
os.Exit(1)
} else {
separators = append(separators, r)
}
}
g, err := glob.Compile(*pattern, separators...)
if err != nil {
fmt.Println("could not compile pattern:", err)
os.Exit(1)
}
if !*verbose {
fmt.Println(g.Match(*fixture))
return
}
fmt.Printf("result: %t\n", g.Match(*fixture))
cb := testing.Benchmark(func(b *testing.B) {
for i := 0; i < b.N; i++ {
glob.Compile(*pattern, separators...)
}
})
fmt.Println("compile:", benchString(cb))
mb := testing.Benchmark(func(b *testing.B) {
for i := 0; i < b.N; i++ {
g.Match(*fixture)
}
})
fmt.Println("match: ", benchString(mb))
}

682
vendor/github.com/gobwas/glob/compiler.go generated vendored Normal file
View File

@ -0,0 +1,682 @@
package glob
// TODO use constructor with all matchers, and to their structs private
// TODO glue multiple Text nodes (like after QuoteMeta)
import (
"fmt"
"github.com/gobwas/glob/match"
"github.com/gobwas/glob/runes"
"reflect"
)
func optimize(matcher match.Matcher) match.Matcher {
switch m := matcher.(type) {
case match.Any:
if len(m.Separators) == 0 {
return match.NewSuper()
}
case match.AnyOf:
if len(m.Matchers) == 1 {
return m.Matchers[0]
}
return m
case match.List:
if m.Not == false && len(m.List) == 1 {
return match.NewText(string(m.List))
}
return m
case match.BTree:
m.Left = optimize(m.Left)
m.Right = optimize(m.Right)
r, ok := m.Value.(match.Text)
if !ok {
return m
}
leftNil := m.Left == nil
rightNil := m.Right == nil
if leftNil && rightNil {
return match.NewText(r.Str)
}
_, leftSuper := m.Left.(match.Super)
lp, leftPrefix := m.Left.(match.Prefix)
_, rightSuper := m.Right.(match.Super)
rs, rightSuffix := m.Right.(match.Suffix)
if leftSuper && rightSuper {
return match.NewContains(r.Str, false)
}
if leftSuper && rightNil {
return match.NewSuffix(r.Str)
}
if rightSuper && leftNil {
return match.NewPrefix(r.Str)
}
if leftNil && rightSuffix {
return match.NewPrefixSuffix(r.Str, rs.Suffix)
}
if rightNil && leftPrefix {
return match.NewPrefixSuffix(lp.Prefix, r.Str)
}
return m
}
return matcher
}
func glueMatchers(matchers []match.Matcher) match.Matcher {
var (
glued []match.Matcher
winner match.Matcher
)
maxLen := -1
if m := glueAsEvery(matchers); m != nil {
glued = append(glued, m)
return m
}
if m := glueAsRow(matchers); m != nil {
glued = append(glued, m)
return m
}
for _, g := range glued {
if l := g.Len(); l > maxLen {
maxLen = l
winner = g
}
}
return winner
}
func glueAsRow(matchers []match.Matcher) match.Matcher {
if len(matchers) <= 1 {
return nil
}
var (
c []match.Matcher
l int
)
for _, matcher := range matchers {
if ml := matcher.Len(); ml == -1 {
return nil
} else {
c = append(c, matcher)
l += ml
}
}
return match.NewRow(l, c...)
}
func glueAsEvery(matchers []match.Matcher) match.Matcher {
if len(matchers) <= 1 {
return nil
}
var (
hasAny bool
hasSuper bool
hasSingle bool
min int
separator []rune
)
for i, matcher := range matchers {
var sep []rune
switch m := matcher.(type) {
case match.Super:
sep = []rune{}
hasSuper = true
case match.Any:
sep = m.Separators
hasAny = true
case match.Single:
sep = m.Separators
hasSingle = true
min++
case match.List:
if !m.Not {
return nil
}
sep = m.List
hasSingle = true
min++
default:
return nil
}
// initialize
if i == 0 {
separator = sep
}
if runes.Equal(sep, separator) {
continue
}
return nil
}
if hasSuper && !hasAny && !hasSingle {
return match.NewSuper()
}
if hasAny && !hasSuper && !hasSingle {
return match.NewAny(separator)
}
if (hasAny || hasSuper) && min > 0 && len(separator) == 0 {
return match.NewMin(min)
}
every := match.NewEveryOf()
if min > 0 {
every.Add(match.NewMin(min))
if !hasAny && !hasSuper {
every.Add(match.NewMax(min))
}
}
if len(separator) > 0 {
every.Add(match.NewContains(string(separator), true))
}
return every
}
func minimizeMatchers(matchers []match.Matcher) []match.Matcher {
var done match.Matcher
var left, right, count int
for l := 0; l < len(matchers); l++ {
for r := len(matchers); r > l; r-- {
if glued := glueMatchers(matchers[l:r]); glued != nil {
var swap bool
if done == nil {
swap = true
} else {
cl, gl := done.Len(), glued.Len()
swap = cl > -1 && gl > -1 && gl > cl
swap = swap || count < r-l
}
if swap {
done = glued
left = l
right = r
count = r - l
}
}
}
}
if done == nil {
return matchers
}
next := append(append([]match.Matcher{}, matchers[:left]...), done)
if right < len(matchers) {
next = append(next, matchers[right:]...)
}
if len(next) == len(matchers) {
return next
}
return minimizeMatchers(next)
}
func minimizeAnyOf(children []node) node {
var nodes [][]node
var min int
var idx int
for i, desc := range children {
pat, ok := desc.(*nodePattern)
if !ok {
return nil
}
n := pat.children()
ln := len(n)
if len(nodes) == 0 || (ln < min) {
min = ln
idx = i
}
nodes = append(nodes, pat.children())
}
minNodes := nodes[idx]
if idx+1 < len(nodes) {
nodes = append(nodes[:idx], nodes[idx+1:]...)
} else {
nodes = nodes[:idx]
}
var commonLeft []node
var commonLeftCount int
for i, n := range minNodes {
has := true
for _, t := range nodes {
if !reflect.DeepEqual(n, t[i]) {
has = false
break
}
}
if has {
commonLeft = append(commonLeft, n)
commonLeftCount++
} else {
break
}
}
var commonRight []node
var commonRightCount int
for i := min - 1; i > commonLeftCount-1; i-- {
n := minNodes[i]
has := true
for _, t := range nodes {
if !reflect.DeepEqual(n, t[len(t)-(min-i)]) {
has = false
break
}
}
if has {
commonRight = append(commonRight, n)
commonRightCount++
} else {
break
}
}
if commonLeftCount == 0 && commonRightCount == 0 {
return nil
}
nodes = append(nodes, minNodes)
nodes[len(nodes)-1], nodes[idx] = nodes[idx], nodes[len(nodes)-1]
var result []node
if commonLeftCount > 0 {
result = append(result, &nodePattern{nodeImpl: nodeImpl{desc: commonLeft}})
}
var anyOf []node
for _, n := range nodes {
if commonLeftCount+commonRightCount == len(n) {
anyOf = append(anyOf, nil)
} else {
anyOf = append(anyOf, &nodePattern{nodeImpl: nodeImpl{desc: n[commonLeftCount : len(n)-commonRightCount]}})
}
}
anyOf = uniqueNodes(anyOf)
if len(anyOf) == 1 {
if anyOf[0] != nil {
result = append(result, &nodePattern{nodeImpl: nodeImpl{desc: anyOf}})
}
} else {
result = append(result, &nodeAnyOf{nodeImpl: nodeImpl{desc: anyOf}})
}
if commonRightCount > 0 {
result = append(result, &nodePattern{nodeImpl: nodeImpl{desc: commonRight}})
}
return &nodePattern{nodeImpl: nodeImpl{desc: result}}
}
func uniqueNodes(nodes []node) (result []node) {
head:
for _, n := range nodes {
for _, e := range result {
if reflect.DeepEqual(e, n) {
continue head
}
}
result = append(result, n)
}
return
}
func compileMatchers(matchers []match.Matcher) (match.Matcher, error) {
if len(matchers) == 0 {
return nil, fmt.Errorf("compile error: need at least one matcher")
}
if len(matchers) == 1 {
return matchers[0], nil
}
if m := glueMatchers(matchers); m != nil {
return m, nil
}
var (
val match.Matcher
idx int
)
maxLen := -1
for i, matcher := range matchers {
l := matcher.Len()
if l >= maxLen {
maxLen = l
idx = i
val = matcher
}
}
left := matchers[:idx]
var right []match.Matcher
if len(matchers) > idx+1 {
right = matchers[idx+1:]
}
var l, r match.Matcher
var err error
if len(left) > 0 {
l, err = compileMatchers(left)
if err != nil {
return nil, err
}
}
if len(right) > 0 {
r, err = compileMatchers(right)
if err != nil {
return nil, err
}
}
return match.NewBTree(val, l, r), nil
}
//func complexity(m match.Matcher) int {
// var matchers []match.Matcher
// var k int
//
// switch matcher := m.(type) {
//
// case match.Nothing:
// return 0
//
// case match.Max, match.Range, match.Suffix, match.Text:
// return 1
//
// case match.PrefixSuffix, match.Single, match.Row:
// return 2
//
// case match.Any, match.Contains, match.List, match.Min, match.Prefix, match.Super:
// return 4
//
// case match.BTree:
// matchers = append(matchers, matcher.Value)
// if matcher.Left != nil {
// matchers = append(matchers, matcher.Left)
// }
// if matcher.Right != nil {
// matchers = append(matchers, matcher.Right)
// }
// k = 1
//
// case match.AnyOf:
// matchers = matcher.Matchers
// k = 1
// case match.EveryOf:
// matchers = matcher.Matchers
// k = 1
//
// default:
// return 0
// }
//
// var sum int
// for _, m := range matchers {
// sum += complexity(m)
// }
//
// return sum * k
//}
func doAnyOf(n *nodeAnyOf, s []rune) (match.Matcher, error) {
var matchers []match.Matcher
for _, desc := range n.children() {
if desc == nil {
matchers = append(matchers, match.NewNothing())
continue
}
m, err := do(desc, s)
if err != nil {
return nil, err
}
matchers = append(matchers, optimize(m))
}
return match.NewAnyOf(matchers...), nil
}
func do(leaf node, s []rune) (m match.Matcher, err error) {
switch n := leaf.(type) {
case *nodeAnyOf:
// todo this could be faster on pattern_alternatives_combine_lite
if n := minimizeAnyOf(n.children()); n != nil {
return do(n, s)
}
var matchers []match.Matcher
for _, desc := range n.children() {
if desc == nil {
matchers = append(matchers, match.NewNothing())
continue
}
m, err := do(desc, s)
if err != nil {
return nil, err
}
matchers = append(matchers, optimize(m))
}
return match.NewAnyOf(matchers...), nil
case *nodePattern:
nodes := leaf.children()
if len(nodes) == 0 {
return match.NewNothing(), nil
}
var matchers []match.Matcher
for _, desc := range nodes {
m, err := do(desc, s)
if err != nil {
return nil, err
}
matchers = append(matchers, optimize(m))
}
m, err = compileMatchers(minimizeMatchers(matchers))
if err != nil {
return nil, err
}
case *nodeList:
m = match.NewList([]rune(n.chars), n.not)
case *nodeRange:
m = match.NewRange(n.lo, n.hi, n.not)
case *nodeAny:
m = match.NewAny(s)
case *nodeSuper:
m = match.NewSuper()
case *nodeSingle:
m = match.NewSingle(s)
case *nodeText:
m = match.NewText(n.text)
default:
return nil, fmt.Errorf("could not compile tree: unknown node type")
}
return optimize(m), nil
}
func do2(node node, s []rune) ([]match.Matcher, error) {
var result []match.Matcher
switch n := node.(type) {
case *nodePattern:
ways := [][]match.Matcher{[]match.Matcher{}}
for _, desc := range node.children() {
variants, err := do2(desc, s)
if err != nil {
return nil, err
}
fmt.Println("variants pat", variants)
for i, l := 0, len(ways); i < l; i++ {
for i := 0; i < len(variants); i++ {
o := optimize(variants[i])
if i == len(variants)-1 {
ways[i] = append(ways[i], o)
} else {
var w []match.Matcher
copy(w, ways[i])
ways = append(ways, append(w, o))
}
}
}
fmt.Println("ways pat", ways)
}
for _, matchers := range ways {
c, err := compileMatchers(minimizeMatchers(matchers))
if err != nil {
return nil, err
}
result = append(result, c)
}
case *nodeAnyOf:
ways := make([][]match.Matcher, len(node.children()))
for _, desc := range node.children() {
variants, err := do2(desc, s)
if err != nil {
return nil, err
}
fmt.Println("variants any", variants)
for x, l := 0, len(ways); x < l; x++ {
for i := 0; i < len(variants); i++ {
o := optimize(variants[i])
if i == len(variants)-1 {
ways[x] = append(ways[x], o)
} else {
var w []match.Matcher
copy(w, ways[x])
ways = append(ways, append(w, o))
}
}
}
fmt.Println("ways any", ways)
}
for _, matchers := range ways {
c, err := compileMatchers(minimizeMatchers(matchers))
if err != nil {
return nil, err
}
result = append(result, c)
}
case *nodeList:
result = append(result, match.NewList([]rune(n.chars), n.not))
case *nodeRange:
result = append(result, match.NewRange(n.lo, n.hi, n.not))
case *nodeAny:
result = append(result, match.NewAny(s))
case *nodeSuper:
result = append(result, match.NewSuper())
case *nodeSingle:
result = append(result, match.NewSingle(s))
case *nodeText:
result = append(result, match.NewText(n.text))
default:
return nil, fmt.Errorf("could not compile tree: unknown node type")
}
for i, m := range result {
result[i] = optimize(m)
}
return result, nil
}
func compile(ast *nodePattern, s []rune) (Glob, error) {
// ms, err := do2(ast, s)
// if err != nil {
// return nil, err
// }
// if len(ms) == 1 {
// return ms[0], nil
// } else {
// return match.NewAnyOf(ms), nil
// }
g, err := do(ast, s)
if err != nil {
return nil, err
}
return g, nil
}

546
vendor/github.com/gobwas/glob/compiler_test.go generated vendored Normal file
View File

@ -0,0 +1,546 @@
package glob
import (
"github.com/gobwas/glob/match"
"reflect"
"testing"
)
var separators = []rune{'.'}
func TestGlueMatchers(t *testing.T) {
for id, test := range []struct {
in []match.Matcher
exp match.Matcher
}{
{
[]match.Matcher{
match.NewSuper(),
match.NewSingle(nil),
},
match.NewMin(1),
},
{
[]match.Matcher{
match.NewAny(separators),
match.NewSingle(separators),
},
match.EveryOf{match.Matchers{
match.NewMin(1),
match.NewContains(string(separators), true),
}},
},
{
[]match.Matcher{
match.NewSingle(nil),
match.NewSingle(nil),
match.NewSingle(nil),
},
match.EveryOf{match.Matchers{
match.NewMin(3),
match.NewMax(3),
}},
},
{
[]match.Matcher{
match.NewList([]rune{'a'}, true),
match.NewAny([]rune{'a'}),
},
match.EveryOf{match.Matchers{
match.NewMin(1),
match.NewContains("a", true),
}},
},
} {
act, err := compileMatchers(test.in)
if err != nil {
t.Errorf("#%d convert matchers error: %s", id, err)
continue
}
if !reflect.DeepEqual(act, test.exp) {
t.Errorf("#%d unexpected convert matchers result:\nact: %#v;\nexp: %#v", id, act, test.exp)
continue
}
}
}
func TestCompileMatchers(t *testing.T) {
for id, test := range []struct {
in []match.Matcher
exp match.Matcher
}{
{
[]match.Matcher{
match.NewSuper(),
match.NewSingle(separators),
match.NewText("c"),
},
match.NewBTree(
match.NewText("c"),
match.NewBTree(
match.NewSingle(separators),
match.NewSuper(),
nil,
),
nil,
),
},
{
[]match.Matcher{
match.NewAny(nil),
match.NewText("c"),
match.NewAny(nil),
},
match.NewBTree(
match.NewText("c"),
match.NewAny(nil),
match.NewAny(nil),
),
},
{
[]match.Matcher{
match.NewRange('a', 'c', true),
match.NewList([]rune{'z', 't', 'e'}, false),
match.NewText("c"),
match.NewSingle(nil),
},
match.NewRow(
4,
match.Matchers{
match.NewRange('a', 'c', true),
match.NewList([]rune{'z', 't', 'e'}, false),
match.NewText("c"),
match.NewSingle(nil),
}...,
),
},
} {
act, err := compileMatchers(test.in)
if err != nil {
t.Errorf("#%d convert matchers error: %s", id, err)
continue
}
if !reflect.DeepEqual(act, test.exp) {
t.Errorf("#%d unexpected convert matchers result:\nact: %#v\nexp: %#v", id, act, test.exp)
continue
}
}
}
func TestConvertMatchers(t *testing.T) {
for id, test := range []struct {
in, exp []match.Matcher
}{
{
[]match.Matcher{
match.NewRange('a', 'c', true),
match.NewList([]rune{'z', 't', 'e'}, false),
match.NewText("c"),
match.NewSingle(nil),
match.NewAny(nil),
},
[]match.Matcher{
match.NewRow(
4,
[]match.Matcher{
match.NewRange('a', 'c', true),
match.NewList([]rune{'z', 't', 'e'}, false),
match.NewText("c"),
match.NewSingle(nil),
}...,
),
match.NewAny(nil),
},
},
{
[]match.Matcher{
match.NewRange('a', 'c', true),
match.NewList([]rune{'z', 't', 'e'}, false),
match.NewText("c"),
match.NewSingle(nil),
match.NewAny(nil),
match.NewSingle(nil),
match.NewSingle(nil),
match.NewAny(nil),
},
[]match.Matcher{
match.NewRow(
3,
match.Matchers{
match.NewRange('a', 'c', true),
match.NewList([]rune{'z', 't', 'e'}, false),
match.NewText("c"),
}...,
),
match.NewMin(3),
},
},
} {
act := minimizeMatchers(test.in)
if !reflect.DeepEqual(act, test.exp) {
t.Errorf("#%d unexpected convert matchers 2 result:\nact: %#v\nexp: %#v", id, act, test.exp)
continue
}
}
}
func pattern(nodes ...node) *nodePattern {
return &nodePattern{
nodeImpl: nodeImpl{
desc: nodes,
},
}
}
func anyOf(nodes ...node) *nodeAnyOf {
return &nodeAnyOf{
nodeImpl: nodeImpl{
desc: nodes,
},
}
}
func TestCompiler(t *testing.T) {
for id, test := range []struct {
ast *nodePattern
result Glob
sep []rune
}{
{
ast: pattern(&nodeText{text: "abc"}),
result: match.NewText("abc"),
},
{
ast: pattern(&nodeAny{}),
sep: separators,
result: match.NewAny(separators),
},
{
ast: pattern(&nodeAny{}),
result: match.NewSuper(),
},
{
ast: pattern(&nodeSuper{}),
result: match.NewSuper(),
},
{
ast: pattern(&nodeSingle{}),
sep: separators,
result: match.NewSingle(separators),
},
{
ast: pattern(&nodeRange{
lo: 'a',
hi: 'z',
not: true,
}),
result: match.NewRange('a', 'z', true),
},
{
ast: pattern(&nodeList{
chars: "abc",
not: true,
}),
result: match.NewList([]rune{'a', 'b', 'c'}, true),
},
{
ast: pattern(&nodeAny{}, &nodeSingle{}, &nodeSingle{}, &nodeSingle{}),
sep: separators,
result: match.EveryOf{Matchers: match.Matchers{
match.NewMin(3),
match.NewContains(string(separators), true),
}},
},
{
ast: pattern(&nodeAny{}, &nodeSingle{}, &nodeSingle{}, &nodeSingle{}),
result: match.NewMin(3),
},
{
ast: pattern(&nodeAny{}, &nodeText{text: "abc"}, &nodeSingle{}),
sep: separators,
result: match.NewBTree(
match.NewRow(
4,
match.Matchers{
match.NewText("abc"),
match.NewSingle(separators),
}...,
),
match.NewAny(separators),
nil,
),
},
{
ast: pattern(&nodeSuper{}, &nodeSingle{}, &nodeText{text: "abc"}, &nodeSingle{}),
sep: separators,
result: match.NewBTree(
match.NewRow(
5,
match.Matchers{
match.NewSingle(separators),
match.NewText("abc"),
match.NewSingle(separators),
}...,
),
match.NewSuper(),
nil,
),
},
{
ast: pattern(&nodeAny{}, &nodeText{text: "abc"}),
result: match.NewSuffix("abc"),
},
{
ast: pattern(&nodeText{text: "abc"}, &nodeAny{}),
result: match.NewPrefix("abc"),
},
{
ast: pattern(&nodeText{text: "abc"}, &nodeAny{}, &nodeText{text: "def"}),
result: match.NewPrefixSuffix("abc", "def"),
},
{
ast: pattern(&nodeAny{}, &nodeAny{}, &nodeAny{}, &nodeText{text: "abc"}, &nodeAny{}, &nodeAny{}),
result: match.NewContains("abc", false),
},
{
ast: pattern(&nodeAny{}, &nodeAny{}, &nodeAny{}, &nodeText{text: "abc"}, &nodeAny{}, &nodeAny{}),
sep: separators,
result: match.NewBTree(
match.NewText("abc"),
match.NewAny(separators),
match.NewAny(separators),
),
},
{
ast: pattern(&nodeSuper{}, &nodeSingle{}, &nodeText{text: "abc"}, &nodeSuper{}, &nodeSingle{}),
result: match.NewBTree(
match.NewText("abc"),
match.NewMin(1),
match.NewMin(1),
),
},
{
ast: pattern(anyOf(&nodeText{text: "abc"})),
result: match.NewText("abc"),
},
{
ast: pattern(anyOf(pattern(anyOf(pattern(&nodeText{text: "abc"}))))),
result: match.NewText("abc"),
},
{
ast: pattern(anyOf(
pattern(
&nodeText{text: "abc"},
&nodeSingle{},
),
pattern(
&nodeText{text: "abc"},
&nodeList{chars: "def"},
),
pattern(
&nodeText{text: "abc"},
),
pattern(
&nodeText{text: "abc"},
),
)),
result: match.NewBTree(
match.NewText("abc"),
nil,
match.AnyOf{Matchers: match.Matchers{
match.NewSingle(nil),
match.NewList([]rune{'d', 'e', 'f'}, false),
match.NewNothing(),
}},
),
},
{
ast: pattern(
&nodeRange{lo: 'a', hi: 'z'},
&nodeRange{lo: 'a', hi: 'x', not: true},
&nodeAny{},
),
result: match.NewBTree(
match.NewRow(
2,
match.Matchers{
match.NewRange('a', 'z', false),
match.NewRange('a', 'x', true),
}...,
),
nil,
match.NewSuper(),
),
},
{
ast: pattern(anyOf(
pattern(
&nodeText{text: "abc"},
&nodeList{chars: "abc"},
&nodeText{text: "ghi"},
),
pattern(
&nodeText{text: "abc"},
&nodeList{chars: "def"},
&nodeText{text: "ghi"},
),
)),
result: match.NewRow(
7,
match.Matchers{
match.NewText("abc"),
match.AnyOf{Matchers: match.Matchers{
match.NewList([]rune{'a', 'b', 'c'}, false),
match.NewList([]rune{'d', 'e', 'f'}, false),
}},
match.NewText("ghi"),
}...,
),
},
// {
// ast: pattern(
// anyOf(&nodeText{text: "a"}, &nodeText{text: "b"}),
// anyOf(&nodeText{text: "c"}, &nodeText{text: "d"}),
// ),
// result: match.AnyOf{Matchers: match.Matchers{
// match.NewRow(Matchers: match.Matchers{match.Raw{"a"}, match.Raw{"c", 1}}),
// match.NewRow(Matchers: match.Matchers{match.Raw{"a"}, match.Raw{"d"}}),
// match.NewRow(Matchers: match.Matchers{match.Raw{"b"}, match.Raw{"c", 1}}),
// match.NewRow(Matchers: match.Matchers{match.Raw{"b"}, match.Raw{"d"}}),
// }},
// },
} {
m, err := compile(test.ast, test.sep)
if err != nil {
t.Errorf("compilation error: %s", err)
continue
}
if !reflect.DeepEqual(m, test.result) {
t.Errorf("#%d results are not equal:\nexp: %#v\nact: %#v", id, test.result, m)
continue
}
}
}
const complexityString = "abcd"
//func BenchmarkComplexityAny(b *testing.B) {
// m := match.NewAny(nil)
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexityContains(b *testing.B) {
// m := match.NewContains()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexityList(b *testing.B) {
// m := match.NewList()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexityMax(b *testing.B) {
// m := match.NewMax()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexityMin(b *testing.B) {
// m := match.NewMin()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexityNothing(b *testing.B) {
// m := match.NewNothing()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexityPrefix(b *testing.B) {
// m := match.NewPrefix()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexityPrefixSuffix(b *testing.B) {
// m := match.NewPrefixSuffix()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexityRange(b *testing.B) {
// m := match.NewRange()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexityRow(b *testing.B) {
// m := match.NewRow()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexitySingle(b *testing.B) {
// m := match.NewSingle(nil)
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexitySuffix(b *testing.B) {
// m := match.NewSuffix()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexitySuper(b *testing.B) {
// m := match.NewSuper()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexityText(b *testing.B) {
// m := match.NewText()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexityAnyOf(b *testing.B) {
// m := match.NewAnyOf()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexityBTree(b *testing.B) {
// m := match.NewBTree(match.NewText("abc"), match.NewText("d"), match.NewText("e"))
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}
//func BenchmarkComplexityEveryOf(b *testing.B) {
// m := match.NewEveryOf()
// for i := 0; i < b.N; i++ {
// _ = m.Match(complexityString)
// _, _ = m.Index(complexityString)
// }
//}

75
vendor/github.com/gobwas/glob/glob.go generated vendored Normal file
View File

@ -0,0 +1,75 @@
package glob
// Glob represents compiled glob pattern.
type Glob interface {
Match(string) bool
}
// Compile creates Glob for given pattern and strings (if any present after pattern) as separators.
// The pattern syntax is:
//
// pattern:
// { term }
//
// term:
// `*` matches any sequence of non-separator characters
// `**` matches any sequence of characters
// `?` matches any single non-separator character
// `[` [ `!` ] { character-range } `]`
// character class (must be non-empty)
// `{` pattern-list `}`
// pattern alternatives
// c matches character c (c != `*`, `**`, `?`, `\`, `[`, `{`, `}`)
// `\` c matches character c
//
// character-range:
// c matches character c (c != `\\`, `-`, `]`)
// `\` c matches character c
// lo `-` hi matches character c for lo <= c <= hi
//
// pattern-list:
// pattern { `,` pattern }
// comma-separated (without spaces) patterns
//
func Compile(pattern string, separators ...rune) (Glob, error) {
ast, err := parse(newLexer(pattern))
if err != nil {
return nil, err
}
matcher, err := compile(ast, separators)
if err != nil {
return nil, err
}
return matcher, nil
}
// MustCompile is the same as Compile, except that if Compile returns error, this will panic
func MustCompile(pattern string, separators ...rune) Glob {
g, err := Compile(pattern, separators...)
if err != nil {
panic(err)
}
return g
}
// QuoteMeta returns a string that quotes all glob pattern meta characters
// inside the argument text; For example, QuoteMeta(`{foo*}`) returns `\[foo\*\]`.
func QuoteMeta(s string) string {
b := make([]byte, 2*len(s))
// a byte loop is correct because all meta characters are ASCII
j := 0
for i := 0; i < len(s); i++ {
if special(s[i]) {
b[j] = '\\'
j++
}
b[j] = s[i]
j++
}
return string(b[0:j])
}

507
vendor/github.com/gobwas/glob/glob_test.go generated vendored Normal file
View File

@ -0,0 +1,507 @@
package glob
import (
"regexp"
"testing"
)
const (
pattern_all = "[a-z][!a-x]*cat*[h][!b]*eyes*"
regexp_all = `^[a-z][^a-x].*cat.*[h][^b].*eyes.*$`
fixture_all_match = "my cat has very bright eyes"
fixture_all_mismatch = "my dog has very bright eyes"
pattern_plain = "google.com"
regexp_plain = `^google\.com$`
fixture_plain_match = "google.com"
fixture_plain_mismatch = "gobwas.com"
pattern_multiple = "https://*.google.*"
regexp_multiple = `^https:\/\/.*\.google\..*$`
fixture_multiple_match = "https://account.google.com"
fixture_multiple_mismatch = "https://google.com"
pattern_alternatives = "{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}"
regexp_alternatives = `^(https:\/\/.*\.google\..*|.*yandex\..*|.*yahoo\..*|.*mail\.ru)$`
fixture_alternatives_match = "http://yahoo.com"
fixture_alternatives_mismatch = "http://google.com"
pattern_alternatives_suffix = "{https://*gobwas.com,http://exclude.gobwas.com}"
regexp_alternatives_suffix = `^(https:\/\/.*gobwas\.com|http://exclude.gobwas.com)$`
fixture_alternatives_suffix_first_match = "https://safe.gobwas.com"
fixture_alternatives_suffix_first_mismatch = "http://safe.gobwas.com"
fixture_alternatives_suffix_second = "http://exclude.gobwas.com"
pattern_prefix = "abc*"
regexp_prefix = `^abc.*$`
pattern_suffix = "*def"
regexp_suffix = `^.*def$`
pattern_prefix_suffix = "ab*ef"
regexp_prefix_suffix = `^ab.*ef$`
fixture_prefix_suffix_match = "abcdef"
fixture_prefix_suffix_mismatch = "af"
pattern_alternatives_combine_lite = "{abc*def,abc?def,abc[zte]def}"
regexp_alternatives_combine_lite = `^(abc.*def|abc.def|abc[zte]def)$`
fixture_alternatives_combine_lite = "abczdef"
pattern_alternatives_combine_hard = "{abc*[a-c]def,abc?[d-g]def,abc[zte]?def}"
regexp_alternatives_combine_hard = `^(abc.*[a-c]def|abc.[d-g]def|abc[zte].def)$`
fixture_alternatives_combine_hard = "abczqdef"
)
type test struct {
pattern, match string
should bool
delimiters []rune
}
func glob(s bool, p, m string, d ...rune) test {
return test{p, m, s, d}
}
func TestGlob(t *testing.T) {
for _, test := range []test{
glob(true, "* ?at * eyes", "my cat has very bright eyes"),
glob(true, "abc", "abc"),
glob(true, "a*c", "abc"),
glob(true, "a*c", "a12345c"),
glob(true, "a?c", "a1c"),
glob(true, "a.b", "a.b", '.'),
glob(true, "a.*", "a.b", '.'),
glob(true, "a.**", "a.b.c", '.'),
glob(true, "a.?.c", "a.b.c", '.'),
glob(true, "a.?.?", "a.b.c", '.'),
glob(true, "?at", "cat"),
glob(true, "?at", "fat"),
glob(true, "*", "abc"),
glob(true, `\*`, "*"),
glob(true, "**", "a.b.c", '.'),
glob(false, "?at", "at"),
glob(false, "?at", "fat", 'f'),
glob(false, "a.*", "a.b.c", '.'),
glob(false, "a.?.c", "a.bb.c", '.'),
glob(false, "*", "a.b.c", '.'),
glob(true, "*test", "this is a test"),
glob(true, "this*", "this is a test"),
glob(true, "*is *", "this is a test"),
glob(true, "*is*a*", "this is a test"),
glob(true, "**test**", "this is a test"),
glob(true, "**is**a***test*", "this is a test"),
glob(false, "*is", "this is a test"),
glob(false, "*no*", "this is a test"),
glob(true, "[!a]*", "this is a test3"),
glob(true, "*abc", "abcabc"),
glob(true, "**abc", "abcabc"),
glob(true, "???", "abc"),
glob(true, "?*?", "abc"),
glob(true, "?*?", "ac"),
glob(true, "{abc,def}ghi", "defghi"),
glob(true, "{abc,abcd}a", "abcda"),
glob(true, "{a,ab}{bc,f}", "abc"),
glob(true, "{*,**}{a,b}", "ab"),
glob(false, "{*,**}{a,b}", "ac"),
glob(true, pattern_all, fixture_all_match),
glob(false, pattern_all, fixture_all_mismatch),
glob(true, pattern_plain, fixture_plain_match),
glob(false, pattern_plain, fixture_plain_mismatch),
glob(true, pattern_multiple, fixture_multiple_match),
glob(false, pattern_multiple, fixture_multiple_mismatch),
glob(true, pattern_alternatives, fixture_alternatives_match),
glob(false, pattern_alternatives, fixture_alternatives_mismatch),
glob(true, pattern_alternatives_suffix, fixture_alternatives_suffix_first_match),
glob(false, pattern_alternatives_suffix, fixture_alternatives_suffix_first_mismatch),
glob(true, pattern_alternatives_suffix, fixture_alternatives_suffix_second),
glob(true, pattern_alternatives_combine_hard, fixture_alternatives_combine_hard),
glob(true, pattern_alternatives_combine_lite, fixture_alternatives_combine_lite),
glob(true, pattern_prefix, fixture_prefix_suffix_match),
glob(false, pattern_prefix, fixture_prefix_suffix_mismatch),
glob(true, pattern_suffix, fixture_prefix_suffix_match),
glob(false, pattern_suffix, fixture_prefix_suffix_mismatch),
glob(true, pattern_prefix_suffix, fixture_prefix_suffix_match),
glob(false, pattern_prefix_suffix, fixture_prefix_suffix_mismatch),
} {
g, err := Compile(test.pattern, test.delimiters...)
if err != nil {
t.Errorf("parsing pattern %q error: %s", test.pattern, err)
continue
}
result := g.Match(test.match)
if result != test.should {
t.Errorf("pattern %q matching %q should be %v but got %v\n%s", test.pattern, test.match, test.should, result, g)
}
}
}
func TestQuoteMeta(t *testing.T) {
specialsQuoted := make([]byte, len(specials)*2)
for i, j := 0, 0; i < len(specials); i, j = i+1, j+2 {
specialsQuoted[j] = '\\'
specialsQuoted[j+1] = specials[i]
}
for id, test := range []struct {
in, out string
}{
{
in: `[foo*]`,
out: `\[foo\*\]`,
},
{
in: string(specials),
out: string(specialsQuoted),
},
{
in: string(append([]byte("some text and"), specials...)),
out: string(append([]byte("some text and"), specialsQuoted...)),
},
} {
act := QuoteMeta(test.in)
if act != test.out {
t.Errorf("#%d QuoteMeta(%q) = %q; want %q", id, test.in, act, test.out)
}
if _, err := Compile(act); err != nil {
t.Errorf("#%d _, err := Compile(QuoteMeta(%q) = %q); err = %q", id, test.in, act, err)
}
}
}
func BenchmarkParseGlob(b *testing.B) {
for i := 0; i < b.N; i++ {
Compile(pattern_all)
}
}
func BenchmarkParseRegexp(b *testing.B) {
for i := 0; i < b.N; i++ {
regexp.MustCompile(regexp_all)
}
}
func BenchmarkAllGlobMatch(b *testing.B) {
m, _ := Compile(pattern_all)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_all_match)
}
}
func BenchmarkAllGlobMatchParallel(b *testing.B) {
m, _ := Compile(pattern_all)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = m.Match(fixture_all_match)
}
})
}
func BenchmarkAllRegexpMatch(b *testing.B) {
m := regexp.MustCompile(regexp_all)
f := []byte(fixture_all_match)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkAllGlobMismatch(b *testing.B) {
m, _ := Compile(pattern_all)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_all_mismatch)
}
}
func BenchmarkAllGlobMismatchParallel(b *testing.B) {
m, _ := Compile(pattern_all)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = m.Match(fixture_all_mismatch)
}
})
}
func BenchmarkAllRegexpMismatch(b *testing.B) {
m := regexp.MustCompile(regexp_all)
f := []byte(fixture_all_mismatch)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkMultipleGlobMatch(b *testing.B) {
m, _ := Compile(pattern_multiple)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_multiple_match)
}
}
func BenchmarkMultipleRegexpMatch(b *testing.B) {
m := regexp.MustCompile(regexp_multiple)
f := []byte(fixture_multiple_match)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkMultipleGlobMismatch(b *testing.B) {
m, _ := Compile(pattern_multiple)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_multiple_mismatch)
}
}
func BenchmarkMultipleRegexpMismatch(b *testing.B) {
m := regexp.MustCompile(regexp_multiple)
f := []byte(fixture_multiple_mismatch)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkAlternativesGlobMatch(b *testing.B) {
m, _ := Compile(pattern_alternatives)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_alternatives_match)
}
}
func BenchmarkAlternativesGlobMismatch(b *testing.B) {
m, _ := Compile(pattern_alternatives)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_alternatives_mismatch)
}
}
func BenchmarkAlternativesRegexpMatch(b *testing.B) {
m := regexp.MustCompile(regexp_alternatives)
f := []byte(fixture_alternatives_match)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkAlternativesRegexpMismatch(b *testing.B) {
m := regexp.MustCompile(regexp_alternatives)
f := []byte(fixture_alternatives_mismatch)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkAlternativesSuffixFirstGlobMatch(b *testing.B) {
m, _ := Compile(pattern_alternatives_suffix)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_alternatives_suffix_first_match)
}
}
func BenchmarkAlternativesSuffixFirstGlobMismatch(b *testing.B) {
m, _ := Compile(pattern_alternatives_suffix)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_alternatives_suffix_first_mismatch)
}
}
func BenchmarkAlternativesSuffixSecondGlobMatch(b *testing.B) {
m, _ := Compile(pattern_alternatives_suffix)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_alternatives_suffix_second)
}
}
func BenchmarkAlternativesCombineLiteGlobMatch(b *testing.B) {
m, _ := Compile(pattern_alternatives_combine_lite)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_alternatives_combine_lite)
}
}
func BenchmarkAlternativesCombineHardGlobMatch(b *testing.B) {
m, _ := Compile(pattern_alternatives_combine_hard)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_alternatives_combine_hard)
}
}
func BenchmarkAlternativesSuffixFirstRegexpMatch(b *testing.B) {
m := regexp.MustCompile(regexp_alternatives_suffix)
f := []byte(fixture_alternatives_suffix_first_match)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkAlternativesSuffixFirstRegexpMismatch(b *testing.B) {
m := regexp.MustCompile(regexp_alternatives_suffix)
f := []byte(fixture_alternatives_suffix_first_mismatch)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkAlternativesSuffixSecondRegexpMatch(b *testing.B) {
m := regexp.MustCompile(regexp_alternatives_suffix)
f := []byte(fixture_alternatives_suffix_second)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkAlternativesCombineLiteRegexpMatch(b *testing.B) {
m := regexp.MustCompile(regexp_alternatives_combine_lite)
f := []byte(fixture_alternatives_combine_lite)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkAlternativesCombineHardRegexpMatch(b *testing.B) {
m := regexp.MustCompile(regexp_alternatives_combine_hard)
f := []byte(fixture_alternatives_combine_hard)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkPlainGlobMatch(b *testing.B) {
m, _ := Compile(pattern_plain)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_plain_match)
}
}
func BenchmarkPlainRegexpMatch(b *testing.B) {
m := regexp.MustCompile(regexp_plain)
f := []byte(fixture_plain_match)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkPlainGlobMismatch(b *testing.B) {
m, _ := Compile(pattern_plain)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_plain_mismatch)
}
}
func BenchmarkPlainRegexpMismatch(b *testing.B) {
m := regexp.MustCompile(regexp_plain)
f := []byte(fixture_plain_mismatch)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkPrefixGlobMatch(b *testing.B) {
m, _ := Compile(pattern_prefix)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_prefix_suffix_match)
}
}
func BenchmarkPrefixRegexpMatch(b *testing.B) {
m := regexp.MustCompile(regexp_prefix)
f := []byte(fixture_prefix_suffix_match)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkPrefixGlobMismatch(b *testing.B) {
m, _ := Compile(pattern_prefix)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_prefix_suffix_mismatch)
}
}
func BenchmarkPrefixRegexpMismatch(b *testing.B) {
m := regexp.MustCompile(regexp_prefix)
f := []byte(fixture_prefix_suffix_mismatch)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkSuffixGlobMatch(b *testing.B) {
m, _ := Compile(pattern_suffix)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_prefix_suffix_match)
}
}
func BenchmarkSuffixRegexpMatch(b *testing.B) {
m := regexp.MustCompile(regexp_suffix)
f := []byte(fixture_prefix_suffix_match)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkSuffixGlobMismatch(b *testing.B) {
m, _ := Compile(pattern_suffix)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_prefix_suffix_mismatch)
}
}
func BenchmarkSuffixRegexpMismatch(b *testing.B) {
m := regexp.MustCompile(regexp_suffix)
f := []byte(fixture_prefix_suffix_mismatch)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkPrefixSuffixGlobMatch(b *testing.B) {
m, _ := Compile(pattern_prefix_suffix)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_prefix_suffix_match)
}
}
func BenchmarkPrefixSuffixRegexpMatch(b *testing.B) {
m := regexp.MustCompile(regexp_prefix_suffix)
f := []byte(fixture_prefix_suffix_match)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}
func BenchmarkPrefixSuffixGlobMismatch(b *testing.B) {
m, _ := Compile(pattern_prefix_suffix)
for i := 0; i < b.N; i++ {
_ = m.Match(fixture_prefix_suffix_mismatch)
}
}
func BenchmarkPrefixSuffixRegexpMismatch(b *testing.B) {
m := regexp.MustCompile(regexp_prefix_suffix)
f := []byte(fixture_prefix_suffix_mismatch)
for i := 0; i < b.N; i++ {
_ = m.Match(f)
}
}

478
vendor/github.com/gobwas/glob/lexer.go generated vendored Normal file
View File

@ -0,0 +1,478 @@
package glob
import (
"bytes"
"fmt"
"strings"
"unicode/utf8"
)
const (
char_any = '*'
char_separator = ','
char_single = '?'
char_escape = '\\'
char_range_open = '['
char_range_close = ']'
char_terms_open = '{'
char_terms_close = '}'
char_range_not = '!'
char_range_between = '-'
)
var specials = []byte{
char_any,
char_single,
char_escape,
char_range_open,
char_range_close,
char_terms_open,
char_terms_close,
}
func special(c byte) bool {
return bytes.IndexByte(specials, c) != -1
}
var eof rune = 0
type stateFn func(*lexer) stateFn
type itemType int
const (
item_eof itemType = iota
item_error
item_text
item_char
item_any
item_super
item_single
item_not
item_separator
item_range_open
item_range_close
item_range_lo
item_range_hi
item_range_between
item_terms_open
item_terms_close
)
func (i itemType) String() string {
switch i {
case item_eof:
return "eof"
case item_error:
return "error"
case item_text:
return "text"
case item_char:
return "char"
case item_any:
return "any"
case item_super:
return "super"
case item_single:
return "single"
case item_not:
return "not"
case item_separator:
return "separator"
case item_range_open:
return "range_open"
case item_range_close:
return "range_close"
case item_range_lo:
return "range_lo"
case item_range_hi:
return "range_hi"
case item_range_between:
return "range_between"
case item_terms_open:
return "terms_open"
case item_terms_close:
return "terms_close"
default:
return "undef"
}
}
type item struct {
t itemType
s string
}
func (i item) String() string {
return fmt.Sprintf("%v<%s>", i.t, i.s)
}
type lexer struct {
input string
start int
pos int
width int
runes int
termScopes []int
termPhrases map[int]int
state stateFn
items chan item
}
func newLexer(source string) *lexer {
l := &lexer{
input: source,
state: lexText,
items: make(chan item, len(source)),
termPhrases: make(map[int]int),
}
return l
}
func (l *lexer) run() {
for state := lexText; state != nil; {
state = state(l)
}
close(l.items)
}
func (l *lexer) nextItem() item {
for {
select {
case item := <-l.items:
return item
default:
if l.state == nil {
return item{t: item_eof}
}
l.state = l.state(l)
}
}
panic("something went wrong")
}
func (l *lexer) read() (r rune) {
if l.pos >= len(l.input) {
return eof
}
r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
l.pos += l.width
l.runes++
return
}
func (l *lexer) unread() {
l.pos -= l.width
l.runes--
}
func (l *lexer) reset() {
l.pos = l.start
l.runes = 0
}
func (l *lexer) ignore() {
l.start = l.pos
l.runes = 0
}
func (l *lexer) lookahead() rune {
r := l.read()
if r != eof {
l.unread()
}
return r
}
func (l *lexer) accept(valid string) bool {
if strings.IndexRune(valid, l.read()) != -1 {
return true
}
l.unread()
return false
}
func (l *lexer) acceptAll(valid string) {
for strings.IndexRune(valid, l.read()) != -1 {
}
l.unread()
}
func (l *lexer) emit(t itemType) {
if l.pos == len(l.input) {
l.items <- item{t, l.input[l.start:]}
} else {
l.items <- item{t, l.input[l.start:l.pos]}
}
l.start = l.pos
l.runes = 0
l.width = 0
}
func (l *lexer) emitMaybe(t itemType) {
if l.pos > l.start {
l.emit(t)
}
}
func (l *lexer) errorf(format string, args ...interface{}) {
l.items <- item{item_error, fmt.Sprintf(format, args...)}
}
func lexText(l *lexer) stateFn {
for {
c := l.read()
if c == eof {
break
}
switch c {
case char_escape:
l.unread()
l.emitMaybe(item_text)
l.read()
l.ignore()
if l.read() == eof {
l.errorf("unclosed '%s' character", string(char_escape))
return nil
}
case char_single:
l.unread()
l.emitMaybe(item_text)
return lexSingle
case char_any:
var n stateFn
if l.lookahead() == char_any {
n = lexSuper
} else {
n = lexAny
}
l.unread()
l.emitMaybe(item_text)
return n
case char_range_open:
l.unread()
l.emitMaybe(item_text)
return lexRangeOpen
case char_terms_open:
l.unread()
l.emitMaybe(item_text)
return lexTermsOpen
case char_terms_close:
l.unread()
l.emitMaybe(item_text)
return lexTermsClose
case char_separator:
l.unread()
l.emitMaybe(item_text)
return lexSeparator
}
}
if l.pos > l.start {
l.emit(item_text)
}
if len(l.termScopes) != 0 {
l.errorf("invalid pattern syntax: unclosed terms")
return nil
}
l.emit(item_eof)
return nil
}
func lexInsideRange(l *lexer) stateFn {
for {
c := l.read()
if c == eof {
l.errorf("unclosed range construction")
return nil
}
switch c {
case char_range_not:
// only first char makes sense
if l.pos-l.width == l.start {
l.emit(item_not)
}
case char_range_between:
if l.runes != 2 {
l.errorf("unexpected length of lo char inside range")
return nil
}
l.reset()
return lexRangeHiLo
case char_range_close:
l.unread()
l.emitMaybe(item_text)
return lexRangeClose
}
}
}
func lexRangeHiLo(l *lexer) stateFn {
start := l.start
for {
c := l.read()
if c == eof {
l.errorf("unexpected end of input")
return nil
}
switch c {
case char_range_between:
if l.runes != 1 {
l.errorf("unexpected length of range: single character expected before minus")
return nil
}
l.emit(item_range_between)
case char_range_close:
l.unread()
if l.runes != 1 {
l.errorf("unexpected length of range: single character expected before close")
return nil
}
l.emit(item_range_hi)
return lexRangeClose
default:
if start != l.start {
continue
}
if l.runes != 1 {
l.errorf("unexpected length of range: single character expected at the begining")
return nil
}
l.emit(item_range_lo)
}
}
}
func lexAny(l *lexer) stateFn {
l.pos += 1
l.emit(item_any)
return lexText
}
func lexSuper(l *lexer) stateFn {
l.pos += 2
l.emit(item_super)
return lexText
}
func lexSingle(l *lexer) stateFn {
l.pos += 1
l.emit(item_single)
return lexText
}
func lexSeparator(l *lexer) stateFn {
if len(l.termScopes) == 0 {
l.errorf("syntax error: separator not inside terms list")
return nil
}
posOpen := l.termScopes[len(l.termScopes)-1]
if l.pos-posOpen == 1 {
l.errorf("syntax error: empty term before separator")
return nil
}
l.termPhrases[posOpen] += 1
l.pos += 1
l.emit(item_separator)
return lexText
}
func lexTermsOpen(l *lexer) stateFn {
l.termScopes = append(l.termScopes, l.pos)
l.pos += 1
l.emit(item_terms_open)
return lexText
}
func lexTermsClose(l *lexer) stateFn {
if len(l.termScopes) == 0 {
l.errorf("unexpected closing of terms: there is no opened terms")
return nil
}
lastOpen := len(l.termScopes) - 1
posOpen := l.termScopes[lastOpen]
// if it is empty term
if posOpen == l.pos-1 {
l.errorf("term could not be empty")
return nil
}
if l.termPhrases[posOpen] == 0 {
l.errorf("term must contain >1 phrases")
return nil
}
// cleanup
l.termScopes = l.termScopes[:lastOpen]
delete(l.termPhrases, posOpen)
l.pos += 1
l.emit(item_terms_close)
return lexText
}
func lexRangeOpen(l *lexer) stateFn {
l.pos += 1
l.emit(item_range_open)
return lexInsideRange
}
func lexRangeClose(l *lexer) stateFn {
l.pos += 1
l.emit(item_range_close)
return lexText
}

136
vendor/github.com/gobwas/glob/lexer_test.go generated vendored Normal file
View File

@ -0,0 +1,136 @@
package glob
import (
"testing"
)
func TestLexGood(t *testing.T) {
for id, test := range []struct {
pattern string
items []item
}{
{
pattern: "hello",
items: []item{
item{item_text, "hello"},
item{item_eof, ""},
},
},
{
pattern: "hello?",
items: []item{
item{item_text, "hello"},
item{item_single, "?"},
item{item_eof, ""},
},
},
{
pattern: "hellof*",
items: []item{
item{item_text, "hellof"},
item{item_any, "*"},
item{item_eof, ""},
},
},
{
pattern: "hello**",
items: []item{
item{item_text, "hello"},
item{item_super, "**"},
item{item_eof, ""},
},
},
{
pattern: "[日-語]",
items: []item{
item{item_range_open, "["},
item{item_range_lo, "日"},
item{item_range_between, "-"},
item{item_range_hi, "語"},
item{item_range_close, "]"},
item{item_eof, ""},
},
},
{
pattern: "[!日-語]",
items: []item{
item{item_range_open, "["},
item{item_not, "!"},
item{item_range_lo, "日"},
item{item_range_between, "-"},
item{item_range_hi, "語"},
item{item_range_close, "]"},
item{item_eof, ""},
},
},
{
pattern: "[日本語]",
items: []item{
item{item_range_open, "["},
item{item_text, "日本語"},
item{item_range_close, "]"},
item{item_eof, ""},
},
},
{
pattern: "[!日本語]",
items: []item{
item{item_range_open, "["},
item{item_not, "!"},
item{item_text, "日本語"},
item{item_range_close, "]"},
item{item_eof, ""},
},
},
{
pattern: "{a,b}",
items: []item{
item{item_terms_open, "{"},
item{item_text, "a"},
item{item_separator, ","},
item{item_text, "b"},
item{item_terms_close, "}"},
item{item_eof, ""},
},
},
{
pattern: "{[!日-語],*,?,{a,b,\\c}}",
items: []item{
item{item_terms_open, "{"},
item{item_range_open, "["},
item{item_not, "!"},
item{item_range_lo, "日"},
item{item_range_between, "-"},
item{item_range_hi, "語"},
item{item_range_close, "]"},
item{item_separator, ","},
item{item_any, "*"},
item{item_separator, ","},
item{item_single, "?"},
item{item_separator, ","},
item{item_terms_open, "{"},
item{item_text, "a"},
item{item_separator, ","},
item{item_text, "b"},
item{item_separator, ","},
item{item_text, "c"},
item{item_terms_close, "}"},
item{item_terms_close, "}"},
item{item_eof, ""},
},
},
} {
lexer := newLexer(test.pattern)
for i, exp := range test.items {
act := lexer.nextItem()
if act.t != exp.t {
t.Errorf("#%d wrong %d-th item type: exp: %v; act: %v (%s vs %s)", id, i, exp.t, act.t, exp, act)
break
}
if act.s != exp.s {
t.Errorf("#%d wrong %d-th item contents: exp: %q; act: %q (%s vs %s)", id, i, exp.s, act.s, exp, act)
break
}
}
}
}

45
vendor/github.com/gobwas/glob/match/any.go generated vendored Normal file
View File

@ -0,0 +1,45 @@
package match
import (
"fmt"
"github.com/gobwas/glob/strings"
)
type Any struct {
Separators []rune
}
func NewAny(s []rune) Any {
return Any{s}
}
func (self Any) Match(s string) bool {
return strings.IndexAnyRunes(s, self.Separators) == -1
}
func (self Any) Index(s string) (int, []int) {
found := strings.IndexAnyRunes(s, self.Separators)
switch found {
case -1:
case 0:
return 0, segments0
default:
s = s[:found]
}
segments := acquireSegments(len(s))
for i := range s {
segments = append(segments, i)
}
segments = append(segments, len(s))
return 0, segments
}
func (self Any) Len() int {
return lenNo
}
func (self Any) String() string {
return fmt.Sprintf("<any:![%s]>", string(self.Separators))
}

85
vendor/github.com/gobwas/glob/match/any_of.go generated vendored Normal file
View File

@ -0,0 +1,85 @@
package match
import (
"fmt"
)
type AnyOf struct {
Matchers Matchers
}
func NewAnyOf(m ...Matcher) AnyOf {
return AnyOf{Matchers(m)}
}
func (self *AnyOf) Add(m Matcher) error {
self.Matchers = append(self.Matchers, m)
return nil
}
func (self AnyOf) Match(s string) bool {
for _, m := range self.Matchers {
if m.Match(s) {
return true
}
}
return false
}
func (self AnyOf) Index(s string) (int, []int) {
index := -1
segments := acquireSegments(len(s))
for _, m := range self.Matchers {
idx, seg := m.Index(s)
if idx == -1 {
continue
}
if index == -1 || idx < index {
index = idx
segments = append(segments[:0], seg...)
continue
}
if idx > index {
continue
}
// here idx == index
segments = appendMerge(segments, seg)
}
if index == -1 {
releaseSegments(segments)
return -1, nil
}
return index, segments
}
func (self AnyOf) Len() (l int) {
l = -1
for _, m := range self.Matchers {
ml := m.Len()
if ml == -1 {
return -1
}
if l == -1 {
l = ml
continue
}
if l != ml {
return -1
}
}
return
}
func (self AnyOf) String() string {
return fmt.Sprintf("<any_of:[%s]>", self.Matchers)
}

53
vendor/github.com/gobwas/glob/match/any_of_test.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
package match
import (
"reflect"
"testing"
)
func TestAnyOfIndex(t *testing.T) {
for id, test := range []struct {
matchers Matchers
fixture string
index int
segments []int
}{
{
Matchers{
NewAny(nil),
NewText("b"),
NewText("c"),
},
"abc",
0,
[]int{0, 1, 2, 3},
},
{
Matchers{
NewPrefix("b"),
NewSuffix("c"),
},
"abc",
0,
[]int{3},
},
{
Matchers{
NewList([]rune("[def]"), false),
NewList([]rune("[abc]"), false),
},
"abcdef",
0,
[]int{1},
},
} {
everyOf := NewAnyOf(test.matchers...)
index, segments := everyOf.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}

57
vendor/github.com/gobwas/glob/match/any_test.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
package match
import (
"reflect"
"testing"
)
func TestAnyIndex(t *testing.T) {
for id, test := range []struct {
sep []rune
fixture string
index int
segments []int
}{
{
[]rune{'.'},
"abc",
0,
[]int{0, 1, 2, 3},
},
{
[]rune{'.'},
"abc.def",
0,
[]int{0, 1, 2, 3},
},
} {
p := NewAny(test.sep)
index, segments := p.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkIndexAny(b *testing.B) {
m := NewAny(bench_separators)
for i := 0; i < b.N; i++ {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
}
func BenchmarkIndexAnyParallel(b *testing.B) {
m := NewAny(bench_separators)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
})
}

146
vendor/github.com/gobwas/glob/match/btree.go generated vendored Normal file
View File

@ -0,0 +1,146 @@
package match
import (
"fmt"
"unicode/utf8"
)
type BTree struct {
Value Matcher
Left Matcher
Right Matcher
ValueLengthRunes int
LeftLengthRunes int
RightLengthRunes int
LengthRunes int
}
func NewBTree(Value, Left, Right Matcher) (tree BTree) {
tree.Value = Value
tree.Left = Left
tree.Right = Right
lenOk := true
if tree.ValueLengthRunes = Value.Len(); tree.ValueLengthRunes == -1 {
lenOk = false
}
if Left != nil {
if tree.LeftLengthRunes = Left.Len(); tree.LeftLengthRunes == -1 {
lenOk = false
}
}
if Right != nil {
if tree.RightLengthRunes = Right.Len(); tree.RightLengthRunes == -1 {
lenOk = false
}
}
if lenOk {
tree.LengthRunes = tree.LeftLengthRunes + tree.ValueLengthRunes + tree.RightLengthRunes
} else {
tree.LengthRunes = -1
}
return tree
}
func (self BTree) Len() int {
return self.LengthRunes
}
// todo?
func (self BTree) Index(s string) (int, []int) {
return -1, nil
}
func (self BTree) Match(s string) bool {
inputLen := len(s)
// self.Length, self.RLen and self.LLen are values meaning the length of runes for each part
// here we manipulating byte length for better optimizations
// but these checks still works, cause minLen of 1-rune string is 1 byte.
if self.LengthRunes != -1 && self.LengthRunes > inputLen {
return false
}
// try to cut unnecessary parts
// by knowledge of length of right and left part
var offset, limit int
if self.LeftLengthRunes >= 0 {
offset = self.LeftLengthRunes
}
if self.RightLengthRunes >= 0 {
limit = inputLen - self.RightLengthRunes
} else {
limit = inputLen
}
for offset < limit {
// search for matching part in substring
index, segments := self.Value.Index(s[offset:limit])
if index == -1 {
releaseSegments(segments)
return false
}
l := s[:offset+index]
var left bool
if self.Left != nil {
left = self.Left.Match(l)
} else {
left = l == ""
}
if left {
for i := len(segments) - 1; i >= 0; i-- {
length := segments[i]
var right bool
var r string
// if there is no string for the right branch
if inputLen <= offset+index+length {
r = ""
} else {
r = s[offset+index+length:]
}
if self.Right != nil {
right = self.Right.Match(r)
} else {
right = r == ""
}
if right {
releaseSegments(segments)
return true
}
}
}
_, step := utf8.DecodeRuneInString(s[offset+index:])
offset += index + step
releaseSegments(segments)
}
return false
}
func (self BTree) String() string {
const n string = "<nil>"
var l, r string
if self.Left == nil {
l = n
} else {
l = self.Left.String()
}
if self.Right == nil {
r = n
} else {
r = self.Right.String()
}
return fmt.Sprintf("<btree:[%s<-%s->%s]>", l, self.Value, r)
}

90
vendor/github.com/gobwas/glob/match/btree_test.go generated vendored Normal file
View File

@ -0,0 +1,90 @@
package match
import (
"testing"
)
func TestBTree(t *testing.T) {
for id, test := range []struct {
tree BTree
str string
exp bool
}{
{
NewBTree(NewText("abc"), NewSuper(), NewSuper()),
"abc",
true,
},
{
NewBTree(NewText("a"), NewSingle(nil), NewSingle(nil)),
"aaa",
true,
},
{
NewBTree(NewText("b"), NewSingle(nil), nil),
"bbb",
false,
},
{
NewBTree(
NewText("c"),
NewBTree(
NewSingle(nil),
NewSuper(),
nil,
),
nil,
),
"abc",
true,
},
} {
act := test.tree.Match(test.str)
if act != test.exp {
t.Errorf("#%d match %q error: act: %t; exp: %t", id, test.str, act, test.exp)
continue
}
}
}
type fakeMatcher struct {
len int
name string
}
func (f *fakeMatcher) Match(string) bool {
return true
}
var i = 3
func (f *fakeMatcher) Index(s string) (int, []int) {
seg := make([]int, 0, i)
for x := 0; x < i; x++ {
seg = append(seg, x)
}
return 0, seg
}
func (f *fakeMatcher) Len() int {
return f.len
}
func (f *fakeMatcher) String() string {
return f.name
}
func BenchmarkMatchBTree(b *testing.B) {
l := &fakeMatcher{4, "left_fake"}
r := &fakeMatcher{4, "right_fake"}
v := &fakeMatcher{2, "value_fake"}
// must be <= len(l + r + v)
fixture := "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij"
bt := NewBTree(v, l, r)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
bt.Match(fixture)
}
})
}

58
vendor/github.com/gobwas/glob/match/contains.go generated vendored Normal file
View File

@ -0,0 +1,58 @@
package match
import (
"fmt"
"strings"
)
type Contains struct {
Needle string
Not bool
}
func NewContains(needle string, not bool) Contains {
return Contains{needle, not}
}
func (self Contains) Match(s string) bool {
return strings.Contains(s, self.Needle) != self.Not
}
func (self Contains) Index(s string) (int, []int) {
var offset int
idx := strings.Index(s, self.Needle)
if !self.Not {
if idx == -1 {
return -1, nil
}
offset = idx + len(self.Needle)
if len(s) <= offset {
return 0, []int{offset}
}
s = s[offset:]
} else if idx != -1 {
s = s[:idx]
}
segments := acquireSegments(len(s) + 1)
for i, _ := range s {
segments = append(segments, offset+i)
}
return 0, append(segments, offset+len(s))
}
func (self Contains) Len() int {
return lenNo
}
func (self Contains) String() string {
var not string
if self.Not {
not = "!"
}
return fmt.Sprintf("<contains:%s[%s]>", not, self.Needle)
}

74
vendor/github.com/gobwas/glob/match/contains_test.go generated vendored Normal file
View File

@ -0,0 +1,74 @@
package match
import (
"reflect"
"testing"
)
func TestContainsIndex(t *testing.T) {
for id, test := range []struct {
prefix string
not bool
fixture string
index int
segments []int
}{
{
"ab",
false,
"abc",
0,
[]int{2, 3},
},
{
"ab",
false,
"fffabfff",
0,
[]int{5, 6, 7, 8},
},
{
"ab",
true,
"abc",
0,
[]int{0},
},
{
"ab",
true,
"fffabfff",
0,
[]int{0, 1, 2, 3},
},
} {
p := NewContains(test.prefix, test.not)
index, segments := p.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkIndexContains(b *testing.B) {
m := NewContains(string(bench_separators), true)
for i := 0; i < b.N; i++ {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
}
func BenchmarkIndexContainsParallel(b *testing.B) {
m := NewContains(string(bench_separators), true)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
})
}

55
vendor/github.com/gobwas/glob/match/debug/debug.go generated vendored Normal file
View File

@ -0,0 +1,55 @@
package debug
import (
"bytes"
"fmt"
"github.com/gobwas/glob/match"
"math/rand"
)
func Graphviz(pattern string, m match.Matcher) string {
return fmt.Sprintf(`digraph G {graph[label="%s"];%s}`, pattern, graphviz_internal(m, fmt.Sprintf("%x", rand.Int63())))
}
func graphviz_internal(m match.Matcher, id string) string {
buf := &bytes.Buffer{}
switch matcher := m.(type) {
case match.BTree:
fmt.Fprintf(buf, `"%s"[label="%s"];`, id, matcher.Value.String())
for _, m := range []match.Matcher{matcher.Left, matcher.Right} {
switch n := m.(type) {
case nil:
rnd := rand.Int63()
fmt.Fprintf(buf, `"%x"[label="<nil>"];`, rnd)
fmt.Fprintf(buf, `"%s"->"%x";`, id, rnd)
default:
sub := fmt.Sprintf("%x", rand.Int63())
fmt.Fprintf(buf, `"%s"->"%s";`, id, sub)
fmt.Fprintf(buf, graphviz_internal(n, sub))
}
}
case match.AnyOf:
fmt.Fprintf(buf, `"%s"[label="AnyOf"];`, id)
for _, m := range matcher.Matchers {
rnd := rand.Int63()
fmt.Fprintf(buf, graphviz_internal(m, fmt.Sprintf("%x", rnd)))
fmt.Fprintf(buf, `"%s"->"%x";`, id, rnd)
}
case match.EveryOf:
fmt.Fprintf(buf, `"%s"[label="EveryOf"];`, id)
for _, m := range matcher.Matchers {
rnd := rand.Int63()
fmt.Fprintf(buf, graphviz_internal(m, fmt.Sprintf("%x", rnd)))
fmt.Fprintf(buf, `"%s"->"%x";`, id, rnd)
}
default:
fmt.Fprintf(buf, `"%s"[label="%s"];`, id, m.String())
}
return buf.String()
}

99
vendor/github.com/gobwas/glob/match/every_of.go generated vendored Normal file
View File

@ -0,0 +1,99 @@
package match
import (
"fmt"
)
type EveryOf struct {
Matchers Matchers
}
func NewEveryOf(m ...Matcher) EveryOf {
return EveryOf{Matchers(m)}
}
func (self *EveryOf) Add(m Matcher) error {
self.Matchers = append(self.Matchers, m)
return nil
}
func (self EveryOf) Len() (l int) {
for _, m := range self.Matchers {
if ml := m.Len(); l > 0 {
l += ml
} else {
return -1
}
}
return
}
func (self EveryOf) Index(s string) (int, []int) {
var index int
var offset int
// make `in` with cap as len(s),
// cause it is the maximum size of output segments values
next := acquireSegments(len(s))
current := acquireSegments(len(s))
sub := s
for i, m := range self.Matchers {
idx, seg := m.Index(sub)
if idx == -1 {
releaseSegments(next)
releaseSegments(current)
return -1, nil
}
if i == 0 {
// we use copy here instead of `current = seg`
// cause seg is a slice from reusable buffer `in`
// and it could be overwritten in next iteration
current = append(current, seg...)
} else {
// clear the next
next = next[:0]
delta := index - (idx + offset)
for _, ex := range current {
for _, n := range seg {
if ex+delta == n {
next = append(next, n)
}
}
}
if len(next) == 0 {
releaseSegments(next)
releaseSegments(current)
return -1, nil
}
current = append(current[:0], next...)
}
index = idx + offset
sub = s[index:]
offset += idx
}
releaseSegments(next)
return index, current
}
func (self EveryOf) Match(s string) bool {
for _, m := range self.Matchers {
if !m.Match(s) {
return false
}
}
return true
}
func (self EveryOf) String() string {
return fmt.Sprintf("<every_of:[%s]>", self.Matchers)
}

45
vendor/github.com/gobwas/glob/match/every_of_test.go generated vendored Normal file
View File

@ -0,0 +1,45 @@
package match
import (
"reflect"
"testing"
)
func TestEveryOfIndex(t *testing.T) {
for id, test := range []struct {
matchers Matchers
fixture string
index int
segments []int
}{
{
Matchers{
NewAny(nil),
NewText("b"),
NewText("c"),
},
"dbc",
-1,
nil,
},
{
Matchers{
NewAny(nil),
NewPrefix("b"),
NewSuffix("c"),
},
"abc",
1,
[]int{2},
},
} {
everyOf := NewEveryOf(test.matchers...)
index, segments := everyOf.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}

49
vendor/github.com/gobwas/glob/match/list.go generated vendored Normal file
View File

@ -0,0 +1,49 @@
package match
import (
"fmt"
"github.com/gobwas/glob/runes"
"unicode/utf8"
)
type List struct {
List []rune
Not bool
}
func NewList(list []rune, not bool) List {
return List{list, not}
}
func (self List) Match(s string) bool {
r, w := utf8.DecodeRuneInString(s)
if len(s) > w {
return false
}
inList := runes.IndexRune(self.List, r) != -1
return inList == !self.Not
}
func (self List) Len() int {
return lenOne
}
func (self List) Index(s string) (int, []int) {
for i, r := range s {
if self.Not == (runes.IndexRune(self.List, r) == -1) {
return i, segmentsByRuneLength[utf8.RuneLen(r)]
}
}
return -1, nil
}
func (self List) String() string {
var not string
if self.Not {
not = "!"
}
return fmt.Sprintf("<list:%s[%s]>", not, string(self.List))
}

58
vendor/github.com/gobwas/glob/match/list_test.go generated vendored Normal file
View File

@ -0,0 +1,58 @@
package match
import (
"reflect"
"testing"
)
func TestListIndex(t *testing.T) {
for id, test := range []struct {
list []rune
not bool
fixture string
index int
segments []int
}{
{
[]rune("ab"),
false,
"abc",
0,
[]int{1},
},
{
[]rune("ab"),
true,
"fffabfff",
0,
[]int{1},
},
} {
p := NewList(test.list, test.not)
index, segments := p.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkIndexList(b *testing.B) {
m := NewList([]rune("def"), false)
for i := 0; i < b.N; i++ {
m.Index(bench_pattern)
}
}
func BenchmarkIndexListParallel(b *testing.B) {
m := NewList([]rune("def"), false)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
m.Index(bench_pattern)
}
})
}

81
vendor/github.com/gobwas/glob/match/match.go generated vendored Normal file
View File

@ -0,0 +1,81 @@
package match
// todo common table of rune's length
import (
"fmt"
"strings"
)
const lenOne = 1
const lenZero = 0
const lenNo = -1
type Matcher interface {
Match(string) bool
Index(string) (int, []int)
Len() int
String() string
}
type Matchers []Matcher
func (m Matchers) String() string {
var s []string
for _, matcher := range m {
s = append(s, fmt.Sprint(matcher))
}
return fmt.Sprintf("%s", strings.Join(s, ","))
}
// appendMerge merges and sorts given already SORTED and UNIQUE segments.
func appendMerge(target, sub []int) []int {
lt, ls := len(target), len(sub)
out := make([]int, 0, lt+ls)
for x, y := 0, 0; x < lt || y < ls; {
if x >= lt {
out = append(out, sub[y:]...)
break
}
if y >= ls {
out = append(out, target[x:]...)
break
}
xValue := target[x]
yValue := sub[y]
switch {
case xValue == yValue:
out = append(out, xValue)
x++
y++
case xValue < yValue:
out = append(out, xValue)
x++
case yValue < xValue:
out = append(out, yValue)
y++
}
}
target = append(target[:0], out...)
return target
}
func reverseSegments(input []int) {
l := len(input)
m := l / 2
for i := 0; i < m; i++ {
input[i], input[l-i-1] = input[l-i-1], input[i]
}
}

90
vendor/github.com/gobwas/glob/match/match_test.go generated vendored Normal file
View File

@ -0,0 +1,90 @@
package match
import (
"reflect"
"testing"
"unicode/utf8"
)
var bench_separators = []rune{'.'}
const bench_pattern = "abcdefghijklmnopqrstuvwxyz0123456789"
func TestAppendMerge(t *testing.T) {
for id, test := range []struct {
segments [2][]int
exp []int
}{
{
[2][]int{
[]int{0, 6, 7},
[]int{0, 1, 3},
},
[]int{0, 1, 3, 6, 7},
},
{
[2][]int{
[]int{0, 1, 3, 6, 7},
[]int{0, 1, 10},
},
[]int{0, 1, 3, 6, 7, 10},
},
} {
act := appendMerge(test.segments[0], test.segments[1])
if !reflect.DeepEqual(act, test.exp) {
t.Errorf("#%d merge sort segments unexpected:\nact: %v\nexp:%v", id, act, test.exp)
continue
}
}
}
func BenchmarkAppendMerge(b *testing.B) {
s1 := []int{0, 1, 3, 6, 7}
s2 := []int{0, 1, 3}
for i := 0; i < b.N; i++ {
appendMerge(s1, s2)
}
}
func BenchmarkAppendMergeParallel(b *testing.B) {
s1 := []int{0, 1, 3, 6, 7}
s2 := []int{0, 1, 3}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
appendMerge(s1, s2)
}
})
}
func BenchmarkReverse(b *testing.B) {
for i := 0; i < b.N; i++ {
reverseSegments([]int{1, 2, 3, 4})
}
}
func getTable() []int {
table := make([]int, utf8.MaxRune+1)
for i := 0; i <= utf8.MaxRune; i++ {
table[i] = utf8.RuneLen(rune(i))
}
return table
}
var table = getTable()
const runeToLen = 'q'
func BenchmarkRuneLenFromTable(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = table[runeToLen]
}
}
func BenchmarkRuneLenFromUTF8(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = utf8.RuneLen(runeToLen)
}
}

49
vendor/github.com/gobwas/glob/match/max.go generated vendored Normal file
View File

@ -0,0 +1,49 @@
package match
import (
"fmt"
"unicode/utf8"
)
type Max struct {
Limit int
}
func NewMax(l int) Max {
return Max{l}
}
func (self Max) Match(s string) bool {
var l int
for range s {
l += 1
if l > self.Limit {
return false
}
}
return true
}
func (self Max) Index(s string) (int, []int) {
segments := acquireSegments(self.Limit + 1)
segments = append(segments, 0)
var count int
for i, r := range s {
count++
if count > self.Limit {
break
}
segments = append(segments, i+utf8.RuneLen(r))
}
return 0, segments
}
func (self Max) Len() int {
return lenNo
}
func (self Max) String() string {
return fmt.Sprintf("<max:%d>", self.Limit)
}

57
vendor/github.com/gobwas/glob/match/max_test.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
package match
import (
"reflect"
"testing"
)
func TestMaxIndex(t *testing.T) {
for id, test := range []struct {
limit int
fixture string
index int
segments []int
}{
{
3,
"abc",
0,
[]int{0, 1, 2, 3},
},
{
3,
"abcdef",
0,
[]int{0, 1, 2, 3},
},
} {
p := NewMax(test.limit)
index, segments := p.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkIndexMax(b *testing.B) {
m := NewMax(10)
for i := 0; i < b.N; i++ {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
}
func BenchmarkIndexMaxParallel(b *testing.B) {
m := NewMax(10)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
})
}

57
vendor/github.com/gobwas/glob/match/min.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
package match
import (
"fmt"
"unicode/utf8"
)
type Min struct {
Limit int
}
func NewMin(l int) Min {
return Min{l}
}
func (self Min) Match(s string) bool {
var l int
for range s {
l += 1
if l >= self.Limit {
return true
}
}
return false
}
func (self Min) Index(s string) (int, []int) {
var count int
c := len(s) - self.Limit + 1
if c <= 0 {
return -1, nil
}
segments := acquireSegments(c)
for i, r := range s {
count++
if count >= self.Limit {
segments = append(segments, i+utf8.RuneLen(r))
}
}
if len(segments) == 0 {
return -1, nil
}
return 0, segments
}
func (self Min) Len() int {
return lenNo
}
func (self Min) String() string {
return fmt.Sprintf("<min:%d>", self.Limit)
}

57
vendor/github.com/gobwas/glob/match/min_test.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
package match
import (
"reflect"
"testing"
)
func TestMinIndex(t *testing.T) {
for id, test := range []struct {
limit int
fixture string
index int
segments []int
}{
{
1,
"abc",
0,
[]int{1, 2, 3},
},
{
3,
"abcd",
0,
[]int{3, 4},
},
} {
p := NewMin(test.limit)
index, segments := p.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkIndexMin(b *testing.B) {
m := NewMin(10)
for i := 0; i < b.N; i++ {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
}
func BenchmarkIndexMinParallel(b *testing.B) {
m := NewMin(10)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
})
}

27
vendor/github.com/gobwas/glob/match/nothing.go generated vendored Normal file
View File

@ -0,0 +1,27 @@
package match
import (
"fmt"
)
type Nothing struct{}
func NewNothing() Nothing {
return Nothing{}
}
func (self Nothing) Match(s string) bool {
return len(s) == 0
}
func (self Nothing) Index(s string) (int, []int) {
return 0, segments0
}
func (self Nothing) Len() int {
return lenZero
}
func (self Nothing) String() string {
return fmt.Sprintf("<nothing>")
}

54
vendor/github.com/gobwas/glob/match/nothing_test.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
package match
import (
"reflect"
"testing"
)
func TestNothingIndex(t *testing.T) {
for id, test := range []struct {
fixture string
index int
segments []int
}{
{
"abc",
0,
[]int{0},
},
{
"",
0,
[]int{0},
},
} {
p := NewNothing()
index, segments := p.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkIndexNothing(b *testing.B) {
m := NewNothing()
for i := 0; i < b.N; i++ {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
}
func BenchmarkIndexNothingParallel(b *testing.B) {
m := NewNothing()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
})
}

50
vendor/github.com/gobwas/glob/match/prefix.go generated vendored Normal file
View File

@ -0,0 +1,50 @@
package match
import (
"fmt"
"strings"
"unicode/utf8"
)
type Prefix struct {
Prefix string
}
func NewPrefix(p string) Prefix {
return Prefix{p}
}
func (self Prefix) Index(s string) (int, []int) {
idx := strings.Index(s, self.Prefix)
if idx == -1 {
return -1, nil
}
length := len(self.Prefix)
var sub string
if len(s) > idx+length {
sub = s[idx+length:]
} else {
sub = ""
}
segments := acquireSegments(len(sub) + 1)
segments = append(segments, length)
for i, r := range sub {
segments = append(segments, length+i+utf8.RuneLen(r))
}
return idx, segments
}
func (self Prefix) Len() int {
return lenNo
}
func (self Prefix) Match(s string) bool {
return strings.HasPrefix(s, self.Prefix)
}
func (self Prefix) String() string {
return fmt.Sprintf("<prefix:%s>", self.Prefix)
}

62
vendor/github.com/gobwas/glob/match/prefix_suffix.go generated vendored Normal file
View File

@ -0,0 +1,62 @@
package match
import (
"fmt"
"strings"
)
type PrefixSuffix struct {
Prefix, Suffix string
}
func NewPrefixSuffix(p, s string) PrefixSuffix {
return PrefixSuffix{p, s}
}
func (self PrefixSuffix) Index(s string) (int, []int) {
prefixIdx := strings.Index(s, self.Prefix)
if prefixIdx == -1 {
return -1, nil
}
suffixLen := len(self.Suffix)
if suffixLen <= 0 {
return prefixIdx, []int{len(s) - prefixIdx}
}
if (len(s) - prefixIdx) <= 0 {
return -1, nil
}
segments := acquireSegments(len(s) - prefixIdx)
for sub := s[prefixIdx:]; ; {
suffixIdx := strings.LastIndex(sub, self.Suffix)
if suffixIdx == -1 {
break
}
segments = append(segments, suffixIdx+suffixLen)
sub = sub[:suffixIdx]
}
if len(segments) == 0 {
releaseSegments(segments)
return -1, nil
}
reverseSegments(segments)
return prefixIdx, segments
}
func (self PrefixSuffix) Len() int {
return lenNo
}
func (self PrefixSuffix) Match(s string) bool {
return strings.HasPrefix(s, self.Prefix) && strings.HasSuffix(s, self.Suffix)
}
func (self PrefixSuffix) String() string {
return fmt.Sprintf("<prefix_suffix:[%s,%s]>", self.Prefix, self.Suffix)
}

View File

@ -0,0 +1,67 @@
package match
import (
"reflect"
"testing"
)
func TestPrefixSuffixIndex(t *testing.T) {
for id, test := range []struct {
prefix string
suffix string
fixture string
index int
segments []int
}{
{
"a",
"c",
"abc",
0,
[]int{3},
},
{
"f",
"f",
"fffabfff",
0,
[]int{1, 2, 3, 6, 7, 8},
},
{
"ab",
"bc",
"abc",
0,
[]int{3},
},
} {
p := NewPrefixSuffix(test.prefix, test.suffix)
index, segments := p.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkIndexPrefixSuffix(b *testing.B) {
m := NewPrefixSuffix("qew", "sqw")
for i := 0; i < b.N; i++ {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
}
func BenchmarkIndexPrefixSuffixParallel(b *testing.B) {
m := NewPrefixSuffix("qew", "sqw")
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
})
}

57
vendor/github.com/gobwas/glob/match/prefix_test.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
package match
import (
"reflect"
"testing"
)
func TestPrefixIndex(t *testing.T) {
for id, test := range []struct {
prefix string
fixture string
index int
segments []int
}{
{
"ab",
"abc",
0,
[]int{2, 3},
},
{
"ab",
"fffabfff",
3,
[]int{2, 3, 4, 5},
},
} {
p := NewPrefix(test.prefix)
index, segments := p.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkIndexPrefix(b *testing.B) {
m := NewPrefix("qew")
for i := 0; i < b.N; i++ {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
}
func BenchmarkIndexPrefixParallel(b *testing.B) {
m := NewPrefix("qew")
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
})
}

48
vendor/github.com/gobwas/glob/match/range.go generated vendored Normal file
View File

@ -0,0 +1,48 @@
package match
import (
"fmt"
"unicode/utf8"
)
type Range struct {
Lo, Hi rune
Not bool
}
func NewRange(lo, hi rune, not bool) Range {
return Range{lo, hi, not}
}
func (self Range) Len() int {
return lenOne
}
func (self Range) Match(s string) bool {
r, w := utf8.DecodeRuneInString(s)
if len(s) > w {
return false
}
inRange := r >= self.Lo && r <= self.Hi
return inRange == !self.Not
}
func (self Range) Index(s string) (int, []int) {
for i, r := range s {
if self.Not != (r >= self.Lo && r <= self.Hi) {
return i, segmentsByRuneLength[utf8.RuneLen(r)]
}
}
return -1, nil
}
func (self Range) String() string {
var not string
if self.Not {
not = "!"
}
return fmt.Sprintf("<range:%s[%s,%s]>", not, string(self.Lo), string(self.Hi))
}

67
vendor/github.com/gobwas/glob/match/range_test.go generated vendored Normal file
View File

@ -0,0 +1,67 @@
package match
import (
"reflect"
"testing"
)
func TestRangeIndex(t *testing.T) {
for id, test := range []struct {
lo, hi rune
not bool
fixture string
index int
segments []int
}{
{
'a', 'z',
false,
"abc",
0,
[]int{1},
},
{
'a', 'c',
false,
"abcd",
0,
[]int{1},
},
{
'a', 'c',
true,
"abcd",
3,
[]int{1},
},
} {
m := NewRange(test.lo, test.hi, test.not)
index, segments := m.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkIndexRange(b *testing.B) {
m := NewRange('0', '9', false)
for i := 0; i < b.N; i++ {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
}
func BenchmarkIndexRangeParallel(b *testing.B) {
m := NewRange('0', '9', false)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
})
}

85
vendor/github.com/gobwas/glob/match/row.go generated vendored Normal file
View File

@ -0,0 +1,85 @@
package match
import (
"fmt"
)
type Row struct {
Matchers Matchers
RunesLength int
Segments []int
}
func NewRow(len int, m ...Matcher) Row {
return Row{
Matchers: Matchers(m),
RunesLength: len,
Segments: []int{len},
}
}
func (self Row) matchAll(s string) bool {
var idx int
for _, m := range self.Matchers {
length := m.Len()
var next, i int
for next = range s[idx:] {
i++
if i == length {
break
}
}
if i < length || !m.Match(s[idx:idx+next+1]) {
return false
}
idx += next + 1
}
return true
}
func (self Row) lenOk(s string) bool {
var i int
for range s {
i++
if i >= self.RunesLength {
return true
}
}
return false
}
func (self Row) Match(s string) bool {
return self.lenOk(s) && self.matchAll(s)
}
func (self Row) Len() (l int) {
return self.RunesLength
}
func (self Row) Index(s string) (int, []int) {
if !self.lenOk(s) {
return -1, nil
}
for i := range s {
// this is not strict check but useful
if len(s[i:]) < self.RunesLength {
break
}
if self.matchAll(s[i:]) {
return i, self.Segments
}
}
return -1, nil
}
func (self Row) String() string {
return fmt.Sprintf("<row_%d:[%s]>", self.RunesLength, self.Matchers)
}

82
vendor/github.com/gobwas/glob/match/row_test.go generated vendored Normal file
View File

@ -0,0 +1,82 @@
package match
import (
"reflect"
"testing"
)
func TestRowIndex(t *testing.T) {
for id, test := range []struct {
matchers Matchers
length int
fixture string
index int
segments []int
}{
{
Matchers{
NewText("abc"),
NewText("def"),
NewSingle(nil),
},
7,
"qweabcdefghij",
3,
[]int{7},
},
{
Matchers{
NewText("abc"),
NewText("def"),
NewSingle(nil),
},
7,
"abcd",
-1,
nil,
},
} {
p := NewRow(test.length, test.matchers...)
index, segments := p.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkRowIndex(b *testing.B) {
m := NewRow(
7,
Matchers{
NewText("abc"),
NewText("def"),
NewSingle(nil),
}...,
)
for i := 0; i < b.N; i++ {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
}
func BenchmarkIndexRowParallel(b *testing.B) {
m := NewRow(
7,
Matchers{
NewText("abc"),
NewText("def"),
NewSingle(nil),
}...,
)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
})
}

91
vendor/github.com/gobwas/glob/match/segments.go generated vendored Normal file
View File

@ -0,0 +1,91 @@
package match
import (
"sync"
)
type SomePool interface {
Get() []int
Put([]int)
}
var segmentsPools [1024]sync.Pool
func toPowerOfTwo(v int) int {
v--
v |= v >> 1
v |= v >> 2
v |= v >> 4
v |= v >> 8
v |= v >> 16
v++
return v
}
const (
cacheFrom = 16
cacheToAndHigher = 1024
cacheFromIndex = 15
cacheToAndHigherIndex = 1023
)
var (
segments0 = []int{0}
segments1 = []int{1}
segments2 = []int{2}
segments3 = []int{3}
segments4 = []int{4}
)
var segmentsByRuneLength [5][]int = [5][]int{
0: segments0,
1: segments1,
2: segments2,
3: segments3,
4: segments4,
}
func init() {
for i := cacheToAndHigher; i >= cacheFrom; i >>= 1 {
func(i int) {
segmentsPools[i-1] = sync.Pool{New: func() interface{} {
return make([]int, 0, i)
}}
}(i)
}
}
func getTableIndex(c int) int {
p := toPowerOfTwo(c)
switch {
case p >= cacheToAndHigher:
return cacheToAndHigherIndex
case p <= cacheFrom:
return cacheFromIndex
default:
return p - 1
}
}
func acquireSegments(c int) []int {
// make []int with less capacity than cacheFrom
// is faster than acquiring it from pool
if c < cacheFrom {
return make([]int, 0, c)
}
return segmentsPools[getTableIndex(c)].Get().([]int)[:0]
}
func releaseSegments(s []int) {
c := cap(s)
// make []int with less capacity than cacheFrom
// is faster than acquiring it from pool
if c < cacheFrom {
return
}
segmentsPools[getTableIndex(c)].Put(s)
}

83
vendor/github.com/gobwas/glob/match/segments_test.go generated vendored Normal file
View File

@ -0,0 +1,83 @@
package match
import (
"sync"
"testing"
)
func benchPool(i int, b *testing.B) {
pool := sync.Pool{New: func() interface{} {
return make([]int, 0, i)
}}
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
s := pool.Get().([]int)[:0]
pool.Put(s)
}
})
}
func benchMake(i int, b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = make([]int, 0, i)
}
})
}
func BenchmarkSegmentsPool_1(b *testing.B) {
benchPool(1, b)
}
func BenchmarkSegmentsPool_2(b *testing.B) {
benchPool(2, b)
}
func BenchmarkSegmentsPool_4(b *testing.B) {
benchPool(4, b)
}
func BenchmarkSegmentsPool_8(b *testing.B) {
benchPool(8, b)
}
func BenchmarkSegmentsPool_16(b *testing.B) {
benchPool(16, b)
}
func BenchmarkSegmentsPool_32(b *testing.B) {
benchPool(32, b)
}
func BenchmarkSegmentsPool_64(b *testing.B) {
benchPool(64, b)
}
func BenchmarkSegmentsPool_128(b *testing.B) {
benchPool(128, b)
}
func BenchmarkSegmentsPool_256(b *testing.B) {
benchPool(256, b)
}
func BenchmarkSegmentsMake_1(b *testing.B) {
benchMake(1, b)
}
func BenchmarkSegmentsMake_2(b *testing.B) {
benchMake(2, b)
}
func BenchmarkSegmentsMake_4(b *testing.B) {
benchMake(4, b)
}
func BenchmarkSegmentsMake_8(b *testing.B) {
benchMake(8, b)
}
func BenchmarkSegmentsMake_16(b *testing.B) {
benchMake(16, b)
}
func BenchmarkSegmentsMake_32(b *testing.B) {
benchMake(32, b)
}
func BenchmarkSegmentsMake_64(b *testing.B) {
benchMake(64, b)
}
func BenchmarkSegmentsMake_128(b *testing.B) {
benchMake(128, b)
}
func BenchmarkSegmentsMake_256(b *testing.B) {
benchMake(256, b)
}

43
vendor/github.com/gobwas/glob/match/single.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
package match
import (
"fmt"
"github.com/gobwas/glob/runes"
"unicode/utf8"
)
// single represents ?
type Single struct {
Separators []rune
}
func NewSingle(s []rune) Single {
return Single{s}
}
func (self Single) Match(s string) bool {
r, w := utf8.DecodeRuneInString(s)
if len(s) > w {
return false
}
return runes.IndexRune(self.Separators, r) == -1
}
func (self Single) Len() int {
return lenOne
}
func (self Single) Index(s string) (int, []int) {
for i, r := range s {
if runes.IndexRune(self.Separators, r) == -1 {
return i, segmentsByRuneLength[utf8.RuneLen(r)]
}
}
return -1, nil
}
func (self Single) String() string {
return fmt.Sprintf("<single:![%s]>", string(self.Separators))
}

57
vendor/github.com/gobwas/glob/match/single_test.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
package match
import (
"reflect"
"testing"
)
func TestSingleIndex(t *testing.T) {
for id, test := range []struct {
separators []rune
fixture string
index int
segments []int
}{
{
[]rune{'.'},
".abc",
1,
[]int{1},
},
{
[]rune{'.'},
".",
-1,
nil,
},
} {
p := NewSingle(test.separators)
index, segments := p.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkIndexSingle(b *testing.B) {
m := NewSingle(bench_separators)
for i := 0; i < b.N; i++ {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
}
func BenchmarkIndexSingleParallel(b *testing.B) {
m := NewSingle(bench_separators)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
})
}

35
vendor/github.com/gobwas/glob/match/suffix.go generated vendored Normal file
View File

@ -0,0 +1,35 @@
package match
import (
"fmt"
"strings"
)
type Suffix struct {
Suffix string
}
func NewSuffix(s string) Suffix {
return Suffix{s}
}
func (self Suffix) Len() int {
return lenNo
}
func (self Suffix) Match(s string) bool {
return strings.HasSuffix(s, self.Suffix)
}
func (self Suffix) Index(s string) (int, []int) {
idx := strings.Index(s, self.Suffix)
if idx == -1 {
return -1, nil
}
return 0, []int{idx + len(self.Suffix)}
}
func (self Suffix) String() string {
return fmt.Sprintf("<suffix:%s>", self.Suffix)
}

57
vendor/github.com/gobwas/glob/match/suffix_test.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
package match
import (
"reflect"
"testing"
)
func TestSuffixIndex(t *testing.T) {
for id, test := range []struct {
prefix string
fixture string
index int
segments []int
}{
{
"ab",
"abc",
0,
[]int{2},
},
{
"ab",
"fffabfff",
0,
[]int{5},
},
} {
p := NewSuffix(test.prefix)
index, segments := p.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkIndexSuffix(b *testing.B) {
m := NewSuffix("qwe")
for i := 0; i < b.N; i++ {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
}
func BenchmarkIndexSuffixParallel(b *testing.B) {
m := NewSuffix("qwe")
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
})
}

33
vendor/github.com/gobwas/glob/match/super.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
package match
import (
"fmt"
)
type Super struct{}
func NewSuper() Super {
return Super{}
}
func (self Super) Match(s string) bool {
return true
}
func (self Super) Len() int {
return lenNo
}
func (self Super) Index(s string) (int, []int) {
segments := acquireSegments(len(s) + 1)
for i := range s {
segments = append(segments, i)
}
segments = append(segments, len(s))
return 0, segments
}
func (self Super) String() string {
return fmt.Sprintf("<super>")
}

54
vendor/github.com/gobwas/glob/match/super_test.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
package match
import (
"reflect"
"testing"
)
func TestSuperIndex(t *testing.T) {
for id, test := range []struct {
fixture string
index int
segments []int
}{
{
"abc",
0,
[]int{0, 1, 2, 3},
},
{
"",
0,
[]int{0},
},
} {
p := NewSuper()
index, segments := p.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkIndexSuper(b *testing.B) {
m := NewSuper()
for i := 0; i < b.N; i++ {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
}
func BenchmarkIndexSuperParallel(b *testing.B) {
m := NewSuper()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
})
}

45
vendor/github.com/gobwas/glob/match/text.go generated vendored Normal file
View File

@ -0,0 +1,45 @@
package match
import (
"fmt"
"strings"
"unicode/utf8"
)
// raw represents raw string to match
type Text struct {
Str string
RunesLength int
BytesLength int
Segments []int
}
func NewText(s string) Text {
return Text{
Str: s,
RunesLength: utf8.RuneCountInString(s),
BytesLength: len(s),
Segments: []int{len(s)},
}
}
func (self Text) Match(s string) bool {
return self.Str == s
}
func (self Text) Len() int {
return self.RunesLength
}
func (self Text) Index(s string) (int, []int) {
index := strings.Index(s, self.Str)
if index == -1 {
return -1, nil
}
return index, self.Segments
}
func (self Text) String() string {
return fmt.Sprintf("<text:`%v`>", self.Str)
}

57
vendor/github.com/gobwas/glob/match/text_test.go generated vendored Normal file
View File

@ -0,0 +1,57 @@
package match
import (
"reflect"
"testing"
)
func TestTextIndex(t *testing.T) {
for id, test := range []struct {
text string
fixture string
index int
segments []int
}{
{
"b",
"abc",
1,
[]int{1},
},
{
"f",
"abcd",
-1,
nil,
},
} {
m := NewText(test.text)
index, segments := m.Index(test.fixture)
if index != test.index {
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
}
if !reflect.DeepEqual(segments, test.segments) {
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
}
}
}
func BenchmarkIndexText(b *testing.B) {
m := NewText("foo")
for i := 0; i < b.N; i++ {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
}
func BenchmarkIndexTextParallel(b *testing.B) {
m := NewText("foo")
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, s := m.Index(bench_pattern)
releaseSegments(s)
}
})
}

225
vendor/github.com/gobwas/glob/parser.go generated vendored Normal file
View File

@ -0,0 +1,225 @@
package glob
import (
"errors"
"fmt"
"unicode/utf8"
)
type node interface {
children() []node
append(node)
}
type nodeImpl struct {
desc []node
}
func (n *nodeImpl) append(c node) {
n.desc = append(n.desc, c)
}
func (n *nodeImpl) children() []node {
return n.desc
}
type nodeList struct {
nodeImpl
not bool
chars string
}
type nodeRange struct {
nodeImpl
not bool
lo, hi rune
}
type nodeText struct {
nodeImpl
text string
}
type nodePattern struct{ nodeImpl }
type nodeAny struct{ nodeImpl }
type nodeSuper struct{ nodeImpl }
type nodeSingle struct{ nodeImpl }
type nodeAnyOf struct{ nodeImpl }
type tree struct {
root node
current node
path []node
}
func (t *tree) enter(c node) {
if t.root == nil {
t.root = c
t.current = c
return
}
t.current.append(c)
t.path = append(t.path, c)
t.current = c
}
func (t *tree) leave() {
if len(t.path)-1 <= 0 {
t.current = t.root
t.path = nil
return
}
t.path = t.path[:len(t.path)-1]
t.current = t.path[len(t.path)-1]
}
type parseFn func(*tree, *lexer) (parseFn, error)
func parse(lexer *lexer) (*nodePattern, error) {
var parser parseFn
root := &nodePattern{}
tree := &tree{}
tree.enter(root)
for parser = parserMain; ; {
next, err := parser(tree, lexer)
if err != nil {
return nil, err
}
if next == nil {
break
}
parser = next
}
return root, nil
}
func parserMain(tree *tree, lexer *lexer) (parseFn, error) {
for stop := false; !stop; {
item := lexer.nextItem()
switch item.t {
case item_eof:
stop = true
continue
case item_error:
return nil, errors.New(item.s)
case item_text:
tree.current.append(&nodeText{text: item.s})
return parserMain, nil
case item_any:
tree.current.append(&nodeAny{})
return parserMain, nil
case item_super:
tree.current.append(&nodeSuper{})
return parserMain, nil
case item_single:
tree.current.append(&nodeSingle{})
return parserMain, nil
case item_range_open:
return parserRange, nil
case item_terms_open:
tree.enter(&nodeAnyOf{})
tree.enter(&nodePattern{})
return parserMain, nil
case item_separator:
tree.leave()
tree.enter(&nodePattern{})
return parserMain, nil
case item_terms_close:
tree.leave()
tree.leave()
return parserMain, nil
default:
return nil, fmt.Errorf("unexpected token: %s", item)
}
}
return nil, nil
}
func parserRange(tree *tree, lexer *lexer) (parseFn, error) {
var (
not bool
lo rune
hi rune
chars string
)
for {
item := lexer.nextItem()
switch item.t {
case item_eof:
return nil, errors.New("unexpected end")
case item_error:
return nil, errors.New(item.s)
case item_not:
not = true
case item_range_lo:
r, w := utf8.DecodeRuneInString(item.s)
if len(item.s) > w {
return nil, fmt.Errorf("unexpected length of lo character")
}
lo = r
case item_range_between:
//
case item_range_hi:
r, w := utf8.DecodeRuneInString(item.s)
if len(item.s) > w {
return nil, fmt.Errorf("unexpected length of lo character")
}
hi = r
if hi < lo {
return nil, fmt.Errorf("hi character '%s' should be greater than lo '%s'", string(hi), string(lo))
}
case item_text:
chars = item.s
case item_range_close:
isRange := lo != 0 && hi != 0
isChars := chars != ""
if isChars == isRange {
return nil, fmt.Errorf("could not parse range")
}
if isRange {
tree.current.append(&nodeRange{
lo: lo,
hi: hi,
not: not,
})
} else {
tree.current.append(&nodeList{
chars: chars,
not: not,
})
}
return parserMain, nil
}
}
}

219
vendor/github.com/gobwas/glob/parser_test.go generated vendored Normal file
View File

@ -0,0 +1,219 @@
package glob
import (
"fmt"
"reflect"
"testing"
)
func TestParseString(t *testing.T) {
for id, test := range []struct {
pattern string
tree node
}{
{
pattern: "abc",
tree: &nodePattern{
nodeImpl: nodeImpl{
desc: []node{
&nodeText{text: "abc"},
},
},
},
},
{
pattern: "a*c",
tree: &nodePattern{
nodeImpl: nodeImpl{
desc: []node{
&nodeText{text: "a"},
&nodeAny{},
&nodeText{text: "c"},
},
},
},
},
{
pattern: "a**c",
tree: &nodePattern{
nodeImpl: nodeImpl{
desc: []node{
&nodeText{text: "a"},
&nodeSuper{},
&nodeText{text: "c"},
},
},
},
},
{
pattern: "a?c",
tree: &nodePattern{
nodeImpl: nodeImpl{
desc: []node{
&nodeText{text: "a"},
&nodeSingle{},
&nodeText{text: "c"},
},
},
},
},
{
pattern: "[!a-z]",
tree: &nodePattern{
nodeImpl: nodeImpl{
desc: []node{
&nodeRange{lo: 'a', hi: 'z', not: true},
},
},
},
},
{
pattern: "[az]",
tree: &nodePattern{
nodeImpl: nodeImpl{
desc: []node{
&nodeList{chars: "az"},
},
},
},
},
{
pattern: "{a,z}",
tree: &nodePattern{
nodeImpl: nodeImpl{
desc: []node{
&nodeAnyOf{nodeImpl: nodeImpl{desc: []node{
&nodePattern{
nodeImpl: nodeImpl{desc: []node{
&nodeText{text: "a"},
}},
},
&nodePattern{
nodeImpl: nodeImpl{desc: []node{
&nodeText{text: "z"},
}},
},
}}},
},
},
},
},
{
pattern: "{a,{x,y},?,[a-z],[!qwe]}",
tree: &nodePattern{
nodeImpl: nodeImpl{
desc: []node{
&nodeAnyOf{nodeImpl: nodeImpl{desc: []node{
&nodePattern{
nodeImpl: nodeImpl{desc: []node{
&nodeText{text: "a"},
}},
},
&nodePattern{
nodeImpl: nodeImpl{desc: []node{
&nodeAnyOf{nodeImpl: nodeImpl{desc: []node{
&nodePattern{
nodeImpl: nodeImpl{desc: []node{
&nodeText{text: "x"},
}},
},
&nodePattern{
nodeImpl: nodeImpl{desc: []node{
&nodeText{text: "y"},
}},
},
}}},
}},
},
&nodePattern{
nodeImpl: nodeImpl{desc: []node{
&nodeSingle{},
}},
},
&nodePattern{
nodeImpl: nodeImpl{
desc: []node{
&nodeRange{lo: 'a', hi: 'z', not: false},
},
},
},
&nodePattern{
nodeImpl: nodeImpl{
desc: []node{
&nodeList{chars: "qwe", not: true},
},
},
},
}}},
},
},
},
},
} {
pattern, err := parse(newLexer(test.pattern))
if err != nil {
t.Errorf("#%d %s", id, err)
continue
}
if !reflect.DeepEqual(test.tree, pattern) {
t.Errorf("#%d tries are not equal", id)
if err = nodeEqual(test.tree, pattern); err != nil {
t.Errorf("#%d %s", id, err)
continue
}
}
}
}
const abstractNodeImpl = "nodeImpl"
func nodeEqual(a, b node) error {
if (a == nil || b == nil) && a != b {
return fmt.Errorf("nodes are not equal: exp %s, act %s", a, b)
}
aValue, bValue := reflect.Indirect(reflect.ValueOf(a)), reflect.Indirect(reflect.ValueOf(b))
aType, bType := aValue.Type(), bValue.Type()
if aType != bType {
return fmt.Errorf("nodes are not equal: exp %s, act %s", aValue.Type(), bValue.Type())
}
for i := 0; i < aType.NumField(); i++ {
var eq bool
f := aType.Field(i).Name
if f == abstractNodeImpl {
continue
}
af, bf := aValue.FieldByName(f), bValue.FieldByName(f)
switch af.Kind() {
case reflect.String:
eq = af.String() == bf.String()
case reflect.Bool:
eq = af.Bool() == bf.Bool()
default:
eq = fmt.Sprint(af) == fmt.Sprint(bf)
}
if !eq {
return fmt.Errorf("nodes<%s> %q fields are not equal: exp %q, act %q", aType, f, af, bf)
}
}
for i, aDesc := range a.children() {
if len(b.children())-1 < i {
return fmt.Errorf("node does not have enough children (got %d children, wanted %d-th token)", len(b.children()), i)
}
bDesc := b.children()[i]
if err := nodeEqual(aDesc, bDesc); err != nil {
return err
}
}
return nil
}

148
vendor/github.com/gobwas/glob/readme.md generated vendored Normal file
View File

@ -0,0 +1,148 @@
# glob.[go](https://golang.org)
[![GoDoc][godoc-image]][godoc-url] [![Build Status][travis-image]][travis-url]
> Go Globbing Library.
## Install
```shell
go get github.com/gobwas/glob
```
## Example
```go
package main
import "github.com/gobwas/glob"
func main() {
var g glob.Glob
// create simple glob
g = glob.MustCompile("*.github.com")
g.Match("api.github.com") // true
// quote meta characters and then create simple glob
g = glob.MustCompile(glob.QuoteMeta("*.github.com"))
g.Match("*.github.com") // true
// create new glob with set of delimiters as ["."]
g = glob.MustCompile("api.*.com", '.')
g.Match("api.github.com") // true
g.Match("api.gi.hub.com") // false
// create new glob with set of delimiters as ["."]
// but now with super wildcard
g = glob.MustCompile("api.**.com", '.')
g.Match("api.github.com") // true
g.Match("api.gi.hub.com") // true
// create glob with single symbol wildcard
g = glob.MustCompile("?at")
g.Match("cat") // true
g.Match("fat") // true
g.Match("at") // false
// create glob with single symbol wildcard and delimiters ['f']
g = glob.MustCompile("?at", 'f')
g.Match("cat") // true
g.Match("fat") // false
g.Match("at") // false
// create glob with character-list matchers
g = glob.MustCompile("[abc]at")
g.Match("cat") // true
g.Match("bat") // true
g.Match("fat") // false
g.Match("at") // false
// create glob with character-list matchers
g = glob.MustCompile("[!abc]at")
g.Match("cat") // false
g.Match("bat") // false
g.Match("fat") // true
g.Match("at") // false
// create glob with character-range matchers
g = glob.MustCompile("[a-c]at")
g.Match("cat") // true
g.Match("bat") // true
g.Match("fat") // false
g.Match("at") // false
// create glob with character-range matchers
g = glob.MustCompile("[!a-c]at")
g.Match("cat") // false
g.Match("bat") // false
g.Match("fat") // true
g.Match("at") // false
// create glob with pattern-alternatives list
g = glob.MustCompile("{cat,bat,[fr]at}")
g.Match("cat") // true
g.Match("bat") // true
g.Match("fat") // true
g.Match("rat") // true
g.Match("at") // false
g.Match("zat") // false
}
```
## Performance
This library is created for compile-once patterns. This means, that compilation could take time, but
strings matching is done faster, than in case when always parsing template.
If you will not use compiled `glob.Glob` object, and do `g := glob.MustCompile(pattern); g.Match(...)` every time, then your code will be much more slower.
Run `go test -bench=.` from source root to see the benchmarks:
Pattern | Fixture | Match | Speed (ns/op)
--------|---------|-------|--------------
`[a-z][!a-x]*cat*[h][!b]*eyes*` | `my cat has very bright eyes` | `true` | 432
`[a-z][!a-x]*cat*[h][!b]*eyes*` | `my dog has very bright eyes` | `false` | 199
`https://*.google.*` | `https://account.google.com` | `true` | 96
`https://*.google.*` | `https://google.com` | `false` | 66
`{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}` | `http://yahoo.com` | `true` | 163
`{https://*.google.*,*yandex.*,*yahoo.*,*mail.ru}` | `http://google.com` | `false` | 197
`{https://*gobwas.com,http://exclude.gobwas.com}` | `https://safe.gobwas.com` | `true` | 22
`{https://*gobwas.com,http://exclude.gobwas.com}` | `http://safe.gobwas.com` | `false` | 24
`abc*` | `abcdef` | `true` | 8.15
`abc*` | `af` | `false` | 5.68
`*def` | `abcdef` | `true` | 8.84
`*def` | `af` | `false` | 5.74
`ab*ef` | `abcdef` | `true` | 15.2
`ab*ef` | `af` | `false` | 10.4
The same things with `regexp` package:
Pattern | Fixture | Match | Speed (ns/op)
--------|---------|-------|--------------
`^[a-z][^a-x].*cat.*[h][^b].*eyes.*$` | `my cat has very bright eyes` | `true` | 2553
`^[a-z][^a-x].*cat.*[h][^b].*eyes.*$` | `my dog has very bright eyes` | `false` | 1383
`^https:\/\/.*\.google\..*$` | `https://account.google.com` | `true` | 1205
`^https:\/\/.*\.google\..*$` | `https://google.com` | `false` | 767
`^(https:\/\/.*\.google\..*|.*yandex\..*|.*yahoo\..*|.*mail\.ru)$` | `http://yahoo.com` | `true` | 1435
`^(https:\/\/.*\.google\..*|.*yandex\..*|.*yahoo\..*|.*mail\.ru)$` | `http://google.com` | `false` | 1674
`^(https:\/\/.*gobwas\.com|http://exclude.gobwas.com)$` | `https://safe.gobwas.com` | `true` | 1039
`^(https:\/\/.*gobwas\.com|http://exclude.gobwas.com)$` | `http://safe.gobwas.com` | `false` | 272
`^abc.*$` | `abcdef` | `true` | 237
`^abc.*$` | `af` | `false` | 100
`^.*def$` | `abcdef` | `true` | 464
`^.*def$` | `af` | `false` | 265
`^ab.*ef$` | `abcdef` | `true` | 375
`^ab.*ef$` | `af` | `false` | 145
[godoc-image]: https://godoc.org/github.com/gobwas/glob?status.svg
[godoc-url]: https://godoc.org/github.com/gobwas/glob
[travis-image]: https://travis-ci.org/gobwas/glob.svg?branch=master
[travis-url]: https://travis-ci.org/gobwas/glob
## Syntax
Syntax is inspired by [standard wildcards](http://tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm),
except that `**` is aka super-asterisk, that do not sensitive for separators.

154
vendor/github.com/gobwas/glob/runes/runes.go generated vendored Normal file
View File

@ -0,0 +1,154 @@
package runes
func Index(s, needle []rune) int {
ls, ln := len(s), len(needle)
switch {
case ln == 0:
return 0
case ln == 1:
return IndexRune(s, needle[0])
case ln == ls:
if Equal(s, needle) {
return 0
}
return -1
case ln > ls:
return -1
}
head:
for i := 0; i < ls && ls-i >= ln; i++ {
for y := 0; y < ln; y++ {
if s[i+y] != needle[y] {
continue head
}
}
return i
}
return -1
}
func LastIndex(s, needle []rune) int {
ls, ln := len(s), len(needle)
switch {
case ln == 0:
if ls == 0 {
return 0
}
return ls
case ln == 1:
return IndexLastRune(s, needle[0])
case ln == ls:
if Equal(s, needle) {
return 0
}
return -1
case ln > ls:
return -1
}
head:
for i := ls - 1; i >= 0 && i >= ln; i-- {
for y := ln - 1; y >= 0; y-- {
if s[i-(ln-y-1)] != needle[y] {
continue head
}
}
return i - ln + 1
}
return -1
}
// IndexAny returns the index of the first instance of any Unicode code point
// from chars in s, or -1 if no Unicode code point from chars is present in s.
func IndexAny(s, chars []rune) int {
if len(chars) > 0 {
for i, c := range s {
for _, m := range chars {
if c == m {
return i
}
}
}
}
return -1
}
func Contains(s, needle []rune) bool {
return Index(s, needle) >= 0
}
func Max(s []rune) (max rune) {
for _, r := range s {
if r > max {
max = r
}
}
return
}
func Min(s []rune) rune {
min := rune(-1)
for _, r := range s {
if min == -1 {
min = r
continue
}
if r < min {
min = r
}
}
return min
}
func IndexRune(s []rune, r rune) int {
for i, c := range s {
if c == r {
return i
}
}
return -1
}
func IndexLastRune(s []rune, r rune) int {
for i := len(s) - 1; i >= 0; i-- {
if s[i] == r {
return i
}
}
return -1
}
func Equal(a, b []rune) bool {
if len(a) == len(b) {
for i := 0; i < len(a); i++ {
if a[i] != b[i] {
return false
}
}
return true
}
return false
}
// HasPrefix tests whether the string s begins with prefix.
func HasPrefix(s, prefix []rune) bool {
return len(s) >= len(prefix) && Equal(s[0:len(prefix)], prefix)
}
// HasSuffix tests whether the string s ends with suffix.
func HasSuffix(s, suffix []rune) bool {
return len(s) >= len(suffix) && Equal(s[len(s)-len(suffix):], suffix)
}

222
vendor/github.com/gobwas/glob/runes/runes_test.go generated vendored Normal file
View File

@ -0,0 +1,222 @@
package runes
import (
"strings"
"testing"
)
type indexTest struct {
s []rune
sep []rune
out int
}
type equalTest struct {
a []rune
b []rune
out bool
}
func newIndexTest(s, sep string, out int) indexTest {
return indexTest{[]rune(s), []rune(sep), out}
}
func newEqualTest(s, sep string, out bool) equalTest {
return equalTest{[]rune(s), []rune(sep), out}
}
var dots = "1....2....3....4"
var indexTests = []indexTest{
newIndexTest("", "", 0),
newIndexTest("", "a", -1),
newIndexTest("", "foo", -1),
newIndexTest("fo", "foo", -1),
newIndexTest("foo", "foo", 0),
newIndexTest("oofofoofooo", "f", 2),
newIndexTest("oofofoofooo", "foo", 4),
newIndexTest("barfoobarfoo", "foo", 3),
newIndexTest("foo", "", 0),
newIndexTest("foo", "o", 1),
newIndexTest("abcABCabc", "A", 3),
// cases with one byte strings - test special case in Index()
newIndexTest("", "a", -1),
newIndexTest("x", "a", -1),
newIndexTest("x", "x", 0),
newIndexTest("abc", "a", 0),
newIndexTest("abc", "b", 1),
newIndexTest("abc", "c", 2),
newIndexTest("abc", "x", -1),
}
var lastIndexTests = []indexTest{
newIndexTest("", "", 0),
newIndexTest("", "a", -1),
newIndexTest("", "foo", -1),
newIndexTest("fo", "foo", -1),
newIndexTest("foo", "foo", 0),
newIndexTest("foo", "f", 0),
newIndexTest("oofofoofooo", "f", 7),
newIndexTest("oofofoofooo", "foo", 7),
newIndexTest("barfoobarfoo", "foo", 9),
newIndexTest("foo", "", 3),
newIndexTest("foo", "o", 2),
newIndexTest("abcABCabc", "A", 3),
newIndexTest("abcABCabc", "a", 6),
}
var indexAnyTests = []indexTest{
newIndexTest("", "", -1),
newIndexTest("", "a", -1),
newIndexTest("", "abc", -1),
newIndexTest("a", "", -1),
newIndexTest("a", "a", 0),
newIndexTest("aaa", "a", 0),
newIndexTest("abc", "xyz", -1),
newIndexTest("abc", "xcz", 2),
newIndexTest("a☺b☻c☹d", "uvw☻xyz", 3),
newIndexTest("aRegExp*", ".(|)*+?^$[]", 7),
newIndexTest(dots+dots+dots, " ", -1),
}
// Execute f on each test case. funcName should be the name of f; it's used
// in failure reports.
func runIndexTests(t *testing.T, f func(s, sep []rune) int, funcName string, testCases []indexTest) {
for _, test := range testCases {
actual := f(test.s, test.sep)
if actual != test.out {
t.Errorf("%s(%q,%q) = %v; want %v", funcName, test.s, test.sep, actual, test.out)
}
}
}
func TestIndex(t *testing.T) { runIndexTests(t, Index, "Index", indexTests) }
func TestLastIndex(t *testing.T) { runIndexTests(t, LastIndex, "LastIndex", lastIndexTests) }
func TestIndexAny(t *testing.T) { runIndexTests(t, IndexAny, "IndexAny", indexAnyTests) }
var equalTests = []equalTest{
newEqualTest("a", "a", true),
newEqualTest("a", "b", false),
newEqualTest("a☺b☻c☹d", "uvw☻xyz", false),
newEqualTest("a☺b☻c☹d", "a☺b☻c☹d", true),
}
func TestEqual(t *testing.T) {
for _, test := range equalTests {
actual := Equal(test.a, test.b)
if actual != test.out {
t.Errorf("Equal(%q,%q) = %v; want %v", test.a, test.b, actual, test.out)
}
}
}
func BenchmarkLastIndexRunes(b *testing.B) {
r := []rune("abcdef")
n := []rune("cd")
for i := 0; i < b.N; i++ {
LastIndex(r, n)
}
}
func BenchmarkLastIndexStrings(b *testing.B) {
r := "abcdef"
n := "cd"
for i := 0; i < b.N; i++ {
strings.LastIndex(r, n)
}
}
func BenchmarkIndexAnyRunes(b *testing.B) {
s := []rune("...b...")
c := []rune("abc")
for i := 0; i < b.N; i++ {
IndexAny(s, c)
}
}
func BenchmarkIndexAnyStrings(b *testing.B) {
s := "...b..."
c := "abc"
for i := 0; i < b.N; i++ {
strings.IndexAny(s, c)
}
}
func BenchmarkIndexRuneRunes(b *testing.B) {
s := []rune("...b...")
r := 'b'
for i := 0; i < b.N; i++ {
IndexRune(s, r)
}
}
func BenchmarkIndexRuneStrings(b *testing.B) {
s := "...b..."
r := 'b'
for i := 0; i < b.N; i++ {
strings.IndexRune(s, r)
}
}
func BenchmarkIndexRunes(b *testing.B) {
r := []rune("abcdef")
n := []rune("cd")
for i := 0; i < b.N; i++ {
Index(r, n)
}
}
func BenchmarkIndexStrings(b *testing.B) {
r := "abcdef"
n := "cd"
for i := 0; i < b.N; i++ {
strings.Index(r, n)
}
}
func BenchmarkEqualRunes(b *testing.B) {
x := []rune("abc")
y := []rune("abc")
for i := 0; i < b.N; i++ {
if Equal(x, y) {
continue
}
}
}
func BenchmarkEqualStrings(b *testing.B) {
x := "abc"
y := "abc"
for i := 0; i < b.N; i++ {
if x == y {
continue
}
}
}
func BenchmarkNotEqualRunes(b *testing.B) {
x := []rune("abc")
y := []rune("abcd")
for i := 0; i < b.N; i++ {
if Equal(x, y) {
continue
}
}
}
func BenchmarkNotEqualStrings(b *testing.B) {
x := "abc"
y := "abcd"
for i := 0; i < b.N; i++ {
if x == y {
continue
}
}
}

13
vendor/github.com/gobwas/glob/strings/strings.go generated vendored Normal file
View File

@ -0,0 +1,13 @@
package strings
import "strings"
func IndexAnyRunes(s string, rs []rune) int {
for _, r := range rs {
if i := strings.IndexRune(s, r); i != -1 {
return i
}
}
return -1
}

6
vendor/manifest vendored
View File

@ -31,6 +31,12 @@
"revision": "bf29d7cd9038386a5b4a22e2d73c8fb20ae14602", "revision": "bf29d7cd9038386a5b4a22e2d73c8fb20ae14602",
"branch": "master" "branch": "master"
}, },
{
"importpath": "github.com/gobwas/glob",
"repository": "https://github.com/gobwas/glob",
"revision": "a3f5513f64fe4307f2c71ea06b25f6154eb9dc5f",
"branch": "master"
},
{ {
"importpath": "github.com/golang/snappy", "importpath": "github.com/golang/snappy",
"repository": "https://github.com/golang/snappy", "repository": "https://github.com/golang/snappy",