aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/strings
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2018-09-24 21:46:21 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2018-09-24 21:46:21 +0000
commitdd931d9b48647e898dc80927c532ae93cc09e192 (patch)
tree71be2295cd79b8a182f6130611658db8628772d5 /libgo/go/strings
parent779d8a5ad09b01428726ea5a0e6c87bd9ac3c0e4 (diff)
downloadgcc-dd931d9b48647e898dc80927c532ae93cc09e192.zip
gcc-dd931d9b48647e898dc80927c532ae93cc09e192.tar.gz
gcc-dd931d9b48647e898dc80927c532ae93cc09e192.tar.bz2
libgo: update to Go 1.11
Reviewed-on: https://go-review.googlesource.com/136435 gotools/: * Makefile.am (mostlyclean-local): Run chmod on check-go-dir to make sure it is writable. (check-go-tools): Likewise. (check-vet): Copy internal/objabi to check-vet-dir. * Makefile.in: Rebuild. From-SVN: r264546
Diffstat (limited to 'libgo/go/strings')
-rw-r--r--libgo/go/strings/builder_test.go92
-rw-r--r--libgo/go/strings/compare_test.go26
-rw-r--r--libgo/go/strings/replace.go77
-rw-r--r--libgo/go/strings/search.go4
-rw-r--r--libgo/go/strings/search_test.go2
-rw-r--r--libgo/go/strings/strings.go98
-rw-r--r--libgo/go/strings/strings_amd64.go90
-rw-r--r--libgo/go/strings/strings_decl.go2
-rw-r--r--libgo/go/strings/strings_generic.go61
-rw-r--r--libgo/go/strings/strings_s390x.go88
-rw-r--r--libgo/go/strings/strings_test.go12
11 files changed, 256 insertions, 296 deletions
diff --git a/libgo/go/strings/builder_test.go b/libgo/go/strings/builder_test.go
index ad012bb..da6c021 100644
--- a/libgo/go/strings/builder_test.go
+++ b/libgo/go/strings/builder_test.go
@@ -85,16 +85,25 @@ func TestBuilderReset(t *testing.T) {
}
func TestBuilderGrow(t *testing.T) {
+ if runtime.Compiler == "gccgo" {
+ t.Skip("skip for gccgo until escape analysis improves")
+ }
for _, growLen := range []int{0, 100, 1000, 10000, 100000} {
- var b Builder
- b.Grow(growLen)
p := bytes.Repeat([]byte{'a'}, growLen)
- allocs := numAllocs(func() { b.Write(p) })
- if allocs > 0 {
- t.Errorf("growLen=%d: allocation occurred during write", growLen)
+ allocs := testing.AllocsPerRun(100, func() {
+ var b Builder
+ b.Grow(growLen) // should be only alloc, when growLen > 0
+ b.Write(p)
+ if b.String() != string(p) {
+ t.Fatalf("growLen=%d: bad data written after Grow", growLen)
+ }
+ })
+ wantAllocs := 1
+ if growLen == 0 {
+ wantAllocs = 0
}
- if b.String() != string(p) {
- t.Errorf("growLen=%d: bad data written after Grow", growLen)
+ if g, w := int(allocs), wantAllocs; g != w {
+ t.Errorf("growLen=%d: got %d allocs during Write; want %v", growLen, g, w)
}
}
}
@@ -168,16 +177,17 @@ func TestBuilderWriteByte(t *testing.T) {
func TestBuilderAllocs(t *testing.T) {
if runtime.Compiler == "gccgo" {
- t.Skip("skip for gccgo until escape analysis enabled by default")
+ t.Skip("skip for gccgo until escape analysis improves")
}
var b Builder
- b.Grow(5)
+ const msg = "hello"
+ b.Grow(len(msg) * 2) // because AllocsPerRun does an extra "warm-up" iteration
var s string
- allocs := numAllocs(func() {
+ allocs := int(testing.AllocsPerRun(1, func() {
b.WriteString("hello")
s = b.String()
- })
- if want := "hello"; s != want {
+ }))
+ if want := msg + msg; s != want {
t.Errorf("String: got %#q; want %#q", s, want)
}
if allocs > 0 {
@@ -197,15 +207,6 @@ func TestBuilderAllocs(t *testing.T) {
}
}
-func numAllocs(fn func()) uint64 {
- defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
- var m1, m2 runtime.MemStats
- runtime.ReadMemStats(&m1)
- fn()
- runtime.ReadMemStats(&m2)
- return m2.Mallocs - m1.Mallocs
-}
-
func TestBuilderCopyPanic(t *testing.T) {
tests := []struct {
name string
@@ -305,3 +306,52 @@ func TestBuilderCopyPanic(t *testing.T) {
}
}
}
+
+var someBytes = []byte("some bytes sdljlk jsklj3lkjlk djlkjw")
+
+var sinkS string
+
+func benchmarkBuilder(b *testing.B, f func(b *testing.B, numWrite int, grow bool)) {
+ b.Run("1Write_NoGrow", func(b *testing.B) {
+ b.ReportAllocs()
+ f(b, 1, false)
+ })
+ b.Run("3Write_NoGrow", func(b *testing.B) {
+ b.ReportAllocs()
+ f(b, 3, false)
+ })
+ b.Run("3Write_Grow", func(b *testing.B) {
+ b.ReportAllocs()
+ f(b, 3, true)
+ })
+}
+
+func BenchmarkBuildString_Builder(b *testing.B) {
+ benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) {
+ for i := 0; i < b.N; i++ {
+ var buf Builder
+ if grow {
+ buf.Grow(len(someBytes) * numWrite)
+ }
+ for i := 0; i < numWrite; i++ {
+ buf.Write(someBytes)
+ }
+ sinkS = buf.String()
+ }
+ })
+}
+
+func BenchmarkBuildString_ByteBuffer(b *testing.B) {
+ benchmarkBuilder(b, func(b *testing.B, numWrite int, grow bool) {
+ for i := 0; i < b.N; i++ {
+ var buf bytes.Buffer
+ if grow {
+ buf.Grow(len(someBytes) * numWrite)
+ }
+ for i := 0; i < numWrite; i++ {
+ buf.Write(someBytes)
+ }
+ sinkS = buf.String()
+ }
+ })
+}
diff --git a/libgo/go/strings/compare_test.go b/libgo/go/strings/compare_test.go
index bc12e42..5d53344 100644
--- a/libgo/go/strings/compare_test.go
+++ b/libgo/go/strings/compare_test.go
@@ -8,6 +8,7 @@ package strings_test
// Benchmarks omitted since the underlying implementation is identical.
import (
+ "internal/testenv"
. "strings"
"testing"
)
@@ -52,10 +53,21 @@ func TestCompareIdenticalString(t *testing.T) {
}
func TestCompareStrings(t *testing.T) {
- n := 128
+ lengths := make([]int, 0) // lengths to test in ascending order
+ for i := 0; i <= 128; i++ {
+ lengths = append(lengths, i)
+ }
+ lengths = append(lengths, 256, 512, 1024, 1333, 4095, 4096, 4097)
+
+ if !testing.Short() || testenv.Builder() != "" {
+ lengths = append(lengths, 65535, 65536, 65537, 99999)
+ }
+
+ n := lengths[len(lengths)-1]
a := make([]byte, n+1)
b := make([]byte, n+1)
- for len := 0; len < 128; len++ {
+ lastLen := 0
+ for _, len := range lengths {
// randomish but deterministic data. No 0 or 255.
for i := 0; i < len; i++ {
a[i] = byte(1 + 31*i%254)
@@ -67,21 +79,22 @@ func TestCompareStrings(t *testing.T) {
b[i] = 9
}
- cmp := Compare(string(a[:len]), string(b[:len]))
+ sa, sb := string(a), string(b)
+ cmp := Compare(sa[:len], sb[:len])
if cmp != 0 {
t.Errorf(`CompareIdentical(%d) = %d`, len, cmp)
}
if len > 0 {
- cmp = Compare(string(a[:len-1]), string(b[:len]))
+ cmp = Compare(sa[:len-1], sb[:len])
if cmp != -1 {
t.Errorf(`CompareAshorter(%d) = %d`, len, cmp)
}
- cmp = Compare(string(a[:len]), string(b[:len-1]))
+ cmp = Compare(sa[:len], sb[:len-1])
if cmp != 1 {
t.Errorf(`CompareBshorter(%d) = %d`, len, cmp)
}
}
- for k := 0; k < len; k++ {
+ for k := lastLen; k < len; k++ {
b[k] = a[k] - 1
cmp = Compare(string(a[:len]), string(b[:len]))
if cmp != 1 {
@@ -94,5 +107,6 @@ func TestCompareStrings(t *testing.T) {
}
b[k] = a[k]
}
+ lastLen = len
}
}
diff --git a/libgo/go/strings/replace.go b/libgo/go/strings/replace.go
index 4752641..58a11a6 100644
--- a/libgo/go/strings/replace.go
+++ b/libgo/go/strings/replace.go
@@ -18,8 +18,9 @@ type replacer interface {
WriteString(w io.Writer, s string) (n int, err error)
}
-// NewReplacer returns a new Replacer from a list of old, new string pairs.
-// Replacements are performed in order, without overlapping matches.
+// NewReplacer returns a new Replacer from a list of old, new string
+// pairs. Replacements are performed in the order they appear in the
+// target string, without overlapping matches.
func NewReplacer(oldnew ...string) *Replacer {
if len(oldnew)%2 == 1 {
panic("strings.NewReplacer: odd argument count")
@@ -54,13 +55,21 @@ func NewReplacer(oldnew ...string) *Replacer {
return &Replacer{r: &r}
}
- r := byteStringReplacer{}
+ r := byteStringReplacer{toReplace: make([]string, 0, len(oldnew)/2)}
// The first occurrence of old->new map takes precedence
// over the others with the same old string.
for i := len(oldnew) - 2; i >= 0; i -= 2 {
o := oldnew[i][0]
n := oldnew[i+1]
- r[o] = []byte(n)
+ // To avoid counting repetitions multiple times.
+ if r.replacements[o] == nil {
+ // We need to use string([]byte{o}) instead of string(o),
+ // to avoid utf8 encoding of o.
+ // E. g. byte(150) produces string of length 2.
+ r.toReplace = append(r.toReplace, string([]byte{o}))
+ }
+ r.replacements[o] = []byte(n)
+
}
return &Replacer{r: &r}
}
@@ -454,34 +463,60 @@ func (r *byteReplacer) WriteString(w io.Writer, s string) (n int, err error) {
// byteStringReplacer is the implementation that's used when all the
// "old" values are single ASCII bytes but the "new" values vary in size.
-// The array contains replacement byte slices indexed by old byte.
-// A nil []byte means that the old byte should not be replaced.
-type byteStringReplacer [256][]byte
+type byteStringReplacer struct {
+ // replacements contains replacement byte slices indexed by old byte.
+ // A nil []byte means that the old byte should not be replaced.
+ replacements [256][]byte
+ // toReplace keeps a list of bytes to replace. Depending on length of toReplace
+ // and length of target string it may be faster to use Count, or a plain loop.
+ // We store single byte as a string, because Count takes a string.
+ toReplace []string
+}
+
+// countCutOff controls the ratio of a string length to a number of replacements
+// at which (*byteStringReplacer).Replace switches algorithms.
+// For strings with higher ration of length to replacements than that value,
+// we call Count, for each replacement from toReplace.
+// For strings, with a lower ratio we use simple loop, because of Count overhead.
+// countCutOff is an empirically determined overhead multiplier.
+// TODO(tocarip) revisit once we have register-based abi/mid-stack inlining.
+const countCutOff = 8
func (r *byteStringReplacer) Replace(s string) string {
newSize := len(s)
anyChanges := false
- for i := 0; i < len(s); i++ {
- b := s[i]
- if r[b] != nil {
- anyChanges = true
- // The -1 is because we are replacing 1 byte with len(r[b]) bytes.
- newSize += len(r[b]) - 1
+ // Is it faster to use Count?
+ if len(r.toReplace)*countCutOff <= len(s) {
+ for _, x := range r.toReplace {
+ if c := Count(s, x); c != 0 {
+ // The -1 is because we are replacing 1 byte with len(replacements[b]) bytes.
+ newSize += c * (len(r.replacements[x[0]]) - 1)
+ anyChanges = true
+ }
+
+ }
+ } else {
+ for i := 0; i < len(s); i++ {
+ b := s[i]
+ if r.replacements[b] != nil {
+ // See above for explanation of -1
+ newSize += len(r.replacements[b]) - 1
+ anyChanges = true
+ }
}
}
if !anyChanges {
return s
}
buf := make([]byte, newSize)
- bi := buf
+ j := 0
for i := 0; i < len(s); i++ {
b := s[i]
- if r[b] != nil {
- n := copy(bi, r[b])
- bi = bi[n:]
+ if r.replacements[b] != nil {
+ j += copy(buf[j:], r.replacements[b])
} else {
- bi[0] = b
- bi = bi[1:]
+ buf[j] = b
+ j++
}
}
return string(buf)
@@ -492,7 +527,7 @@ func (r *byteStringReplacer) WriteString(w io.Writer, s string) (n int, err erro
last := 0
for i := 0; i < len(s); i++ {
b := s[i]
- if r[b] == nil {
+ if r.replacements[b] == nil {
continue
}
if last != i {
@@ -503,7 +538,7 @@ func (r *byteStringReplacer) WriteString(w io.Writer, s string) (n int, err erro
}
}
last = i + 1
- nw, err := w.Write(r[b])
+ nw, err := w.Write(r.replacements[b])
n += nw
if err != nil {
return n, err
diff --git a/libgo/go/strings/search.go b/libgo/go/strings/search.go
index f77c879..e5bffbb 100644
--- a/libgo/go/strings/search.go
+++ b/libgo/go/strings/search.go
@@ -6,8 +6,8 @@ package strings
// stringFinder efficiently finds strings in a source text. It's implemented
// using the Boyer-Moore string search algorithm:
-// http://en.wikipedia.org/wiki/Boyer-Moore_string_search_algorithm
-// http://www.cs.utexas.edu/~moore/publications/fstrpos.pdf (note: this aged
+// https://en.wikipedia.org/wiki/Boyer-Moore_string_search_algorithm
+// https://www.cs.utexas.edu/~moore/publications/fstrpos.pdf (note: this aged
// document uses 1-based indexing)
type stringFinder struct {
// pattern is the string that we are searching for in the text.
diff --git a/libgo/go/strings/search_test.go b/libgo/go/strings/search_test.go
index 966c05e..c01a393 100644
--- a/libgo/go/strings/search_test.go
+++ b/libgo/go/strings/search_test.go
@@ -57,7 +57,7 @@ func TestFinderCreation(t *testing.T) {
[256]int{'i': 3, 'm': 7, 's': 1},
[]int{15, 14, 13, 7, 11, 10, 7, 1},
},
- // From http://www.cs.utexas.edu/~moore/publications/fstrpos.pdf
+ // From https://www.cs.utexas.edu/~moore/publications/fstrpos.pdf
{
"abcxxxabc",
[256]int{'a': 2, 'b': 1, 'c': 6, 'x': 3},
diff --git a/libgo/go/strings/strings.go b/libgo/go/strings/strings.go
index 05e8243..20868be 100644
--- a/libgo/go/strings/strings.go
+++ b/libgo/go/strings/strings.go
@@ -8,6 +8,7 @@
package strings
import (
+ "internal/bytealg"
"unicode"
"unicode/utf8"
)
@@ -72,12 +73,16 @@ func hashStrRev(sep string) (uint32, uint32) {
return hash, pow
}
-// countGeneric implements Count.
-func countGeneric(s, substr string) int {
+// Count counts the number of non-overlapping instances of substr in s.
+// If substr is an empty string, Count returns 1 + the number of Unicode code points in s.
+func Count(s, substr string) int {
// special case
if len(substr) == 0 {
return utf8.RuneCountInString(s) + 1
}
+ if len(substr) == 1 {
+ return bytealg.CountString(s, substr[0])
+ }
n := 0
for {
i := Index(s, substr)
@@ -792,6 +797,8 @@ func Trim(s string, cutset string) string {
// TrimLeft returns a slice of the string s with all leading
// Unicode code points contained in cutset removed.
+//
+// To remove a prefix, use TrimPrefix instead.
func TrimLeft(s string, cutset string) string {
if s == "" || cutset == "" {
return s
@@ -801,6 +808,8 @@ func TrimLeft(s string, cutset string) string {
// TrimRight returns a slice of the string s, with all trailing
// Unicode code points contained in cutset removed.
+//
+// To remove a suffix, use TrimSuffix instead.
func TrimRight(s string, cutset string) string {
if s == "" || cutset == "" {
return s
@@ -903,9 +912,9 @@ func EqualFold(s, t string) bool {
tr, sr = sr, tr
}
// Fast check for ASCII.
- if tr < utf8.RuneSelf && 'A' <= sr && sr <= 'Z' {
- // ASCII, and sr is upper case. tr must be lower case.
- if tr == sr+'a'-'A' {
+ if tr < utf8.RuneSelf {
+ // ASCII only, sr/tr must be upper/lower case
+ if 'A' <= sr && sr <= 'Z' && tr == sr+'a'-'A' {
continue
}
return false
@@ -927,6 +936,85 @@ func EqualFold(s, t string) bool {
return s == t
}
+// Index returns the index of the first instance of substr in s, or -1 if substr is not present in s.
+func Index(s, substr string) int {
+ n := len(substr)
+ switch {
+ case n == 0:
+ return 0
+ case n == 1:
+ return IndexByte(s, substr[0])
+ case n == len(s):
+ if substr == s {
+ return 0
+ }
+ return -1
+ case n > len(s):
+ return -1
+ case n <= bytealg.MaxLen:
+ // Use brute force when s and substr both are small
+ if len(s) <= bytealg.MaxBruteForce {
+ return bytealg.IndexString(s, substr)
+ }
+ c := substr[0]
+ i := 0
+ t := s[:len(s)-n+1]
+ fails := 0
+ for i < len(t) {
+ if t[i] != c {
+ // IndexByte is faster than bytealg.IndexString, so use it as long as
+ // we're not getting lots of false positives.
+ o := IndexByte(t[i:], c)
+ if o < 0 {
+ return -1
+ }
+ i += o
+ }
+ if s[i:i+n] == substr {
+ return i
+ }
+ fails++
+ i++
+ // Switch to bytealg.IndexString when IndexByte produces too many false positives.
+ if fails > bytealg.Cutover(i) {
+ r := bytealg.IndexString(s[i:], substr)
+ if r >= 0 {
+ return r + i
+ }
+ return -1
+ }
+ }
+ return -1
+ }
+ c := substr[0]
+ i := 0
+ t := s[:len(s)-n+1]
+ fails := 0
+ for i < len(t) {
+ if t[i] != c {
+ o := IndexByte(t[i:], c)
+ if o < 0 {
+ return -1
+ }
+ i += o
+ }
+ if s[i:i+n] == substr {
+ return i
+ }
+ i++
+ fails++
+ if fails >= 4+i>>4 && i < len(t) {
+ // See comment in ../bytes/bytes_generic.go.
+ j := indexRabinKarp(s[i:], substr)
+ if j < 0 {
+ return -1
+ }
+ return i + j
+ }
+ }
+ return -1
+}
+
func indexRabinKarp(s, substr string) int {
// Rabin-Karp search
hashss, pow := hashStr(substr)
diff --git a/libgo/go/strings/strings_amd64.go b/libgo/go/strings/strings_amd64.go
deleted file mode 100644
index 2441569..0000000
--- a/libgo/go/strings/strings_amd64.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build ignore
-
-package strings
-
-import "internal/cpu"
-
-//go:noescape
-
-// indexShortStr returns the index of the first instance of c in s, or -1 if c is not present in s.
-// indexShortStr requires 2 <= len(c) <= shortStringLen
-func indexShortStr(s, c string) int // ../runtime/asm_amd64.s
-func countByte(s string, c byte) int // ../runtime/asm_amd64.s
-
-var shortStringLen int
-
-func init() {
- if cpu.X86.HasAVX2 {
- shortStringLen = 63
- } else {
- shortStringLen = 31
- }
-}
-
-// Index returns the index of the first instance of substr in s, or -1 if substr is not present in s.
-func Index(s, substr string) int {
- n := len(substr)
- switch {
- case n == 0:
- return 0
- case n == 1:
- return IndexByte(s, substr[0])
- case n == len(s):
- if substr == s {
- return 0
- }
- return -1
- case n > len(s):
- return -1
- case n <= shortStringLen:
- // Use brute force when s and substr both are small
- if len(s) <= 64 {
- return indexShortStr(s, substr)
- }
- c := substr[0]
- i := 0
- t := s[:len(s)-n+1]
- fails := 0
- for i < len(t) {
- if t[i] != c {
- // IndexByte skips 16/32 bytes per iteration,
- // so it's faster than indexShortStr.
- o := IndexByte(t[i:], c)
- if o < 0 {
- return -1
- }
- i += o
- }
- if s[i:i+n] == substr {
- return i
- }
- fails++
- i++
- // Switch to indexShortStr when IndexByte produces too many false positives.
- // Too many means more that 1 error per 8 characters.
- // Allow some errors in the beginning.
- if fails > (i+16)/8 {
- r := indexShortStr(s[i:], substr)
- if r >= 0 {
- return r + i
- }
- return -1
- }
- }
- return -1
- }
- return indexRabinKarp(s, substr)
-}
-
-// Count counts the number of non-overlapping instances of substr in s.
-// If substr is an empty string, Count returns 1 + the number of Unicode code points in s.
-func Count(s, substr string) int {
- if len(substr) == 1 && cpu.X86.HasPOPCNT {
- return countByte(s, byte(substr[0]))
- }
- return countGeneric(s, substr)
-}
diff --git a/libgo/go/strings/strings_decl.go b/libgo/go/strings/strings_decl.go
index 3bae844..9819444 100644
--- a/libgo/go/strings/strings_decl.go
+++ b/libgo/go/strings/strings_decl.go
@@ -5,4 +5,4 @@
package strings
// IndexByte returns the index of the first instance of c in s, or -1 if c is not present in s.
-func IndexByte(s string, c byte) int // ../runtime/asm_$GOARCH.s
+func IndexByte(s string, c byte) int // in internal/bytealg
diff --git a/libgo/go/strings/strings_generic.go b/libgo/go/strings/strings_generic.go
deleted file mode 100644
index 60a6f78..0000000
--- a/libgo/go/strings/strings_generic.go
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// -build !amd64,!s390x
-
-package strings
-
-// TODO: implements short string optimization on non amd64 platforms
-// and get rid of strings_amd64.go
-
-// Index returns the index of the first instance of substr in s, or -1 if substr is not present in s.
-func Index(s, substr string) int {
- n := len(substr)
- switch {
- case n == 0:
- return 0
- case n == 1:
- return IndexByte(s, substr[0])
- case n == len(s):
- if substr == s {
- return 0
- }
- return -1
- case n > len(s):
- return -1
- }
- c := substr[0]
- i := 0
- t := s[:len(s)-n+1]
- fails := 0
- for i < len(t) {
- if t[i] != c {
- o := IndexByte(t[i:], c)
- if o < 0 {
- return -1
- }
- i += o
- }
- if s[i:i+n] == substr {
- return i
- }
- i++
- fails++
- if fails >= 4+i>>4 && i < len(t) {
- // See comment in ../bytes/bytes_generic.go.
- j := indexRabinKarp(s[i:], substr)
- if j < 0 {
- return -1
- }
- return i + j
- }
- }
- return -1
-}
-
-// Count counts the number of non-overlapping instances of substr in s.
-// If substr is an empty string, Count returns 1 + the number of Unicode code points in s.
-func Count(s, substr string) int {
- return countGeneric(s, substr)
-}
diff --git a/libgo/go/strings/strings_s390x.go b/libgo/go/strings/strings_s390x.go
deleted file mode 100644
index e74e4cd..0000000
--- a/libgo/go/strings/strings_s390x.go
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build ignore
-
-package strings
-
-//go:noescape
-
-// indexShortStr returns the index of the first instance of sep in s,
-// or -1 if sep is not present in s.
-// indexShortStr requires 2 <= len(sep) <= shortStringLen
-func indexShortStr(s, sep string) int // ../runtime/asm_$GOARCH.s
-
-// supportsVX reports whether the vector facility is available.
-// indexShortStr must not be called if the vector facility is not
-// available.
-func supportsVX() bool // ../runtime/asm_s390x.s
-
-var shortStringLen = -1
-
-func init() {
- if supportsVX() {
- shortStringLen = 64
- }
-}
-
-// Index returns the index of the first instance of substr in s, or -1 if substr is not present in s.
-func Index(s, substr string) int {
- n := len(substr)
- switch {
- case n == 0:
- return 0
- case n == 1:
- return IndexByte(s, substr[0])
- case n == len(s):
- if substr == s {
- return 0
- }
- return -1
- case n > len(s):
- return -1
- case n <= shortStringLen:
- // Use brute force when s and substr both are small
- if len(s) <= 64 {
- return indexShortStr(s, substr)
- }
- c := substr[0]
- i := 0
- t := s[:len(s)-n+1]
- fails := 0
- for i < len(t) {
- if t[i] != c {
- // IndexByte skips 16/32 bytes per iteration,
- // so it's faster than indexShortStr.
- o := IndexByte(t[i:], c)
- if o < 0 {
- return -1
- }
- i += o
- }
- if s[i:i+n] == substr {
- return i
- }
- fails++
- i++
- // Switch to indexShortStr when IndexByte produces too many false positives.
- // Too many means more that 1 error per 8 characters.
- // Allow some errors in the beginning.
- if fails > (i+16)/8 {
- r := indexShortStr(s[i:], substr)
- if r >= 0 {
- return r + i
- }
- return -1
- }
- }
- return -1
- }
- return indexRabinKarp(s, substr)
-}
-
-// Count counts the number of non-overlapping instances of substr in s.
-// If substr is an empty string, Count returns 1 + the number of Unicode code points in s.
-func Count(s, substr string) int {
- return countGeneric(s, substr)
-}
diff --git a/libgo/go/strings/strings_test.go b/libgo/go/strings/strings_test.go
index 92122db..3f0c790 100644
--- a/libgo/go/strings/strings_test.go
+++ b/libgo/go/strings/strings_test.go
@@ -1406,6 +1406,8 @@ var EqualFoldTests = []struct {
{"abcdefghijK", "abcdefghij\u212A", true},
{"abcdefghijkz", "abcdefghij\u212Ay", false},
{"abcdefghijKz", "abcdefghij\u212Ay", false},
+ {"1", "2", false},
+ {"utf-8", "US-ASCII", false},
}
func TestEqualFold(t *testing.T) {
@@ -1419,6 +1421,16 @@ func TestEqualFold(t *testing.T) {
}
}
+func BenchmarkEqualFold(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ for _, tt := range EqualFoldTests {
+ if out := EqualFold(tt.s, tt.t); out != tt.out {
+ b.Fatal("wrong result")
+ }
+ }
+ }
+}
+
var CountTests = []struct {
s, sep string
num int