forked from mirror/glob
use pool of segments
This commit is contained in:
parent
88fcc08f39
commit
57a5246fac
|
@ -9,6 +9,7 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
func draw(pattern string, m match.Matcher) string {
|
func draw(pattern string, m match.Matcher) string {
|
||||||
|
@ -60,7 +61,7 @@ func graphviz(m match.Matcher, id string) string {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
pattern := flag.String("p", "", "pattern to draw")
|
pattern := flag.String("p", "", "pattern to draw")
|
||||||
sep := flag.String("s", "", "comma separated list of separators")
|
sep := flag.String("s", "", "comma separated list of separators characters")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *pattern == "" {
|
if *pattern == "" {
|
||||||
|
@ -68,7 +69,17 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
glob, err := glob.Compile(*pattern, strings.Split(*sep, ",")...)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glob, err := glob.Compile(*pattern, separators...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("could not compile pattern:", err)
|
fmt.Println("could not compile pattern:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
func benchString(r testing.BenchmarkResult) string {
|
func benchString(r testing.BenchmarkResult) string {
|
||||||
|
@ -42,7 +43,16 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
separators := strings.Split(*sep, ",")
|
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...)
|
g, err := glob.Compile(*pattern, separators...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("could not compile pattern:", err)
|
fmt.Println("could not compile pattern:", err)
|
||||||
|
|
22
compiler.go
22
compiler.go
|
@ -3,8 +3,8 @@ package glob
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gobwas/glob/match"
|
"github.com/gobwas/glob/match"
|
||||||
|
"github.com/gobwas/glob/runes"
|
||||||
"reflect"
|
"reflect"
|
||||||
"unicode/utf8"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func optimize(matcher match.Matcher) match.Matcher {
|
func optimize(matcher match.Matcher) match.Matcher {
|
||||||
|
@ -23,8 +23,8 @@ func optimize(matcher match.Matcher) match.Matcher {
|
||||||
return m
|
return m
|
||||||
|
|
||||||
case match.List:
|
case match.List:
|
||||||
if m.Not == false && utf8.RuneCountInString(m.List) == 1 {
|
if m.Not == false && len(m.List) == 1 {
|
||||||
return match.NewText(m.List)
|
return match.NewText(string(m.List))
|
||||||
}
|
}
|
||||||
|
|
||||||
return m
|
return m
|
||||||
|
@ -172,7 +172,7 @@ func glueAsEvery(matchers []match.Matcher) match.Matcher {
|
||||||
separator = sep
|
separator = sep
|
||||||
}
|
}
|
||||||
|
|
||||||
if sep == separator {
|
if runes.Equal(sep, separator) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ func glueAsEvery(matchers []match.Matcher) match.Matcher {
|
||||||
return match.Any{separator}
|
return match.Any{separator}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasAny || hasSuper) && min > 0 && separator == "" {
|
if (hasAny || hasSuper) && min > 0 && len(separator) == 0 {
|
||||||
return match.Min{min}
|
return match.Min{min}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,8 +201,8 @@ func glueAsEvery(matchers []match.Matcher) match.Matcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if separator != "" {
|
if len(separator) > 0 {
|
||||||
every.Add(match.Contains{separator, true})
|
every.Add(match.Contains{string(separator), true})
|
||||||
}
|
}
|
||||||
|
|
||||||
return every
|
return every
|
||||||
|
@ -468,7 +468,7 @@ func compileMatchers(matchers []match.Matcher) (match.Matcher, error) {
|
||||||
// return sum * k
|
// return sum * k
|
||||||
//}
|
//}
|
||||||
|
|
||||||
func doAnyOf(n *nodeAnyOf, s string) (match.Matcher, error) {
|
func doAnyOf(n *nodeAnyOf, s []rune) (match.Matcher, error) {
|
||||||
var matchers []match.Matcher
|
var matchers []match.Matcher
|
||||||
for _, desc := range n.children() {
|
for _, desc := range n.children() {
|
||||||
if desc == nil {
|
if desc == nil {
|
||||||
|
@ -532,7 +532,7 @@ func do(leaf node, s []rune) (m match.Matcher, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case *nodeList:
|
case *nodeList:
|
||||||
m = match.List{n.chars, n.not}
|
m = match.List{[]rune(n.chars), n.not}
|
||||||
|
|
||||||
case *nodeRange:
|
case *nodeRange:
|
||||||
m = match.Range{n.lo, n.hi, n.not}
|
m = match.Range{n.lo, n.hi, n.not}
|
||||||
|
@ -556,7 +556,7 @@ func do(leaf node, s []rune) (m match.Matcher, err error) {
|
||||||
return optimize(m), nil
|
return optimize(m), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func do2(node node, s string) ([]match.Matcher, error) {
|
func do2(node node, s []rune) ([]match.Matcher, error) {
|
||||||
var result []match.Matcher
|
var result []match.Matcher
|
||||||
|
|
||||||
switch n := node.(type) {
|
switch n := node.(type) {
|
||||||
|
@ -631,7 +631,7 @@ func do2(node node, s string) ([]match.Matcher, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
case *nodeList:
|
case *nodeList:
|
||||||
result = append(result, match.List{n.chars, n.not})
|
result = append(result, match.List{[]rune(n.chars), n.not})
|
||||||
|
|
||||||
case *nodeRange:
|
case *nodeRange:
|
||||||
result = append(result, match.Range{n.lo, n.hi, n.not})
|
result = append(result, match.Range{n.lo, n.hi, n.not})
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
const separators = "."
|
var separators = []rune{'.'}
|
||||||
|
|
||||||
func TestGlueMatchers(t *testing.T) {
|
func TestGlueMatchers(t *testing.T) {
|
||||||
for id, test := range []struct {
|
for id, test := range []struct {
|
||||||
|
@ -27,7 +27,7 @@ func TestGlueMatchers(t *testing.T) {
|
||||||
},
|
},
|
||||||
match.EveryOf{match.Matchers{
|
match.EveryOf{match.Matchers{
|
||||||
match.Min{1},
|
match.Min{1},
|
||||||
match.Contains{separators, true},
|
match.Contains{string(separators), true},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -43,8 +43,8 @@ func TestGlueMatchers(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[]match.Matcher{
|
[]match.Matcher{
|
||||||
match.List{"a", true},
|
match.List{[]rune{'a'}, true},
|
||||||
match.Any{"a"},
|
match.Any{[]rune{'a'}},
|
||||||
},
|
},
|
||||||
match.EveryOf{match.Matchers{
|
match.EveryOf{match.Matchers{
|
||||||
match.Min{1},
|
match.Min{1},
|
||||||
|
@ -101,14 +101,14 @@ func TestCompileMatchers(t *testing.T) {
|
||||||
{
|
{
|
||||||
[]match.Matcher{
|
[]match.Matcher{
|
||||||
match.Range{'a', 'c', true},
|
match.Range{'a', 'c', true},
|
||||||
match.List{"zte", false},
|
match.List{[]rune{'z', 't', 'e'}, false},
|
||||||
match.NewText("c"),
|
match.NewText("c"),
|
||||||
match.Single{},
|
match.Single{},
|
||||||
},
|
},
|
||||||
match.Row{
|
match.Row{
|
||||||
Matchers: match.Matchers{
|
Matchers: match.Matchers{
|
||||||
match.Range{'a', 'c', true},
|
match.Range{'a', 'c', true},
|
||||||
match.List{"zte", false},
|
match.List{[]rune{'z', 't', 'e'}, false},
|
||||||
match.NewText("c"),
|
match.NewText("c"),
|
||||||
match.Single{},
|
match.Single{},
|
||||||
},
|
},
|
||||||
|
@ -136,7 +136,7 @@ func TestConvertMatchers(t *testing.T) {
|
||||||
{
|
{
|
||||||
[]match.Matcher{
|
[]match.Matcher{
|
||||||
match.Range{'a', 'c', true},
|
match.Range{'a', 'c', true},
|
||||||
match.List{"zte", false},
|
match.List{[]rune{'z', 't', 'e'}, false},
|
||||||
match.NewText("c"),
|
match.NewText("c"),
|
||||||
match.Single{},
|
match.Single{},
|
||||||
match.Any{},
|
match.Any{},
|
||||||
|
@ -145,7 +145,7 @@ func TestConvertMatchers(t *testing.T) {
|
||||||
match.Row{
|
match.Row{
|
||||||
Matchers: match.Matchers{
|
Matchers: match.Matchers{
|
||||||
match.Range{'a', 'c', true},
|
match.Range{'a', 'c', true},
|
||||||
match.List{"zte", false},
|
match.List{[]rune{'z', 't', 'e'}, false},
|
||||||
match.NewText("c"),
|
match.NewText("c"),
|
||||||
match.Single{},
|
match.Single{},
|
||||||
},
|
},
|
||||||
|
@ -157,7 +157,7 @@ func TestConvertMatchers(t *testing.T) {
|
||||||
{
|
{
|
||||||
[]match.Matcher{
|
[]match.Matcher{
|
||||||
match.Range{'a', 'c', true},
|
match.Range{'a', 'c', true},
|
||||||
match.List{"zte", false},
|
match.List{[]rune{'z', 't', 'e'}, false},
|
||||||
match.NewText("c"),
|
match.NewText("c"),
|
||||||
match.Single{},
|
match.Single{},
|
||||||
match.Any{},
|
match.Any{},
|
||||||
|
@ -169,7 +169,7 @@ func TestConvertMatchers(t *testing.T) {
|
||||||
match.Row{
|
match.Row{
|
||||||
Matchers: match.Matchers{
|
Matchers: match.Matchers{
|
||||||
match.Range{'a', 'c', true},
|
match.Range{'a', 'c', true},
|
||||||
match.List{"zte", false},
|
match.List{[]rune{'z', 't', 'e'}, false},
|
||||||
match.NewText("c"),
|
match.NewText("c"),
|
||||||
},
|
},
|
||||||
RunesLength: 3,
|
RunesLength: 3,
|
||||||
|
@ -204,7 +204,7 @@ func TestCompiler(t *testing.T) {
|
||||||
for id, test := range []struct {
|
for id, test := range []struct {
|
||||||
ast *nodePattern
|
ast *nodePattern
|
||||||
result Glob
|
result Glob
|
||||||
sep string
|
sep []rune
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
ast: pattern(&nodeText{text: "abc"}),
|
ast: pattern(&nodeText{text: "abc"}),
|
||||||
|
@ -241,14 +241,14 @@ func TestCompiler(t *testing.T) {
|
||||||
chars: "abc",
|
chars: "abc",
|
||||||
not: true,
|
not: true,
|
||||||
}),
|
}),
|
||||||
result: match.List{"abc", true},
|
result: match.List{[]rune{'a', 'b', 'c'}, true},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ast: pattern(&nodeAny{}, &nodeSingle{}, &nodeSingle{}, &nodeSingle{}),
|
ast: pattern(&nodeAny{}, &nodeSingle{}, &nodeSingle{}, &nodeSingle{}),
|
||||||
sep: separators,
|
sep: separators,
|
||||||
result: match.EveryOf{Matchers: match.Matchers{
|
result: match.EveryOf{Matchers: match.Matchers{
|
||||||
match.Min{3},
|
match.Min{3},
|
||||||
match.Contains{separators, true},
|
match.Contains{string(separators), true},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -349,7 +349,7 @@ func TestCompiler(t *testing.T) {
|
||||||
nil,
|
nil,
|
||||||
match.AnyOf{Matchers: match.Matchers{
|
match.AnyOf{Matchers: match.Matchers{
|
||||||
match.Single{},
|
match.Single{},
|
||||||
match.List{List: "def"},
|
match.List{List: []rune{'d', 'e', 'f'}},
|
||||||
match.Nothing{},
|
match.Nothing{},
|
||||||
}},
|
}},
|
||||||
),
|
),
|
||||||
|
@ -390,8 +390,8 @@ func TestCompiler(t *testing.T) {
|
||||||
Matchers: match.Matchers{
|
Matchers: match.Matchers{
|
||||||
match.NewText("abc"),
|
match.NewText("abc"),
|
||||||
match.AnyOf{Matchers: match.Matchers{
|
match.AnyOf{Matchers: match.Matchers{
|
||||||
match.List{List: "abc"},
|
match.List{List: []rune{'a', 'b', 'c'}},
|
||||||
match.List{List: "def"},
|
match.List{List: []rune{'d', 'e', 'f'}},
|
||||||
}},
|
}},
|
||||||
match.NewText("ghi"),
|
match.NewText("ghi"),
|
||||||
},
|
},
|
||||||
|
|
4
glob.go
4
glob.go
|
@ -1,7 +1,5 @@
|
||||||
package glob
|
package glob
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// Glob represents compiled glob pattern.
|
// Glob represents compiled glob pattern.
|
||||||
type Glob interface {
|
type Glob interface {
|
||||||
Match(string) bool
|
Match(string) bool
|
||||||
|
@ -48,7 +46,7 @@ func Compile(pattern string, separators ...rune) (Glob, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustCompile is the same as Compile, except that if Compile returns error, this will panic
|
// MustCompile is the same as Compile, except that if Compile returns error, this will panic
|
||||||
func MustCompile(pattern string, separators ...string) Glob {
|
func MustCompile(pattern string, separators ...rune) Glob {
|
||||||
g, err := Compile(pattern, separators...)
|
g, err := Compile(pattern, separators...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
24
glob_test.go
24
glob_test.go
|
@ -53,10 +53,10 @@ const (
|
||||||
type test struct {
|
type test struct {
|
||||||
pattern, match string
|
pattern, match string
|
||||||
should bool
|
should bool
|
||||||
delimiters []string
|
delimiters []rune
|
||||||
}
|
}
|
||||||
|
|
||||||
func glob(s bool, p, m string, d ...string) test {
|
func glob(s bool, p, m string, d ...rune) test {
|
||||||
return test{p, m, s, d}
|
return test{p, m, s, d}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,22 +68,22 @@ func TestGlob(t *testing.T) {
|
||||||
glob(true, "a*c", "abc"),
|
glob(true, "a*c", "abc"),
|
||||||
glob(true, "a*c", "a12345c"),
|
glob(true, "a*c", "a12345c"),
|
||||||
glob(true, "a?c", "a1c"),
|
glob(true, "a?c", "a1c"),
|
||||||
glob(true, "a.b", "a.b", "."),
|
glob(true, "a.b", "a.b", '.'),
|
||||||
glob(true, "a.*", "a.b", "."),
|
glob(true, "a.*", "a.b", '.'),
|
||||||
glob(true, "a.**", "a.b.c", "."),
|
glob(true, "a.**", "a.b.c", '.'),
|
||||||
glob(true, "a.?.c", "a.b.c", "."),
|
glob(true, "a.?.c", "a.b.c", '.'),
|
||||||
glob(true, "a.?.?", "a.b.c", "."),
|
glob(true, "a.?.?", "a.b.c", '.'),
|
||||||
glob(true, "?at", "cat"),
|
glob(true, "?at", "cat"),
|
||||||
glob(true, "?at", "fat"),
|
glob(true, "?at", "fat"),
|
||||||
glob(true, "*", "abc"),
|
glob(true, "*", "abc"),
|
||||||
glob(true, `\*`, "*"),
|
glob(true, `\*`, "*"),
|
||||||
glob(true, "**", "a.b.c", "."),
|
glob(true, "**", "a.b.c", '.'),
|
||||||
|
|
||||||
glob(false, "?at", "at"),
|
glob(false, "?at", "at"),
|
||||||
glob(false, "?at", "fat", "f"),
|
glob(false, "?at", "fat", 'f'),
|
||||||
glob(false, "a.*", "a.b.c", "."),
|
glob(false, "a.*", "a.b.c", '.'),
|
||||||
glob(false, "a.?.c", "a.bb.c", "."),
|
glob(false, "a.?.c", "a.bb.c", '.'),
|
||||||
glob(false, "*", "a.b.c", "."),
|
glob(false, "*", "a.b.c", '.'),
|
||||||
|
|
||||||
glob(true, "*test", "this is a test"),
|
glob(true, "*test", "this is a test"),
|
||||||
glob(true, "this*", "this is a test"),
|
glob(true, "this*", "this is a test"),
|
||||||
|
|
22
match/any.go
22
match/any.go
|
@ -2,8 +2,7 @@ package match
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"github.com/gobwas/glob/strings"
|
||||||
"unicode/utf8"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Any struct {
|
type Any struct {
|
||||||
|
@ -11,28 +10,25 @@ type Any struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Any) Match(s string) bool {
|
func (self Any) Match(s string) bool {
|
||||||
return strings.IndexAny(s, self.Separators) == -1
|
return strings.IndexAnyRunes(s, self.Separators) == -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Any) Index(s string) (int, []int) {
|
func (self Any) Index(s string, segments []int) (int, []int) {
|
||||||
var sub string
|
found := strings.IndexAnyRunes(s, self.Separators)
|
||||||
|
|
||||||
found := strings.IndexAny(s, self.Separators)
|
|
||||||
switch found {
|
switch found {
|
||||||
case -1:
|
case -1:
|
||||||
sub = s
|
|
||||||
case 0:
|
case 0:
|
||||||
return 0, []int{0}
|
segments = append(segments)
|
||||||
|
return 0, segments
|
||||||
default:
|
default:
|
||||||
sub = s[:found]
|
s = s[:found]
|
||||||
}
|
}
|
||||||
|
|
||||||
segments := make([]int, 0, utf8.RuneCountInString(sub)+1)
|
for i := range s {
|
||||||
for i := range sub {
|
|
||||||
segments = append(segments, i)
|
segments = append(segments, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
segments = append(segments, len(sub))
|
segments = append(segments, len(s))
|
||||||
|
|
||||||
return 0, segments
|
return 0, segments
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,39 +23,38 @@ func (self AnyOf) Match(s string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self AnyOf) Index(s string) (int, []int) {
|
func (self AnyOf) Index(s string, segments []int) (int, []int) {
|
||||||
if len(self.Matchers) == 0 {
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// segments to merge
|
|
||||||
var segments [][]int
|
|
||||||
index := -1
|
index := -1
|
||||||
|
|
||||||
for _, m := range self.Matchers {
|
for _, m := range self.Matchers {
|
||||||
idx, seg := m.Index(s)
|
in := acquireSegments(len(s))
|
||||||
|
idx, seg := m.Index(s, in)
|
||||||
if idx == -1 {
|
if idx == -1 {
|
||||||
|
releaseSegments(in)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if index == -1 || idx < index {
|
if index == -1 || idx < index {
|
||||||
index = idx
|
index = idx
|
||||||
segments = [][]int{seg}
|
segments = append(segments[:0], seg...)
|
||||||
|
releaseSegments(in)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if idx > index {
|
if idx > index {
|
||||||
|
releaseSegments(in)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
segments = append(segments, seg)
|
// here idx == index
|
||||||
|
segments = appendMerge(segments, seg)
|
||||||
|
releaseSegments(in)
|
||||||
}
|
}
|
||||||
|
|
||||||
if index == -1 {
|
if index == -1 {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return index, mergeSegments(segments)
|
return index, segments
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self AnyOf) Len() (l int) {
|
func (self AnyOf) Len() (l int) {
|
||||||
|
|
|
@ -33,8 +33,8 @@ func TestAnyOfIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Matchers{
|
Matchers{
|
||||||
List{"[def]", false},
|
List{[]rune("[def]"), false},
|
||||||
List{"[abc]", false},
|
List{[]rune("[abc]"), false},
|
||||||
},
|
},
|
||||||
"abcdef",
|
"abcdef",
|
||||||
0,
|
0,
|
||||||
|
@ -42,7 +42,7 @@ func TestAnyOfIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
everyOf := AnyOf{test.matchers}
|
everyOf := AnyOf{test.matchers}
|
||||||
index, segments := everyOf.Index(test.fixture)
|
index, segments := everyOf.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,38 +7,53 @@ import (
|
||||||
|
|
||||||
func TestAnyIndex(t *testing.T) {
|
func TestAnyIndex(t *testing.T) {
|
||||||
for id, test := range []struct {
|
for id, test := range []struct {
|
||||||
sep string
|
sep []rune
|
||||||
fixture string
|
fixture string
|
||||||
index int
|
index int
|
||||||
segments []int
|
segments []int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
".",
|
[]rune{'.'},
|
||||||
"abc",
|
"abc",
|
||||||
0,
|
0,
|
||||||
[]int{0, 1, 2, 3},
|
[]int{0, 1, 2, 3},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
".",
|
[]rune{'.'},
|
||||||
"abc.def",
|
"abc.def",
|
||||||
0,
|
0,
|
||||||
[]int{0, 1, 2, 3},
|
[]int{0, 1, 2, 3},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
p := Any{test.sep}
|
p := Any{test.sep}
|
||||||
index, segments := p.Index(test.fixture)
|
index, segments := p.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(segments, test.segments) {
|
if !reflect.DeepEqual(segments, test.segments) {
|
||||||
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
|
t.Errorf("#%d unexpected segments: exp: %v, act: %v", id, test.segments, segments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
releaseSegments(segments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkIndexAny(b *testing.B) {
|
func BenchmarkIndexAny(b *testing.B) {
|
||||||
p := Any{bench_separators}
|
m := Any{bench_separators}
|
||||||
|
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
p.Index(bench_pattern)
|
m.Index(bench_pattern, in[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexAnyParallel(b *testing.B) {
|
||||||
|
m := Any{bench_separators}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (self BTree) Len() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo?
|
// todo?
|
||||||
func (self BTree) Index(s string) (int, []int) {
|
func (self BTree) Index(s string, segments []int) (int, []int) {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +79,10 @@ func (self BTree) Match(s string) bool {
|
||||||
|
|
||||||
for offset < limit {
|
for offset < limit {
|
||||||
// search for matching part in substring
|
// search for matching part in substring
|
||||||
index, segments := self.Value.Index(s[offset:limit])
|
in := acquireSegments(limit - offset)
|
||||||
|
index, segments := self.Value.Index(s[offset:limit], in)
|
||||||
if index == -1 {
|
if index == -1 {
|
||||||
|
releaseSegments(in)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,11 +114,14 @@ func (self BTree) Match(s string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if right {
|
if right {
|
||||||
|
releaseSegments(in)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
releaseSegments(in)
|
||||||
|
|
||||||
_, step := utf8.DecodeRuneInString(s[offset+index:])
|
_, step := utf8.DecodeRuneInString(s[offset+index:])
|
||||||
offset += index + step
|
offset += index + step
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ package match
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Contains struct {
|
type Contains struct {
|
||||||
|
@ -15,11 +14,8 @@ func (self Contains) Match(s string) bool {
|
||||||
return strings.Contains(s, self.Needle) != self.Not
|
return strings.Contains(s, self.Needle) != self.Not
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Contains) Index(s string) (int, []int) {
|
func (self Contains) Index(s string, segments []int) (int, []int) {
|
||||||
var (
|
var offset int
|
||||||
sub string
|
|
||||||
offset int
|
|
||||||
)
|
|
||||||
|
|
||||||
idx := strings.Index(s, self.Needle)
|
idx := strings.Index(s, self.Needle)
|
||||||
|
|
||||||
|
@ -29,27 +25,19 @@ func (self Contains) Index(s string) (int, []int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = idx + len(self.Needle)
|
offset = idx + len(self.Needle)
|
||||||
|
|
||||||
if len(s) <= offset {
|
if len(s) <= offset {
|
||||||
return 0, []int{offset}
|
return 0, append(segments, offset)
|
||||||
|
}
|
||||||
|
s = s[offset:]
|
||||||
|
} else if idx != -1 {
|
||||||
|
s = s[:idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
sub = s[offset:]
|
for i, _ := range s {
|
||||||
} else {
|
|
||||||
switch idx {
|
|
||||||
case -1:
|
|
||||||
sub = s
|
|
||||||
default:
|
|
||||||
sub = s[:idx]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
segments := make([]int, 0, utf8.RuneCountInString(sub)+1)
|
|
||||||
for i, _ := range sub {
|
|
||||||
segments = append(segments, offset+i)
|
segments = append(segments, offset+i)
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0, append(segments, offset+len(sub))
|
return 0, append(segments, offset+len(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Contains) Len() int {
|
func (self Contains) Len() int {
|
||||||
|
|
|
@ -43,7 +43,7 @@ func TestContainsIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
p := Contains{test.prefix, test.not}
|
p := Contains{test.prefix, test.not}
|
||||||
index, segments := p.Index(test.fixture)
|
index, segments := p.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
@ -54,8 +54,21 @@ func TestContainsIndex(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkIndexContains(b *testing.B) {
|
func BenchmarkIndexContains(b *testing.B) {
|
||||||
m := Contains{bench_separators, true}
|
m := Contains{string(bench_separators), true}
|
||||||
|
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
m.Index(bench_pattern)
|
m.Index(bench_pattern, in[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexContainsParallel(b *testing.B) {
|
||||||
|
m := Contains{string(bench_separators), true}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -25,43 +25,66 @@ func (self EveryOf) Len() (l int) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self EveryOf) Index(s string) (int, []int) {
|
func max(a, b int) int {
|
||||||
|
if a >= b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self EveryOf) Index(s string, out []int) (int, []int) {
|
||||||
var index int
|
var index int
|
||||||
var offset int
|
var offset int
|
||||||
var segments []int
|
var current []int
|
||||||
|
|
||||||
sub := s
|
sub := s
|
||||||
for _, m := range self.Matchers {
|
for i, m := range self.Matchers {
|
||||||
idx, seg := m.Index(sub)
|
in := acquireSegments(len(sub))
|
||||||
|
idx, seg := m.Index(sub, in)
|
||||||
if idx == -1 {
|
if idx == -1 {
|
||||||
|
releaseSegments(in)
|
||||||
|
if cap(current) > 0 {
|
||||||
|
releaseSegments(current)
|
||||||
|
}
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var sum []int
|
next := acquireSegments(max(len(seg), len(current)))
|
||||||
if segments == nil {
|
if i == 0 {
|
||||||
sum = seg
|
next = append(next, seg...)
|
||||||
} else {
|
} else {
|
||||||
delta := index - (idx + offset)
|
delta := index - (idx + offset)
|
||||||
for _, ex := range segments {
|
for _, ex := range current {
|
||||||
for _, n := range seg {
|
for _, n := range seg {
|
||||||
if ex+delta == n {
|
if ex+delta == n {
|
||||||
sum = append(sum, n)
|
next = append(next, n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(sum) == 0 {
|
if cap(current) > 0 {
|
||||||
|
releaseSegments(current)
|
||||||
|
}
|
||||||
|
releaseSegments(in)
|
||||||
|
|
||||||
|
if len(next) == 0 {
|
||||||
|
releaseSegments(next)
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
segments = sum
|
current = next
|
||||||
|
|
||||||
index = idx + offset
|
index = idx + offset
|
||||||
sub = s[index:]
|
sub = s[index:]
|
||||||
offset += idx
|
offset += idx
|
||||||
}
|
}
|
||||||
|
|
||||||
return index, segments
|
out = append(out, current...)
|
||||||
|
releaseSegments(current)
|
||||||
|
|
||||||
|
return index, out
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self EveryOf) Match(s string) bool {
|
func (self EveryOf) Match(s string) bool {
|
||||||
|
|
|
@ -34,7 +34,7 @@ func TestEveryOfIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
everyOf := EveryOf{test.matchers}
|
everyOf := EveryOf{test.matchers}
|
||||||
index, segments := everyOf.Index(test.fixture)
|
index, segments := everyOf.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,22 @@ package match
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"github.com/gobwas/glob/runes"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
type List struct {
|
type List struct {
|
||||||
List string
|
List []rune
|
||||||
Not bool
|
Not bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self List) Match(s string) bool {
|
func (self List) Match(s string) bool {
|
||||||
// if s 100% have two symbols
|
r, w := utf8.DecodeRuneInString(s)
|
||||||
// _, w := utf8.DecodeRuneInString(s)
|
if len(s) > w {
|
||||||
// if len(s) > w {
|
|
||||||
if len(s) > 4 {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
inList := strings.Index(self.List, s) != -1
|
inList := runes.IndexRune(self.List, r) != -1
|
||||||
return inList == !self.Not
|
return inList == !self.Not
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,10 +25,10 @@ func (self List) Len() int {
|
||||||
return lenOne
|
return lenOne
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self List) Index(s string) (int, []int) {
|
func (self List) Index(s string, segments []int) (int, []int) {
|
||||||
for i, r := range s {
|
for i, r := range s {
|
||||||
if self.Not == (strings.IndexRune(self.List, r) == -1) {
|
if self.Not == (runes.IndexRune(self.List, r) == -1) {
|
||||||
return i, []int{utf8.RuneLen(r)}
|
return i, append(segments, utf8.RuneLen(r))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,21 +7,21 @@ import (
|
||||||
|
|
||||||
func TestListIndex(t *testing.T) {
|
func TestListIndex(t *testing.T) {
|
||||||
for id, test := range []struct {
|
for id, test := range []struct {
|
||||||
list string
|
list []rune
|
||||||
not bool
|
not bool
|
||||||
fixture string
|
fixture string
|
||||||
index int
|
index int
|
||||||
segments []int
|
segments []int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"ab",
|
[]rune("ab"),
|
||||||
false,
|
false,
|
||||||
"abc",
|
"abc",
|
||||||
0,
|
0,
|
||||||
[]int{1},
|
[]int{1},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ab",
|
[]rune("ab"),
|
||||||
true,
|
true,
|
||||||
"fffabfff",
|
"fffabfff",
|
||||||
0,
|
0,
|
||||||
|
@ -29,7 +29,7 @@ func TestListIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
p := List{test.list, test.not}
|
p := List{test.list, test.not}
|
||||||
index, segments := p.Index(test.fixture)
|
index, segments := p.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,21 @@ func TestListIndex(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkIndexList(b *testing.B) {
|
func BenchmarkIndexList(b *testing.B) {
|
||||||
m := List{"def", false}
|
m := List{[]rune("def"), false}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
m.Index(bench_pattern)
|
m.Index(bench_pattern, in[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexListParallel(b *testing.B) {
|
||||||
|
m := List{[]rune("def"), false}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
134
match/match.go
134
match/match.go
|
@ -3,6 +3,7 @@ package match
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
const lenOne = 1
|
const lenOne = 1
|
||||||
|
@ -11,7 +12,7 @@ const lenNo = -1
|
||||||
|
|
||||||
type Matcher interface {
|
type Matcher interface {
|
||||||
Match(string) bool
|
Match(string) bool
|
||||||
Index(string) (int, []int)
|
Index(string, []int) (int, []int)
|
||||||
Len() int
|
Len() int
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
@ -27,6 +28,58 @@ func (m Matchers) String() string {
|
||||||
return fmt.Sprintf("%s", strings.Join(s, ","))
|
return fmt.Sprintf("%s", strings.Join(s, ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for i := 1024; i >= 1; i >>= 1 {
|
||||||
|
func(i int) {
|
||||||
|
segmentsPools[i-1] = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]int, 0, i)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var segmentsPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return make([]int, 0, 64)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIdx(c int) int {
|
||||||
|
p := toPowerOfTwo(c)
|
||||||
|
switch {
|
||||||
|
case p >= 1024:
|
||||||
|
return 1023
|
||||||
|
case p < 1:
|
||||||
|
return 0
|
||||||
|
default:
|
||||||
|
return p - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func acquireSegments(c int) []int {
|
||||||
|
return segmentsPools[getIdx(c)].Get().([]int)[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func releaseSegments(s []int) {
|
||||||
|
segmentsPools[getIdx(cap(s))].Put(s)
|
||||||
|
}
|
||||||
|
|
||||||
func appendIfNotAsPrevious(target []int, val int) []int {
|
func appendIfNotAsPrevious(target []int, val int) []int {
|
||||||
l := len(target)
|
l := len(target)
|
||||||
if l != 0 && target[l-1] == val {
|
if l != 0 && target[l-1] == val {
|
||||||
|
@ -36,16 +89,64 @@ func appendIfNotAsPrevious(target []int, val int) []int {
|
||||||
return append(target, val)
|
return append(target, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
// mergeSegments merges and sorts given already SORTED and UNIQUE segments.
|
func appendMerge(target, sub []int) []int {
|
||||||
func mergeSegments(segments [][]int) []int {
|
lt, ls := len(target), len(sub)
|
||||||
var current []int
|
out := acquireSegments(lt + ls)
|
||||||
for _, s := range segments {
|
|
||||||
if current == nil {
|
for x, y := 0, 0; x < lt || y < ls; {
|
||||||
current = s
|
if x >= lt {
|
||||||
continue
|
out = append(out, sub[y:]...)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
var next []int
|
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...)
|
||||||
|
releaseSegments(out)
|
||||||
|
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
|
// mergeSegments merges and sorts given already SORTED and UNIQUE segments.
|
||||||
|
func mergeSegments(list [][]int, out []int) []int {
|
||||||
|
var current []int
|
||||||
|
switch len(list) {
|
||||||
|
case 0:
|
||||||
|
return out
|
||||||
|
case 1:
|
||||||
|
return list[0]
|
||||||
|
default:
|
||||||
|
current = acquireSegments(len(list[0]))
|
||||||
|
current = append(current, list[0]...)
|
||||||
|
// releaseSegments(list[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range list[1:] {
|
||||||
|
next := acquireSegments(len(current) + len(s))
|
||||||
for x, y := 0, 0; x < len(current) || y < len(s); {
|
for x, y := 0, 0; x < len(current) || y < len(s); {
|
||||||
if x >= len(current) {
|
if x >= len(current) {
|
||||||
next = append(next, s[y:]...)
|
next = append(next, s[y:]...)
|
||||||
|
@ -78,8 +179,21 @@ func mergeSegments(segments [][]int) []int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
releaseSegments(current)
|
||||||
current = next
|
current = next
|
||||||
}
|
}
|
||||||
|
|
||||||
return current
|
out = append(out, current...)
|
||||||
|
releaseSegments(current)
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,36 +5,60 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
const bench_separators = "."
|
var bench_separators = []rune{'.'}
|
||||||
|
|
||||||
const bench_pattern = "abcdefghijklmnopqrstuvwxyz0123456789"
|
const bench_pattern = "abcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
|
|
||||||
func TestMergeSegments(t *testing.T) {
|
func TestAppendMerge(t *testing.T) {
|
||||||
for id, test := range []struct {
|
for id, test := range []struct {
|
||||||
segments [][]int
|
segments [2][]int
|
||||||
exp []int
|
exp []int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
[][]int{
|
[2][]int{
|
||||||
[]int{0, 6, 7},
|
[]int{0, 6, 7},
|
||||||
[]int{0, 1, 3},
|
[]int{0, 1, 3},
|
||||||
[]int{2, 4},
|
|
||||||
},
|
},
|
||||||
[]int{0, 1, 2, 3, 4, 6, 7},
|
[]int{0, 1, 3, 6, 7},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
[][]int{
|
[2][]int{
|
||||||
[]int{0, 1, 3, 6, 7},
|
[]int{0, 1, 3, 6, 7},
|
||||||
[]int{0, 1, 3},
|
[]int{0, 1, 10},
|
||||||
[]int{2, 4},
|
|
||||||
[]int{1},
|
|
||||||
},
|
},
|
||||||
[]int{0, 1, 2, 3, 4, 6, 7},
|
[]int{0, 1, 3, 6, 7, 10},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
act := mergeSegments(test.segments)
|
act := appendMerge(test.segments[0], test.segments[1])
|
||||||
if !reflect.DeepEqual(act, test.exp) {
|
if !reflect.DeepEqual(act, test.exp) {
|
||||||
t.Errorf("#%d merge sort segments unexpected:\nact: %v\nexp:%v", id, act, test.exp)
|
t.Errorf("#%d merge sort segments unexpected:\nact: %v\nexp:%v", id, act, test.exp)
|
||||||
continue
|
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})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ func (self Max) Match(s string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Max) Index(s string) (index int, segments []int) {
|
func (self Max) Index(s string, segments []int) (int, []int) {
|
||||||
segments = append(segments, 0)
|
segments = append(segments, 0)
|
||||||
var count int
|
var count int
|
||||||
for i, r := range s {
|
for i, r := range s {
|
||||||
|
|
|
@ -26,7 +26,7 @@ func TestMaxIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
p := Max{test.limit}
|
p := Max{test.limit}
|
||||||
index, segments := p.Index(test.fixture)
|
index, segments := p.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,20 @@ func TestMaxIndex(t *testing.T) {
|
||||||
|
|
||||||
func BenchmarkIndexMax(b *testing.B) {
|
func BenchmarkIndexMax(b *testing.B) {
|
||||||
m := Max{10}
|
m := Max{10}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
m.Index(bench_pattern)
|
m.Index(bench_pattern, in[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexMaxParallel(b *testing.B) {
|
||||||
|
m := Max{10}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
14
match/min.go
14
match/min.go
|
@ -21,22 +21,22 @@ func (self Min) Match(s string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Min) Index(s string) (int, []int) {
|
func (self Min) Index(s string, segments []int) (int, []int) {
|
||||||
var count int
|
var count int
|
||||||
|
var found bool
|
||||||
|
|
||||||
c := utf8.RuneCountInString(s)
|
|
||||||
if c < self.Limit {
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
segments := make([]int, 0, c-self.Limit+1)
|
|
||||||
for i, r := range s {
|
for i, r := range s {
|
||||||
count++
|
count++
|
||||||
if count >= self.Limit {
|
if count >= self.Limit {
|
||||||
|
found = true
|
||||||
segments = append(segments, i+utf8.RuneLen(r))
|
segments = append(segments, i+utf8.RuneLen(r))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return -1, nil
|
||||||
|
}
|
||||||
|
|
||||||
return 0, segments
|
return 0, segments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ func TestMinIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
p := Min{test.limit}
|
p := Min{test.limit}
|
||||||
index, segments := p.Index(test.fixture)
|
index, segments := p.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,20 @@ func TestMinIndex(t *testing.T) {
|
||||||
|
|
||||||
func BenchmarkIndexMin(b *testing.B) {
|
func BenchmarkIndexMin(b *testing.B) {
|
||||||
m := Min{10}
|
m := Min{10}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
m.Index(bench_pattern)
|
m.Index(bench_pattern, in[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexMinParallel(b *testing.B) {
|
||||||
|
m := Min{10}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ func (self Nothing) Match(s string) bool {
|
||||||
return len(s) == 0
|
return len(s) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Nothing) Index(s string) (int, []int) {
|
func (self Nothing) Index(s string, segments []int) (int, []int) {
|
||||||
return 0, []int{0}
|
return 0, append(segments, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Nothing) Len() int {
|
func (self Nothing) Len() int {
|
||||||
|
|
|
@ -23,7 +23,7 @@ func TestNothingIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
p := Nothing{}
|
p := Nothing{}
|
||||||
index, segments := p.Index(test.fixture)
|
index, segments := p.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,21 @@ func TestNothingIndex(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkIndexNothing(b *testing.B) {
|
func BenchmarkIndexNothing(b *testing.B) {
|
||||||
m := Max{10}
|
m := Nothing{}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
m.Index(bench_pattern)
|
m.Index(bench_pattern, in[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexNothingParallel(b *testing.B) {
|
||||||
|
m := Nothing{}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ type Prefix struct {
|
||||||
Prefix string
|
Prefix string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Prefix) Index(s string) (int, []int) {
|
func (self Prefix) Index(s string, segments []int) (int, []int) {
|
||||||
idx := strings.Index(s, self.Prefix)
|
idx := strings.Index(s, self.Prefix)
|
||||||
if idx == -1 {
|
if idx == -1 {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
|
@ -24,7 +24,6 @@ func (self Prefix) Index(s string) (int, []int) {
|
||||||
sub = ""
|
sub = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
segments := make([]int, 0, utf8.RuneCountInString(sub)+1)
|
|
||||||
segments = append(segments, length)
|
segments = append(segments, length)
|
||||||
for i, r := range sub {
|
for i, r := range sub {
|
||||||
segments = append(segments, length+i+utf8.RuneLen(r))
|
segments = append(segments, length+i+utf8.RuneLen(r))
|
||||||
|
|
|
@ -9,17 +9,15 @@ type PrefixSuffix struct {
|
||||||
Prefix, Suffix string
|
Prefix, Suffix string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self PrefixSuffix) Index(s string) (int, []int) {
|
func (self PrefixSuffix) Index(s string, segments []int) (int, []int) {
|
||||||
prefixIdx := strings.Index(s, self.Prefix)
|
prefixIdx := strings.Index(s, self.Prefix)
|
||||||
if prefixIdx == -1 {
|
if prefixIdx == -1 {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp []int
|
|
||||||
suffixLen := len(self.Suffix)
|
suffixLen := len(self.Suffix)
|
||||||
|
|
||||||
if suffixLen > 0 {
|
if suffixLen > 0 {
|
||||||
var segments []int
|
|
||||||
for sub := s[prefixIdx:]; ; {
|
for sub := s[prefixIdx:]; ; {
|
||||||
suffixIdx := strings.LastIndex(sub, self.Suffix)
|
suffixIdx := strings.LastIndex(sub, self.Suffix)
|
||||||
if suffixIdx == -1 {
|
if suffixIdx == -1 {
|
||||||
|
@ -30,20 +28,16 @@ func (self PrefixSuffix) Index(s string) (int, []int) {
|
||||||
sub = sub[:suffixIdx]
|
sub = sub[:suffixIdx]
|
||||||
}
|
}
|
||||||
|
|
||||||
segLen := len(segments)
|
if len(segments) == 0 {
|
||||||
if segLen == 0 {
|
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
resp = make([]int, segLen)
|
reverseSegments(segments)
|
||||||
for i, s := range segments {
|
|
||||||
resp[segLen-i-1] = s
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
resp = append(resp, len(s)-prefixIdx)
|
segments = append(segments, len(s)-prefixIdx)
|
||||||
}
|
}
|
||||||
|
|
||||||
return prefixIdx, resp
|
return prefixIdx, segments
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self PrefixSuffix) Len() int {
|
func (self PrefixSuffix) Len() int {
|
||||||
|
|
|
@ -36,7 +36,7 @@ func TestPrefixSuffixIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
p := PrefixSuffix{test.prefix, test.suffix}
|
p := PrefixSuffix{test.prefix, test.suffix}
|
||||||
index, segments := p.Index(test.fixture)
|
index, segments := p.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,20 @@ func TestPrefixSuffixIndex(t *testing.T) {
|
||||||
|
|
||||||
func BenchmarkIndexPrefixSuffix(b *testing.B) {
|
func BenchmarkIndexPrefixSuffix(b *testing.B) {
|
||||||
m := PrefixSuffix{"qew", "sqw"}
|
m := PrefixSuffix{"qew", "sqw"}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
m.Index(bench_pattern)
|
m.Index(bench_pattern, in[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexPrefixSuffixParallel(b *testing.B) {
|
||||||
|
m := PrefixSuffix{"qew", "sqw"}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ func TestPrefixIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
p := Prefix{test.prefix}
|
p := Prefix{test.prefix}
|
||||||
index, segments := p.Index(test.fixture)
|
index, segments := p.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,20 @@ func TestPrefixIndex(t *testing.T) {
|
||||||
|
|
||||||
func BenchmarkIndexPrefix(b *testing.B) {
|
func BenchmarkIndexPrefix(b *testing.B) {
|
||||||
m := Prefix{"qew"}
|
m := Prefix{"qew"}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
m.Index(bench_pattern)
|
m.Index(bench_pattern, in[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexPrefixParallel(b *testing.B) {
|
||||||
|
m := Prefix{"qew"}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,9 @@ type Range struct {
|
||||||
Not bool
|
Not bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo make factory
|
||||||
|
// todo make range table inside factory
|
||||||
|
|
||||||
func (self Range) Len() int {
|
func (self Range) Len() int {
|
||||||
return lenOne
|
return lenOne
|
||||||
}
|
}
|
||||||
|
@ -25,10 +28,10 @@ func (self Range) Match(s string) bool {
|
||||||
return inRange == !self.Not
|
return inRange == !self.Not
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Range) Index(s string) (int, []int) {
|
func (self Range) Index(s string, segments []int) (int, []int) {
|
||||||
for i, r := range s {
|
for i, r := range s {
|
||||||
if self.Not != (r >= self.Lo && r <= self.Hi) {
|
if self.Not != (r >= self.Lo && r <= self.Hi) {
|
||||||
return i, []int{utf8.RuneLen(r)}
|
return i, append(segments, utf8.RuneLen(r))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ func TestRangeIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
m := Range{test.lo, test.hi, test.not}
|
m := Range{test.lo, test.hi, test.not}
|
||||||
index, segments := m.Index(test.fixture)
|
index, segments := m.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,20 @@ func TestRangeIndex(t *testing.T) {
|
||||||
|
|
||||||
func BenchmarkIndexRange(b *testing.B) {
|
func BenchmarkIndexRange(b *testing.B) {
|
||||||
m := Range{'0', '9', false}
|
m := Range{'0', '9', false}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
m.Index(bench_pattern)
|
m.Index(bench_pattern, in[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexRangeParallel(b *testing.B) {
|
||||||
|
m := Range{'0', '9', false}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ func (self Row) Len() (l int) {
|
||||||
return self.RunesLength
|
return self.RunesLength
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Row) Index(s string) (int, []int) {
|
func (self Row) Index(s string, segments []int) (int, []int) {
|
||||||
if !self.lenOk(s) {
|
if !self.lenOk(s) {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ func (self Row) Index(s string) (int, []int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.matchAll(s[i:]) {
|
if self.matchAll(s[i:]) {
|
||||||
return i, []int{self.RunesLength}
|
return i, append(segments, self.RunesLength)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,20 +5,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkRowIndex(b *testing.B) {
|
|
||||||
m := Row{
|
|
||||||
Matchers: Matchers{
|
|
||||||
NewText("abc"),
|
|
||||||
NewText("def"),
|
|
||||||
Single{},
|
|
||||||
},
|
|
||||||
RunesLength: 7,
|
|
||||||
}
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
m.Index("abcdefghijk")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRowIndex(t *testing.T) {
|
func TestRowIndex(t *testing.T) {
|
||||||
for id, test := range []struct {
|
for id, test := range []struct {
|
||||||
matchers Matchers
|
matchers Matchers
|
||||||
|
@ -54,7 +40,7 @@ func TestRowIndex(t *testing.T) {
|
||||||
Matchers: test.matchers,
|
Matchers: test.matchers,
|
||||||
RunesLength: test.length,
|
RunesLength: test.length,
|
||||||
}
|
}
|
||||||
index, segments := p.Index(test.fixture)
|
index, segments := p.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
@ -63,3 +49,37 @@ func TestRowIndex(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkRowIndex(b *testing.B) {
|
||||||
|
m := Row{
|
||||||
|
Matchers: Matchers{
|
||||||
|
NewText("abc"),
|
||||||
|
NewText("def"),
|
||||||
|
Single{},
|
||||||
|
},
|
||||||
|
RunesLength: 7,
|
||||||
|
}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexRowParallel(b *testing.B) {
|
||||||
|
m := Row{
|
||||||
|
Matchers: Matchers{
|
||||||
|
NewText("abc"),
|
||||||
|
NewText("def"),
|
||||||
|
Single{},
|
||||||
|
},
|
||||||
|
RunesLength: 7,
|
||||||
|
}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package match
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"github.com/gobwas/glob/runes"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,17 +17,17 @@ func (self Single) Match(s string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return strings.IndexRune(self.Separators, r) == -1
|
return runes.IndexRune(self.Separators, r) == -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Single) Len() int {
|
func (self Single) Len() int {
|
||||||
return lenOne
|
return lenOne
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Single) Index(s string) (int, []int) {
|
func (self Single) Index(s string, segments []int) (int, []int) {
|
||||||
for i, r := range s {
|
for i, r := range s {
|
||||||
if strings.IndexRune(self.Separators, r) == -1 {
|
if runes.IndexRune(self.Separators, r) == -1 {
|
||||||
return i, []int{utf8.RuneLen(r)}
|
return i, append(segments, utf8.RuneLen(r))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,26 +7,26 @@ import (
|
||||||
|
|
||||||
func TestSingleIndex(t *testing.T) {
|
func TestSingleIndex(t *testing.T) {
|
||||||
for id, test := range []struct {
|
for id, test := range []struct {
|
||||||
separators string
|
separators []rune
|
||||||
fixture string
|
fixture string
|
||||||
index int
|
index int
|
||||||
segments []int
|
segments []int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
".",
|
[]rune{'.'},
|
||||||
".abc",
|
".abc",
|
||||||
1,
|
1,
|
||||||
[]int{1},
|
[]int{1},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
".",
|
[]rune{'.'},
|
||||||
".",
|
".",
|
||||||
-1,
|
-1,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
p := Single{test.separators}
|
p := Single{test.separators}
|
||||||
index, segments := p.Index(test.fixture)
|
index, segments := p.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,20 @@ func TestSingleIndex(t *testing.T) {
|
||||||
|
|
||||||
func BenchmarkIndexSingle(b *testing.B) {
|
func BenchmarkIndexSingle(b *testing.B) {
|
||||||
m := Single{bench_separators}
|
m := Single{bench_separators}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
m.Index(bench_pattern)
|
m.Index(bench_pattern, in[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexSingleParallel(b *testing.B) {
|
||||||
|
m := Single{bench_separators}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -9,13 +9,13 @@ type Suffix struct {
|
||||||
Suffix string
|
Suffix string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Suffix) Index(s string) (int, []int) {
|
func (self Suffix) Index(s string, segments []int) (int, []int) {
|
||||||
idx := strings.Index(s, self.Suffix)
|
idx := strings.Index(s, self.Suffix)
|
||||||
if idx == -1 {
|
if idx == -1 {
|
||||||
return -1, nil
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0, []int{idx + len(self.Suffix)}
|
return 0, append(segments, idx+len(self.Suffix))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Suffix) Len() int {
|
func (self Suffix) Len() int {
|
||||||
|
|
|
@ -26,7 +26,7 @@ func TestSuffixIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
p := Suffix{test.prefix}
|
p := Suffix{test.prefix}
|
||||||
index, segments := p.Index(test.fixture)
|
index, segments := p.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,20 @@ func TestSuffixIndex(t *testing.T) {
|
||||||
|
|
||||||
func BenchmarkIndexSuffix(b *testing.B) {
|
func BenchmarkIndexSuffix(b *testing.B) {
|
||||||
m := Suffix{"qwe"}
|
m := Suffix{"qwe"}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
m.Index(bench_pattern)
|
m.Index(bench_pattern, in[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexSuffixParallel(b *testing.B) {
|
||||||
|
m := Suffix{"qwe"}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package match
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"unicode/utf8"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Super struct{}
|
type Super struct{}
|
||||||
|
@ -15,12 +14,10 @@ func (self Super) Len() int {
|
||||||
return lenNo
|
return lenNo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Super) Index(s string) (int, []int) {
|
func (self Super) Index(s string, segments []int) (int, []int) {
|
||||||
segments := make([]int, 0, utf8.RuneCountInString(s)+1)
|
|
||||||
for i := range s {
|
for i := range s {
|
||||||
segments = append(segments, i)
|
segments = append(segments, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
segments = append(segments, len(s))
|
segments = append(segments, len(s))
|
||||||
|
|
||||||
return 0, segments
|
return 0, segments
|
||||||
|
|
|
@ -23,7 +23,7 @@ func TestSuperIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
p := Super{}
|
p := Super{}
|
||||||
index, segments := p.Index(test.fixture)
|
index, segments := p.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,20 @@ func TestSuperIndex(t *testing.T) {
|
||||||
|
|
||||||
func BenchmarkIndexSuper(b *testing.B) {
|
func BenchmarkIndexSuper(b *testing.B) {
|
||||||
m := Super{}
|
m := Super{}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
m.Index(bench_pattern)
|
m.Index(bench_pattern, in[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexSuperParallel(b *testing.B) {
|
||||||
|
m := Super{}
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -29,15 +29,13 @@ func (self Text) Len() int {
|
||||||
return self.RunesLength
|
return self.RunesLength
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Text) Index(s string) (index int, segments []int) {
|
func (self Text) Index(s string, segments []int) (int, []int) {
|
||||||
index = strings.Index(s, self.Str)
|
index := strings.Index(s, self.Str)
|
||||||
if index == -1 {
|
if index == -1 {
|
||||||
return
|
return -1, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
segments = []int{self.BytesLength}
|
return index, append(segments, self.BytesLength)
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self Text) String() string {
|
func (self Text) String() string {
|
||||||
|
|
|
@ -26,7 +26,7 @@ func TestTextIndex(t *testing.T) {
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
m := NewText(test.text)
|
m := NewText(test.text)
|
||||||
index, segments := m.Index(test.fixture)
|
index, segments := m.Index(test.fixture, []int{})
|
||||||
if index != test.index {
|
if index != test.index {
|
||||||
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
t.Errorf("#%d unexpected index: exp: %d, act: %d", id, test.index, index)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,20 @@ func TestTextIndex(t *testing.T) {
|
||||||
|
|
||||||
func BenchmarkIndexText(b *testing.B) {
|
func BenchmarkIndexText(b *testing.B) {
|
||||||
m := NewText("foo")
|
m := NewText("foo")
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
m.Index(bench_pattern)
|
m.Index(bench_pattern, in[:0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkIndexTextParallel(b *testing.B) {
|
||||||
|
m := NewText("foo")
|
||||||
|
in := acquireSegments(len(bench_pattern))
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
m.Index(bench_pattern, in[:0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
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 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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
27
todo.txt
27
todo.txt
|
@ -11,17 +11,22 @@ BenchmarkPlainGlobMatch-4 7.20 154 +20
|
||||||
BenchmarkPrefixGlobMatch-4 8.75 113 +1191.43%
|
BenchmarkPrefixGlobMatch-4 8.75 113 +1191.43%
|
||||||
BenchmarkSuffixGlobMatch-4 9.07 115 +1167.92%
|
BenchmarkSuffixGlobMatch-4 9.07 115 +1167.92%
|
||||||
BenchmarkPrefixSuffixGlobMatch-4 15.1 125 +727.81%
|
BenchmarkPrefixSuffixGlobMatch-4 15.1 125 +727.81%
|
||||||
BenchmarkIndexAny-4 887 255 -71.25%
|
|
||||||
BenchmarkIndexContains-4 492 247 -49.80%
|
|
||||||
BenchmarkIndexList-4 151 51.1 -66.16%
|
|
||||||
BenchmarkIndexMax-4 442 92.4 -79.10%
|
|
||||||
BenchmarkIndexMin-4 516 161 -68.80%
|
|
||||||
BenchmarkIndexNothing-4 452 92.8 -79.47%
|
|
||||||
BenchmarkIndexPrefixSuffix-4 84.3 57.2 -32.15%
|
|
||||||
BenchmarkIndexPrefix-4 85.1 55.9 -34.31%
|
BenchmarkIndexPrefix-4 85.1 55.9 -34.31%
|
||||||
BenchmarkIndexRange-4 170 60.6 -64.35%
|
BenchmarkIndexRange-4 170(143) 60.6 -64.35%
|
||||||
BenchmarkRowIndex-4 172 94.0 -45.35%
|
BenchmarkRowIndex-4 172(128) 94.0 -45.35%
|
||||||
BenchmarkIndexSingle-4 61.0 35.8 -41.31%
|
BenchmarkIndexSingle-4 61.0(16) 35.8 -41.31%
|
||||||
BenchmarkIndexSuffix-4 84.8 55.7 -34.32%
|
BenchmarkIndexSuffix-4 84.8 55.7 -34.32%
|
||||||
BenchmarkIndexSuper-4 461 192 -58.35%
|
BenchmarkIndexSuper-4 461(180) 192 -58.35%
|
||||||
BenchmarkIndexText-4 84.6 54.4 -35.70%
|
BenchmarkIndexText-4 84.6 54.4 -35.70%
|
||||||
|
BenchmarkIndexPrefixSuffix-4 84.3 57.2 -32.15%
|
||||||
|
BenchmarkIndexNothing-4 452(3.31) 92.8 -79.47% XXX
|
||||||
|
BenchmarkIndexMin-4 516(274) 161 -68.80%
|
||||||
|
BenchmarkIndexMax-4 442(88) 92.4 -79.10%
|
||||||
|
BenchmarkIndexList-4 151(41) 51.1 -66.16%
|
||||||
|
BenchmarkIndexContains-4 492(220) 247 -49.80%
|
||||||
|
BenchmarkIndexAny-4 887(222) 255 -71.25%
|
||||||
|
|
Loading…
Reference in New Issue