diff options
Diffstat (limited to 'src/strings')
| -rw-r--r-- | src/strings/strings.go | 18 | ||||
| -rw-r--r-- | src/strings/strings_test.go | 49 |
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) } } |
