aboutsummaryrefslogtreecommitdiff
path: root/src/strings
diff options
context:
space:
mode:
Diffstat (limited to 'src/strings')
-rw-r--r--src/strings/strings.go18
-rw-r--r--src/strings/strings_test.go49
2 files changed, 48 insertions, 19 deletions
diff --git a/src/strings/strings.go b/src/strings/strings.go
index 64022533ea..349989278d 100644
--- a/src/strings/strings.go
+++ b/src/strings/strings.go
@@ -145,12 +145,24 @@ func LastIndex(s, sep string) int {
// IndexRune returns the index of the first instance of the Unicode code point
// r, or -1 if rune is not present in s.
+// If r is utf8.RuneError, it returns the first instance of any
+// invalid UTF-8 byte sequence.
func IndexRune(s string, r rune) int {
- if r < utf8.RuneSelf {
+ switch {
+ case 0 <= r && r < utf8.RuneSelf:
return IndexByte(s, byte(r))
+ case r == utf8.RuneError:
+ for i, r := range s {
+ if r == utf8.RuneError {
+ return i
+ }
+ }
+ return -1
+ case !utf8.ValidRune(r):
+ return -1
+ default:
+ return Index(s, string(r))
}
-
- return Index(s, string(r))
}
// IndexAny returns the index of the first instance of any Unicode code point
diff --git a/src/strings/strings_test.go b/src/strings/strings_test.go
index 738185e5dd..6815944899 100644
--- a/src/strings/strings_test.go
+++ b/src/strings/strings_test.go
@@ -240,21 +240,39 @@ func TestIndexRandom(t *testing.T) {
}
}
-var indexRuneTests = []struct {
- s string
- rune rune
- out int
-}{
- {"a A x", 'A', 2},
- {"some_text=some_value", '=', 9},
- {"☺a", 'a', 3},
- {"a☻☺b", '☺', 4},
-}
-
func TestIndexRune(t *testing.T) {
- for _, test := range indexRuneTests {
- if actual := IndexRune(test.s, test.rune); actual != test.out {
- t.Errorf("IndexRune(%q,%d)= %v; want %v", test.s, test.rune, actual, test.out)
+ tests := []struct {
+ in string
+ rune rune
+ want int
+ }{
+ {"", 'a', -1},
+ {"", '☺', -1},
+ {"foo", '☹', -1},
+ {"foo", 'o', 1},
+ {"foo☺bar", '☺', 3},
+ {"foo☺☻☹bar", '☹', 9},
+ {"a A x", 'A', 2},
+ {"some_text=some_value", '=', 9},
+ {"☺a", 'a', 3},
+ {"a☻☺b", '☺', 4},
+
+ // RuneError should match any invalid UTF-8 byte sequence.
+ {"�", '�', 0},
+ {"\xff", '�', 0},
+ {"☻x�", '�', len("☻x")},
+ {"☻x\xe2\x98", '�', len("☻x")},
+ {"☻x\xe2\x98�", '�', len("☻x")},
+ {"☻x\xe2\x98x", '�', len("☻x")},
+
+ // Invalid rune values should never match.
+ {"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", -1, -1},
+ {"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", 0xD800, -1}, // Surrogate pair
+ {"a☺b☻c☹d\xe2\x98�\xff�\xed\xa0\x80", utf8.MaxRune + 1, -1},
+ }
+ for _, tt := range tests {
+ if got := IndexRune(tt.in, tt.rune); got != tt.want {
+ t.Errorf("IndexRune(%q, %d) = %v; want %v", tt.in, tt.rune, got, tt.want)
}
}
@@ -267,9 +285,8 @@ func TestIndexRune(t *testing.T) {
t.Fatalf("'世' at %d; want 4", i)
}
})
-
if allocs != 0 {
- t.Errorf(`expected no allocations, got %f`, allocs)
+ t.Errorf("expected no allocations, got %f", allocs)
}
}