diff options
| author | Joe Tsai <joetsai@digital-static.net> | 2016-10-26 14:18:37 -0700 |
|---|---|---|
| committer | Joe Tsai <thebrokentoaster@gmail.com> | 2016-10-26 23:02:27 +0000 |
| commit | 4b2665786ec13c82ab751cd2d4312772b80cef12 (patch) | |
| tree | b3e0cc0d9677b3bc8400c6280e054eb144ecb1cb /src/bytes/bytes.go | |
| parent | 4f1e7be51f401a5374c0def2df0773abc924b03c (diff) | |
| download | go-4b2665786ec13c82ab751cd2d4312772b80cef12.tar.xz | |
bytes, strings: fix regression in IndexRune
In all previous versions of Go, the behavior of IndexRune(s, r)
where r was utf.RuneError was that it would effectively return the
index of any invalid UTF-8 byte sequence (include RuneError).
Optimizations made in http://golang.org/cl/28537 and
http://golang.org/cl/28546 altered this undocumented behavior such
that RuneError would only match on the RuneError rune itself.
Although, the new behavior is arguably reasonable, it did break code
that depended on the previous behavior. Thus, we add special checks
to ensure that we preserve the old behavior.
There is a slight performance hit for correctness:
benchmark old ns/op new ns/op delta
BenchmarkIndexRune/10-4 19.3 21.6 +11.92%
BenchmarkIndexRune/32-4 33.6 35.2 +4.76%
This only occurs on small strings. The performance hit for larger strings
is neglible and not shown.
Fixes #17611
Change-Id: I1d863a741213d46c40b2e1724c41245df52502a5
Reviewed-on: https://go-review.googlesource.com/32123
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Diffstat (limited to 'src/bytes/bytes.go')
| -rw-r--r-- | src/bytes/bytes.go | 23 |
1 files changed, 19 insertions, 4 deletions
diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go index 5dfc441b81..40c7c23cd7 100644 --- a/src/bytes/bytes.go +++ b/src/bytes/bytes.go @@ -130,13 +130,28 @@ func LastIndexByte(s []byte, c byte) int { // IndexRune interprets s as a sequence of UTF-8-encoded Unicode code points. // It returns the byte index of the first occurrence in s of the given rune. // It returns -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 []byte, 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 := 0; i < len(s); { + r1, n := utf8.DecodeRune(s[i:]) + if r1 == utf8.RuneError { + return i + } + i += n + } + return -1 + case !utf8.ValidRune(r): + return -1 + default: + var b [utf8.UTFMax]byte + n := utf8.EncodeRune(b[:], r) + return Index(s, b[:n]) } - var b [utf8.UTFMax]byte - n := utf8.EncodeRune(b[:], r) - return Index(s, b[:n]) } // IndexAny interprets s as a sequence of UTF-8-encoded Unicode code points. |
