diff options
author | Ian Lance Taylor <iant@golang.org> | 2022-02-11 14:53:56 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2022-02-11 15:01:19 -0800 |
commit | 8dc2499aa62f768c6395c9754b8cabc1ce25c494 (patch) | |
tree | 43d7fd2bbfd7ad8c9625a718a5e8718889351994 /libgo/go/strings | |
parent | 9a56779dbc4e2d9c15be8d31e36f2f59be7331a8 (diff) | |
download | gcc-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.go | 28 | ||||
-rw-r--r-- | libgo/go/strings/clone_test.go | 49 | ||||
-rw-r--r-- | libgo/go/strings/compare.go | 2 | ||||
-rw-r--r-- | libgo/go/strings/example_test.go | 58 | ||||
-rw-r--r-- | libgo/go/strings/export_test.go | 2 | ||||
-rw-r--r-- | libgo/go/strings/reader_test.go | 2 | ||||
-rw-r--r-- | libgo/go/strings/replace.go | 11 | ||||
-rw-r--r-- | libgo/go/strings/strings.go | 120 | ||||
-rw-r--r-- | libgo/go/strings/strings_test.go | 34 |
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} { |