diff options
Diffstat (limited to 'libgo/go/strings')
-rw-r--r-- | libgo/go/strings/compare.go | 28 | ||||
-rw-r--r-- | libgo/go/strings/compare_test.go | 98 | ||||
-rw-r--r-- | libgo/go/strings/reader.go | 6 | ||||
-rw-r--r-- | libgo/go/strings/reader_test.go | 13 | ||||
-rw-r--r-- | libgo/go/strings/strings.go | 32 | ||||
-rw-r--r-- | libgo/go/strings/strings_test.go | 46 |
6 files changed, 210 insertions, 13 deletions
diff --git a/libgo/go/strings/compare.go b/libgo/go/strings/compare.go new file mode 100644 index 00000000000..b84dddea74e --- /dev/null +++ b/libgo/go/strings/compare.go @@ -0,0 +1,28 @@ +// 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. + +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. +// +// Compare is included only for symmetry with package bytes. +// It is usually clearer and always faster to use the built-in +// string comparison operators ==, <, >, and so on. +func Compare(a, b string) int { + // NOTE(rsc): This function does NOT call the runtime cmpstring function, + // because we do not want to provide any performance justification for + // using strings.Compare. Basically no one should use strings.Compare. + // As the comment above says, it is here only for symmetry with package bytes. + // If performance is important, the compiler should be changed to recognize + // the pattern so that all code doing three-way comparisons, not just code + // using strings.Compare, can benefit. + if a == b { + return 0 + } + if a < b { + return -1 + } + return +1 +} diff --git a/libgo/go/strings/compare_test.go b/libgo/go/strings/compare_test.go new file mode 100644 index 00000000000..68fc88e1431 --- /dev/null +++ b/libgo/go/strings/compare_test.go @@ -0,0 +1,98 @@ +// Copyright 2013 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 + +// Derived from bytes/compare_test.go. +// Benchmarks omitted since the underlying implementation is identical. + +import ( + . "strings" + "testing" +) + +var compareTests = []struct { + a, b string + i int +}{ + {"", "", 0}, + {"a", "", 1}, + {"", "a", -1}, + {"abc", "abc", 0}, + {"ab", "abc", -1}, + {"abc", "ab", 1}, + {"x", "ab", 1}, + {"ab", "x", -1}, + {"x", "a", 1}, + {"b", "x", -1}, + // test runtime·memeq's chunked implementation + {"abcdefgh", "abcdefgh", 0}, + {"abcdefghi", "abcdefghi", 0}, + {"abcdefghi", "abcdefghj", -1}, +} + +func TestCompare(t *testing.T) { + for _, tt := range compareTests { + cmp := Compare(tt.a, tt.b) + if cmp != tt.i { + t.Errorf(`Compare(%q, %q) = %v`, tt.a, tt.b, cmp) + } + } +} + +func TestCompareIdenticalString(t *testing.T) { + var s = "Hello Gophers!" + if Compare(s, s) != 0 { + t.Error("s != s") + } + if Compare(s, s[:1]) != 1 { + t.Error("s > s[:1] failed") + } +} + +func TestCompareStrings(t *testing.T) { + n := 128 + a := make([]byte, n+1) + b := make([]byte, n+1) + for len := 0; len < 128; len++ { + // randomish but deterministic data. No 0 or 255. + for i := 0; i < len; i++ { + a[i] = byte(1 + 31*i%254) + b[i] = byte(1 + 31*i%254) + } + // data past the end is different + for i := len; i <= n; i++ { + a[i] = 8 + b[i] = 9 + } + + cmp := Compare(string(a[:len]), string(b[:len])) + if cmp != 0 { + t.Errorf(`CompareIdentical(%d) = %d`, len, cmp) + } + if len > 0 { + cmp = Compare(string(a[:len-1]), string(b[:len])) + if cmp != -1 { + t.Errorf(`CompareAshorter(%d) = %d`, len, cmp) + } + cmp = Compare(string(a[:len]), string(b[:len-1])) + if cmp != 1 { + t.Errorf(`CompareBshorter(%d) = %d`, len, cmp) + } + } + for k := 0; k < len; k++ { + b[k] = a[k] - 1 + cmp = Compare(string(a[:len]), string(b[:len])) + if cmp != 1 { + t.Errorf(`CompareAbigger(%d,%d) = %d`, len, k, cmp) + } + b[k] = a[k] + 1 + cmp = Compare(string(a[:len]), string(b[:len])) + if cmp != -1 { + t.Errorf(`CompareBbigger(%d,%d) = %d`, len, k, cmp) + } + b[k] = a[k] + } + } +} diff --git a/libgo/go/strings/reader.go b/libgo/go/strings/reader.go index 82df974398c..7a872fbcb08 100644 --- a/libgo/go/strings/reader.go +++ b/libgo/go/strings/reader.go @@ -28,6 +28,12 @@ func (r *Reader) Len() int { return int(int64(len(r.s)) - r.i) } +// Size returns the original length of the underlying string. +// Size is the number of bytes available for reading via ReadAt. +// The returned value is always the same and is not affected by calls +// to any other method. +func (r *Reader) Size() int64 { return int64(len(r.s)) } + func (r *Reader) Read(b []byte) (n int, err error) { if len(b) == 0 { return 0, nil diff --git a/libgo/go/strings/reader_test.go b/libgo/go/strings/reader_test.go index bee90eb2585..5003a37be48 100644 --- a/libgo/go/strings/reader_test.go +++ b/libgo/go/strings/reader_test.go @@ -8,6 +8,7 @@ import ( "bytes" "fmt" "io" + "io/ioutil" "os" "strings" "sync" @@ -157,3 +158,15 @@ func TestWriteTo(t *testing.T) { } } } + +// tests that Len is affected by reads, but Size is not. +func TestReaderLenSize(t *testing.T) { + r := strings.NewReader("abc") + io.CopyN(ioutil.Discard, r, 1) + if r.Len() != 2 { + t.Errorf("Len = %d; want 2", r.Len()) + } + if r.Size() != 3 { + t.Errorf("Size = %d; want 3", r.Size()) + } +} diff --git a/libgo/go/strings/strings.go b/libgo/go/strings/strings.go index 27d384983ef..dd51dabb322 100644 --- a/libgo/go/strings/strings.go +++ b/libgo/go/strings/strings.go @@ -2,7 +2,9 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package strings implements simple functions to manipulate strings. +// Package strings implements simple functions to manipulate UTF-8 encoded strings. +// +// For information about UTF-8 strings in Go, see https://blog.golang.org/strings. package strings import ( @@ -78,6 +80,7 @@ func hashStrRev(sep string) (uint32, uint32) { } // Count counts the number of non-overlapping instances of sep in s. +// If sep is an empty string, Count returns 1 + the number of Unicode code points in s. func Count(s, sep string) int { n := 0 // special cases @@ -125,17 +128,17 @@ func Count(s, sep string) int { return n } -// Contains returns true if substr is within s. +// Contains reports whether substr is within s. func Contains(s, substr string) bool { return Index(s, substr) >= 0 } -// ContainsAny returns true if any Unicode code points in chars are within s. +// ContainsAny reports whether any Unicode code points in chars are within s. func ContainsAny(s, chars string) bool { return IndexAny(s, chars) >= 0 } -// ContainsRune returns true if the Unicode code point r is within s. +// ContainsRune reports whether the Unicode code point r is within s. func ContainsRune(s string, r rune) bool { return IndexRune(s, r) >= 0 } @@ -184,14 +187,7 @@ func LastIndex(s, sep string) int { case n == 0: return len(s) case n == 1: - // special case worth making fast - c := sep[0] - for i := len(s) - 1; i >= 0; i-- { - if s[i] == c { - return i - } - } - return -1 + return LastIndexByte(s, sep[0]) case n == len(s): if sep == s { return 0 @@ -270,6 +266,16 @@ func LastIndexAny(s, chars string) int { return -1 } +// LastIndexByte returns the index of the last instance of c in s, or -1 if c is not present in s. +func LastIndexByte(s string, c byte) int { + for i := len(s) - 1; i >= 0; i-- { + if s[i] == c { + return i + } + } + return -1 +} + // Generic split: splits after each instance of sep, // including sepSave bytes of sep in the subarrays. func genSplit(s, sep string, sepSave, n int) []string { @@ -519,7 +525,7 @@ func isSeparator(r rune) bool { // Title returns a copy of the string s with all Unicode letters that begin words // mapped to their title case. // -// BUG: The rule Title uses for word boundaries does not handle Unicode punctuation properly. +// BUG(rsc): The rule Title uses for word boundaries does not handle Unicode punctuation properly. func Title(s string) string { // Use a closure here to remember state. // Hackish but effective. Depends on Map scanning in order and calling diff --git a/libgo/go/strings/strings_test.go b/libgo/go/strings/strings_test.go index 7bb81ef3ca1..4e21deaecde 100644 --- a/libgo/go/strings/strings_test.go +++ b/libgo/go/strings/strings_test.go @@ -120,6 +120,23 @@ func TestLastIndex(t *testing.T) { runIndexTests(t, LastIndex, "LastIndex", l func TestIndexAny(t *testing.T) { runIndexTests(t, IndexAny, "IndexAny", indexAnyTests) } func TestLastIndexAny(t *testing.T) { runIndexTests(t, LastIndexAny, "LastIndexAny", lastIndexAnyTests) } +func TestLastIndexByte(t *testing.T) { + testCases := []IndexTest{ + {"", "q", -1}, + {"abcdef", "q", -1}, + {"abcdefabcdef", "a", len("abcdef")}, // something in the middle + {"abcdefabcdef", "f", len("abcdefabcde")}, // last byte + {"zabcdefabcdef", "z", 0}, // first byte + {"a☺b☻c☹d", "b", len("a☺")}, // non-ascii + } + for _, test := range testCases { + actual := LastIndexByte(test.s, test.sep[0]) + if actual != test.out { + t.Errorf("LastIndexByte(%q,%c) = %v; want %v", test.s, test.sep[0], actual, test.out) + } + } +} + var indexRuneTests = []struct { s string rune rune @@ -569,6 +586,35 @@ func TestTrim(t *testing.T) { } } +func BenchmarkTrim(b *testing.B) { + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + for _, tc := range trimTests { + name := tc.f + var f func(string, string) string + switch name { + case "Trim": + f = Trim + case "TrimLeft": + f = TrimLeft + case "TrimRight": + f = TrimRight + case "TrimPrefix": + f = TrimPrefix + case "TrimSuffix": + f = TrimSuffix + default: + b.Errorf("Undefined trim function %s", name) + } + actual := f(tc.in, tc.arg) + if actual != tc.out { + b.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.arg, actual, tc.out) + } + } + } +} + type predicate struct { f func(rune) bool name string |