forked from mirror/glob
Initial
This commit is contained in:
commit
d4d579d13e
|
@ -0,0 +1,2 @@
|
|||
glob.iml
|
||||
.idea
|
|
@ -0,0 +1,155 @@
|
|||
package glob
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
Any = "*"
|
||||
SuperAny = "**"
|
||||
SingleAny = "?"
|
||||
)
|
||||
|
||||
var Chars = []string{Any, SuperAny, SingleAny}
|
||||
|
||||
type Matcher interface {
|
||||
Match(string) bool
|
||||
}
|
||||
|
||||
func firstIndexOfChars(p string, any []string) (min int, c string) {
|
||||
l := len(p)
|
||||
min = l
|
||||
weight := 0
|
||||
|
||||
for _, s := range any {
|
||||
w := len(s)
|
||||
i := strings.Index(p, s)
|
||||
if i != -1 && i <= min && w > weight {
|
||||
min = i
|
||||
weight = w
|
||||
c = s
|
||||
}
|
||||
}
|
||||
|
||||
if min == l {
|
||||
return -1, ""
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func parse(p string, m []Matcher, d []string) ([]Matcher, error) {
|
||||
if len(p) == 0 {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
i, c := firstIndexOfChars(p, Chars)
|
||||
if i == -1 {
|
||||
return append(m, raw{p}), nil
|
||||
}
|
||||
|
||||
if i > 0 {
|
||||
m = append(m, raw{p[0:i]})
|
||||
}
|
||||
|
||||
switch c {
|
||||
case SuperAny:
|
||||
m = append(m, multiple{})
|
||||
case Any:
|
||||
m = append(m, multiple{d})
|
||||
case SingleAny:
|
||||
m = append(m, single{d})
|
||||
}
|
||||
|
||||
return parse(p[i+len(c):], m, d)
|
||||
}
|
||||
|
||||
func New(pattern string, d ...string) (Matcher, error) {
|
||||
chunks, err := parse(pattern, nil, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(chunks) == 1 {
|
||||
return chunks[0], nil
|
||||
}
|
||||
|
||||
return &composite{chunks}, nil
|
||||
}
|
||||
|
||||
type raw struct {
|
||||
s string
|
||||
}
|
||||
func (self raw) Match(s string) bool {
|
||||
return self.s == s
|
||||
}
|
||||
func (self raw) String() string {
|
||||
return fmt.Sprintf("[raw:%s]", self.s)
|
||||
}
|
||||
|
||||
type multiple struct {
|
||||
delimiters []string
|
||||
}
|
||||
func (self multiple) Match(s string) bool {
|
||||
i, _ := firstIndexOfChars(s, self.delimiters)
|
||||
return i == -1
|
||||
}
|
||||
func (self multiple) String() string {
|
||||
return fmt.Sprintf("[multiple:%s]", self.delimiters)
|
||||
}
|
||||
|
||||
type single struct {
|
||||
delimiters []string
|
||||
}
|
||||
func (self single) Match(s string) bool {
|
||||
if len(s) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
i, _ := firstIndexOfChars(s, self.delimiters)
|
||||
|
||||
return i == -1
|
||||
}
|
||||
func (self single) String() string {
|
||||
return fmt.Sprintf("[single:%s]", self.delimiters)
|
||||
}
|
||||
|
||||
type composite struct {
|
||||
chunks []Matcher
|
||||
}
|
||||
|
||||
|
||||
func (self composite) Match(m string) bool {
|
||||
var prev Matcher
|
||||
|
||||
for _, c := range self.chunks {
|
||||
if str, ok := c.(raw); ok {
|
||||
i := strings.Index(m, str.s)
|
||||
if i == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
l := len(str.s)
|
||||
|
||||
if prev != nil {
|
||||
if !prev.Match(m[:i]) {
|
||||
return false
|
||||
}
|
||||
|
||||
prev = nil
|
||||
}
|
||||
|
||||
m = m[i+l:]
|
||||
continue
|
||||
}
|
||||
|
||||
prev = c
|
||||
}
|
||||
|
||||
if prev != nil {
|
||||
return prev.Match(m)
|
||||
}
|
||||
|
||||
return len(m) == 0
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
package glob
|
||||
import (
|
||||
"testing"
|
||||
rGlob "github.com/ryanuber/go-glob"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
||||
type test struct {
|
||||
pattern, match string
|
||||
should bool
|
||||
delimiters []string
|
||||
}
|
||||
|
||||
func glob(s bool, p, m string, d ...string) test {
|
||||
return test{p, m, s, d}
|
||||
}
|
||||
|
||||
func TestFirstIndexOfChars(t *testing.T) {
|
||||
for _, test := range []struct{
|
||||
s string
|
||||
c []string
|
||||
i int
|
||||
r string
|
||||
}{
|
||||
{
|
||||
"**",
|
||||
[]string{"**", "*"},
|
||||
0,
|
||||
"**",
|
||||
},
|
||||
{
|
||||
"**",
|
||||
[]string{"*", "**"},
|
||||
0,
|
||||
"**",
|
||||
},
|
||||
}{
|
||||
i, r := firstIndexOfChars(test.s, test.c)
|
||||
if i != test.i || r != test.r {
|
||||
t.Errorf("unexpeted index: expected %q at %v, got %q at %v", test.r, test.i, r, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlob(t *testing.T) {
|
||||
for _, test := range []test {
|
||||
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, "*", "abc"),
|
||||
glob(true, "**", "a.b.c", "."),
|
||||
|
||||
glob(false, "?at", "at"),
|
||||
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"),
|
||||
}{
|
||||
g, err := New(test.pattern, test.delimiters...)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
continue
|
||||
}
|
||||
|
||||
result := g.Match(test.match)
|
||||
if result != test.should {
|
||||
t.Errorf("pattern %q matching %q should be %v but got %v", test.pattern, test.match, test.should, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const Pattern = "*cat*eyes*"
|
||||
const ExpPattern = ".*cat.*eyes.*"
|
||||
const String = "my cat has very bright eyes"
|
||||
|
||||
func BenchmarkGobwas(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
m, err := New(Pattern)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
_ = m.Match(String)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRyanuber(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = rGlob.Glob(Pattern, String)
|
||||
}
|
||||
}
|
||||
func BenchmarkRegExp(b *testing.B) {
|
||||
r := regexp.MustCompile(ExpPattern)
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = r.Match([]byte(String))
|
||||
}
|
||||
}
|
||||
|
||||
var ALPHABET_S = []string{"a", "b", "c"}
|
||||
const ALPHABET = "abc"
|
||||
const STR = "faafsdfcsdffc"
|
||||
|
||||
|
||||
func BenchmarkIndexOfAny(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strings.IndexAny(STR, ALPHABET)
|
||||
}
|
||||
}
|
||||
func BenchmarkFirstIndexOfChars(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
firstIndexOfChars(STR, ALPHABET_S)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue