summaryrefslogtreecommitdiff
path: root/libgo/go/strings
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/strings')
-rw-r--r--libgo/go/strings/compare.go28
-rw-r--r--libgo/go/strings/compare_test.go98
-rw-r--r--libgo/go/strings/reader.go6
-rw-r--r--libgo/go/strings/reader_test.go13
-rw-r--r--libgo/go/strings/strings.go32
-rw-r--r--libgo/go/strings/strings_test.go46
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