aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/strings
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2022-02-11 14:53:56 -0800
committerIan Lance Taylor <iant@golang.org>2022-02-11 15:01:19 -0800
commit8dc2499aa62f768c6395c9754b8cabc1ce25c494 (patch)
tree43d7fd2bbfd7ad8c9625a718a5e8718889351994 /libgo/go/strings
parent9a56779dbc4e2d9c15be8d31e36f2f59be7331a8 (diff)
downloadgcc-8dc2499aa62f768c6395c9754b8cabc1ce25c494.zip
gcc-8dc2499aa62f768c6395c9754b8cabc1ce25c494.tar.gz
gcc-8dc2499aa62f768c6395c9754b8cabc1ce25c494.tar.bz2
libgo: update to Go1.18beta2
gotools/ * Makefile.am (go_cmd_cgo_files): Add ast_go118.go (check-go-tool): Copy golang.org/x/tools directories. * Makefile.in: Regenerate. Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/384695
Diffstat (limited to 'libgo/go/strings')
-rw-r--r--libgo/go/strings/clone.go28
-rw-r--r--libgo/go/strings/clone_test.go49
-rw-r--r--libgo/go/strings/compare.go2
-rw-r--r--libgo/go/strings/example_test.go58
-rw-r--r--libgo/go/strings/export_test.go2
-rw-r--r--libgo/go/strings/reader_test.go2
-rw-r--r--libgo/go/strings/replace.go11
-rw-r--r--libgo/go/strings/strings.go120
-rw-r--r--libgo/go/strings/strings_test.go34
9 files changed, 256 insertions, 50 deletions
diff --git a/libgo/go/strings/clone.go b/libgo/go/strings/clone.go
new file mode 100644
index 0000000..edd1497
--- /dev/null
+++ b/libgo/go/strings/clone.go
@@ -0,0 +1,28 @@
+// Copyright 2021 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.
+
+package strings
+
+import (
+ "unsafe"
+)
+
+// Clone returns a fresh copy of s.
+// It guarantees to make a copy of s into a new allocation,
+// which can be important when retaining only a small substring
+// of a much larger string. Using Clone can help such programs
+// use less memory. Of course, since using Clone makes a copy,
+// overuse of Clone can make programs use more memory.
+// Clone should typically be used only rarely, and only when
+// profiling indicates that it is needed.
+// For strings of length zero the string "" will be returned
+// and no allocation is made.
+func Clone(s string) string {
+ if len(s) == 0 {
+ return ""
+ }
+ b := make([]byte, len(s))
+ copy(b, s)
+ return *(*string)(unsafe.Pointer(&b))
+}
diff --git a/libgo/go/strings/clone_test.go b/libgo/go/strings/clone_test.go
new file mode 100644
index 0000000..a9ba8ad
--- /dev/null
+++ b/libgo/go/strings/clone_test.go
@@ -0,0 +1,49 @@
+// Copyright 2021 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.ß
+
+package strings_test
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+ "unsafe"
+)
+
+var emptyString string
+
+func TestClone(t *testing.T) {
+ var cloneTests = []string{
+ "",
+ strings.Clone(""),
+ strings.Repeat("a", 42)[:0],
+ "short",
+ strings.Repeat("a", 42),
+ }
+ for _, input := range cloneTests {
+ clone := strings.Clone(input)
+ if clone != input {
+ t.Errorf("Clone(%q) = %q; want %q", input, clone, input)
+ }
+
+ inputHeader := (*reflect.StringHeader)(unsafe.Pointer(&input))
+ cloneHeader := (*reflect.StringHeader)(unsafe.Pointer(&clone))
+ if len(input) != 0 && cloneHeader.Data == inputHeader.Data {
+ t.Errorf("Clone(%q) return value should not reference inputs backing memory.", input)
+ }
+
+ emptyHeader := (*reflect.StringHeader)(unsafe.Pointer(&emptyString))
+ if len(input) == 0 && cloneHeader.Data != emptyHeader.Data {
+ t.Errorf("Clone(%#v) return value should be equal to empty string.", inputHeader)
+ }
+ }
+}
+
+func BenchmarkClone(b *testing.B) {
+ var str = strings.Repeat("a", 42)
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ stringSink = strings.Clone(str)
+ }
+}
diff --git a/libgo/go/strings/compare.go b/libgo/go/strings/compare.go
index 1fe6b8d..2bd4a24 100644
--- a/libgo/go/strings/compare.go
+++ b/libgo/go/strings/compare.go
@@ -5,7 +5,7 @@
package strings
// Compare returns an integer comparing two strings lexicographically.
-// The result will be 0 if a==b, -1 if a < b, and +1 if a > b.
+// The result will be 0 if a == b, -1 if a < b, and +1 if a > b.
//
// Compare is included only for symmetry with package bytes.
// It is usually clearer and always faster to use the built-in
diff --git a/libgo/go/strings/example_test.go b/libgo/go/strings/example_test.go
index 375f9ca..94aa167 100644
--- a/libgo/go/strings/example_test.go
+++ b/libgo/go/strings/example_test.go
@@ -10,17 +10,15 @@ import (
"unicode"
)
-func ExampleFields() {
- fmt.Printf("Fields are: %q", strings.Fields(" foo bar baz "))
- // Output: Fields are: ["foo" "bar" "baz"]
-}
-
-func ExampleFieldsFunc() {
- f := func(c rune) bool {
- return !unicode.IsLetter(c) && !unicode.IsNumber(c)
+func ExampleBuilder() {
+ var b strings.Builder
+ for i := 3; i >= 1; i-- {
+ fmt.Fprintf(&b, "%d...", i)
}
- fmt.Printf("Fields are: %q", strings.FieldsFunc(" foo1;bar2,baz3...", f))
- // Output: Fields are: ["foo1" "bar2" "baz3"]
+ b.WriteString("ignition")
+ fmt.Println(b.String())
+
+ // Output: 3...2...1...ignition
}
func ExampleCompare() {
@@ -79,11 +77,40 @@ func ExampleCount() {
// 5
}
+func ExampleCut() {
+ show := func(s, sep string) {
+ before, after, found := strings.Cut(s, sep)
+ fmt.Printf("Cut(%q, %q) = %q, %q, %v\n", s, sep, before, after, found)
+ }
+ show("Gopher", "Go")
+ show("Gopher", "ph")
+ show("Gopher", "er")
+ show("Gopher", "Badger")
+ // Output:
+ // Cut("Gopher", "Go") = "", "pher", true
+ // Cut("Gopher", "ph") = "Go", "er", true
+ // Cut("Gopher", "er") = "Goph", "", true
+ // Cut("Gopher", "Badger") = "Gopher", "", false
+}
+
func ExampleEqualFold() {
fmt.Println(strings.EqualFold("Go", "go"))
// Output: true
}
+func ExampleFields() {
+ fmt.Printf("Fields are: %q", strings.Fields(" foo bar baz "))
+ // Output: Fields are: ["foo" "bar" "baz"]
+}
+
+func ExampleFieldsFunc() {
+ f := func(c rune) bool {
+ return !unicode.IsLetter(c) && !unicode.IsNumber(c)
+ }
+ fmt.Printf("Fields are: %q", strings.FieldsFunc(" foo1;bar2,baz3...", f))
+ // Output: Fields are: ["foo1" "bar2" "baz3"]
+}
+
func ExampleHasPrefix() {
fmt.Println(strings.HasPrefix("Gopher", "Go"))
fmt.Println(strings.HasPrefix("Gopher", "C"))
@@ -370,14 +397,3 @@ func ExampleTrimRightFunc() {
}))
// Output: ¡¡¡Hello, Gophers
}
-
-func ExampleBuilder() {
- var b strings.Builder
- for i := 3; i >= 1; i-- {
- fmt.Fprintf(&b, "%d...", i)
- }
- b.WriteString("ignition")
- fmt.Println(b.String())
-
- // Output: 3...2...1...ignition
-}
diff --git a/libgo/go/strings/export_test.go b/libgo/go/strings/export_test.go
index b39cee6..81d1cab 100644
--- a/libgo/go/strings/export_test.go
+++ b/libgo/go/strings/export_test.go
@@ -4,7 +4,7 @@
package strings
-func (r *Replacer) Replacer() interface{} {
+func (r *Replacer) Replacer() any {
r.once.Do(r.buildOnce)
return r.r
}
diff --git a/libgo/go/strings/reader_test.go b/libgo/go/strings/reader_test.go
index 5adea6f..dc99f9c 100644
--- a/libgo/go/strings/reader_test.go
+++ b/libgo/go/strings/reader_test.go
@@ -77,7 +77,7 @@ func TestReaderAt(t *testing.T) {
off int64
n int
want string
- wanterr interface{}
+ wanterr any
}{
{0, 10, "0123456789", nil},
{1, 10, "123456789", io.EOF},
diff --git a/libgo/go/strings/replace.go b/libgo/go/strings/replace.go
index e28d428..ee728bb 100644
--- a/libgo/go/strings/replace.go
+++ b/libgo/go/strings/replace.go
@@ -387,7 +387,7 @@ func makeSingleStringReplacer(pattern string, value string) *singleStringReplace
}
func (r *singleStringReplacer) Replace(s string) string {
- var buf []byte
+ var buf Builder
i, matched := 0, false
for {
match := r.finder.next(s[i:])
@@ -395,15 +395,16 @@ func (r *singleStringReplacer) Replace(s string) string {
break
}
matched = true
- buf = append(buf, s[i:i+match]...)
- buf = append(buf, r.value...)
+ buf.Grow(match + len(r.value))
+ buf.WriteString(s[i : i+match])
+ buf.WriteString(r.value)
i += match + len(r.finder.pattern)
}
if !matched {
return s
}
- buf = append(buf, s[i:]...)
- return string(buf)
+ buf.WriteString(s[i:])
+ return buf.String()
}
func (r *singleStringReplacer) WriteString(w io.Writer, s string) (n int, err error) {
diff --git a/libgo/go/strings/strings.go b/libgo/go/strings/strings.go
index b429735..c5a29e9 100644
--- a/libgo/go/strings/strings.go
+++ b/libgo/go/strings/strings.go
@@ -706,7 +706,8 @@ func isSeparator(r rune) bool {
// Title returns a copy of the string s with all Unicode letters that begin words
// mapped to their Unicode title case.
//
-// BUG(rsc): The rule Title uses for word boundaries does not handle Unicode punctuation properly.
+// Deprecated: The rule Title uses for word boundaries does not handle Unicode
+// punctuation properly. Use golang.org/x/text/cases instead.
func Title(s string) string {
// Use a closure here to remember state.
// Hackish but effective. Depends on Map scanning in order and calling
@@ -797,6 +798,8 @@ func lastIndexFunc(s string, f func(rune) bool, truth bool) int {
// most-significant bit of the highest word, map to the full range of all
// 128 ASCII characters. The 128-bits of the upper 16 bytes will be zeroed,
// ensuring that any non-ASCII character will be reported as not in the set.
+// This allocates a total of 32 bytes even though the upper half
+// is unused to avoid bounds checks in asciiSet.contains.
type asciiSet [8]uint32
// makeASCIISet creates a set of ASCII characters and reports whether all
@@ -807,28 +810,14 @@ func makeASCIISet(chars string) (as asciiSet, ok bool) {
if c >= utf8.RuneSelf {
return as, false
}
- as[c>>5] |= 1 << uint(c&31)
+ as[c/32] |= 1 << (c % 32)
}
return as, true
}
// contains reports whether c is inside the set.
func (as *asciiSet) contains(c byte) bool {
- return (as[c>>5] & (1 << uint(c&31))) != 0
-}
-
-func makeCutsetFunc(cutset string) func(rune) bool {
- if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
- return func(r rune) bool {
- return r == rune(cutset[0])
- }
- }
- if as, isASCII := makeASCIISet(cutset); isASCII {
- return func(r rune) bool {
- return r < utf8.RuneSelf && as.contains(byte(r))
- }
- }
- return func(r rune) bool { return IndexRune(cutset, r) >= 0 }
+ return (as[c/32] & (1 << (c % 32))) != 0
}
// Trim returns a slice of the string s with all leading and
@@ -837,7 +826,13 @@ func Trim(s, cutset string) string {
if s == "" || cutset == "" {
return s
}
- return TrimFunc(s, makeCutsetFunc(cutset))
+ if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
+ return trimLeftByte(trimRightByte(s, cutset[0]), cutset[0])
+ }
+ if as, ok := makeASCIISet(cutset); ok {
+ return trimLeftASCII(trimRightASCII(s, &as), &as)
+ }
+ return trimLeftUnicode(trimRightUnicode(s, cutset), cutset)
}
// TrimLeft returns a slice of the string s with all leading
@@ -848,7 +843,44 @@ func TrimLeft(s, cutset string) string {
if s == "" || cutset == "" {
return s
}
- return TrimLeftFunc(s, makeCutsetFunc(cutset))
+ if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
+ return trimLeftByte(s, cutset[0])
+ }
+ if as, ok := makeASCIISet(cutset); ok {
+ return trimLeftASCII(s, &as)
+ }
+ return trimLeftUnicode(s, cutset)
+}
+
+func trimLeftByte(s string, c byte) string {
+ for len(s) > 0 && s[0] == c {
+ s = s[1:]
+ }
+ return s
+}
+
+func trimLeftASCII(s string, as *asciiSet) string {
+ for len(s) > 0 {
+ if !as.contains(s[0]) {
+ break
+ }
+ s = s[1:]
+ }
+ return s
+}
+
+func trimLeftUnicode(s, cutset string) string {
+ for len(s) > 0 {
+ r, n := rune(s[0]), 1
+ if r >= utf8.RuneSelf {
+ r, n = utf8.DecodeRuneInString(s)
+ }
+ if !ContainsRune(cutset, r) {
+ break
+ }
+ s = s[n:]
+ }
+ return s
}
// TrimRight returns a slice of the string s, with all trailing
@@ -859,7 +891,44 @@ func TrimRight(s, cutset string) string {
if s == "" || cutset == "" {
return s
}
- return TrimRightFunc(s, makeCutsetFunc(cutset))
+ if len(cutset) == 1 && cutset[0] < utf8.RuneSelf {
+ return trimRightByte(s, cutset[0])
+ }
+ if as, ok := makeASCIISet(cutset); ok {
+ return trimRightASCII(s, &as)
+ }
+ return trimRightUnicode(s, cutset)
+}
+
+func trimRightByte(s string, c byte) string {
+ for len(s) > 0 && s[len(s)-1] == c {
+ s = s[:len(s)-1]
+ }
+ return s
+}
+
+func trimRightASCII(s string, as *asciiSet) string {
+ for len(s) > 0 {
+ if !as.contains(s[len(s)-1]) {
+ break
+ }
+ s = s[:len(s)-1]
+ }
+ return s
+}
+
+func trimRightUnicode(s, cutset string) string {
+ for len(s) > 0 {
+ r, n := rune(s[len(s)-1]), 1
+ if r >= utf8.RuneSelf {
+ r, n = utf8.DecodeLastRuneInString(s)
+ }
+ if !ContainsRune(cutset, r) {
+ break
+ }
+ s = s[:len(s)-n]
+ }
+ return s
}
// TrimSpace returns a slice of the string s, with all leading
@@ -1100,3 +1169,14 @@ func Index(s, substr string) int {
}
return -1
}
+
+// Cut slices s around the first instance of sep,
+// returning the text before and after sep.
+// The found result reports whether sep appears in s.
+// If sep does not appear in s, cut returns s, "", false.
+func Cut(s, sep string) (before, after string, found bool) {
+ if i := Index(s, sep); i >= 0 {
+ return s[:i], s[i+len(sep):], true
+ }
+ return s, "", false
+}
diff --git a/libgo/go/strings/strings_test.go b/libgo/go/strings/strings_test.go
index c15aca3..6855ab4 100644
--- a/libgo/go/strings/strings_test.go
+++ b/libgo/go/strings/strings_test.go
@@ -812,7 +812,9 @@ var trimTests = []struct {
{"TrimLeft", "abba", "ab", ""},
{"TrimRight", "abba", "ab", ""},
{"TrimLeft", "abba", "a", "bba"},
+ {"TrimLeft", "abba", "b", "abba"},
{"TrimRight", "abba", "a", "abb"},
+ {"TrimRight", "abba", "b", "abba"},
{"Trim", "<tag>", "<>", "tag"},
{"Trim", "* listitem", " *", "listitem"},
{"Trim", `"quote"`, `"`, "quote"},
@@ -1581,7 +1583,30 @@ var CountTests = []struct {
func TestCount(t *testing.T) {
for _, tt := range CountTests {
if num := Count(tt.s, tt.sep); num != tt.num {
- t.Errorf("Count(\"%s\", \"%s\") = %d, want %d", tt.s, tt.sep, num, tt.num)
+ t.Errorf("Count(%q, %q) = %d, want %d", tt.s, tt.sep, num, tt.num)
+ }
+ }
+}
+
+var cutTests = []struct {
+ s, sep string
+ before, after string
+ found bool
+}{
+ {"abc", "b", "a", "c", true},
+ {"abc", "a", "", "bc", true},
+ {"abc", "c", "ab", "", true},
+ {"abc", "abc", "", "", true},
+ {"abc", "", "", "abc", true},
+ {"abc", "d", "abc", "", false},
+ {"", "d", "", "", false},
+ {"", "", "", "", true},
+}
+
+func TestCut(t *testing.T) {
+ for _, tt := range cutTests {
+ if before, after, found := Cut(tt.s, tt.sep); before != tt.before || after != tt.after || found != tt.found {
+ t.Errorf("Cut(%q, %q) = %q, %q, %v, want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found)
}
}
}
@@ -1864,6 +1889,13 @@ func BenchmarkTrimASCII(b *testing.B) {
}
}
+func BenchmarkTrimByte(b *testing.B) {
+ x := " the quick brown fox "
+ for i := 0; i < b.N; i++ {
+ Trim(x, " ")
+ }
+}
+
func BenchmarkIndexPeriodic(b *testing.B) {
key := "aa"
for _, skip := range [...]int{2, 4, 8, 16, 32, 64} {