aboutsummaryrefslogtreecommitdiff
path: root/src/unicode
diff options
context:
space:
mode:
Diffstat (limited to 'src/unicode')
-rw-r--r--src/unicode/utf8/utf8.go26
-rw-r--r--src/unicode/utf8/utf8_test.go27
2 files changed, 49 insertions, 4 deletions
diff --git a/src/unicode/utf8/utf8.go b/src/unicode/utf8/utf8.go
index 01cad1cc81..68283341d9 100644
--- a/src/unicode/utf8/utf8.go
+++ b/src/unicode/utf8/utf8.go
@@ -155,6 +155,20 @@ func FullRuneInString(s string) bool {
// out of range, or is not the shortest possible UTF-8 encoding for the
// value. No other validation is performed.
func DecodeRune(p []byte) (r rune, size int) {
+ // Inlineable fast path for ASCII characters; see #48195.
+ // This implementation is weird but effective at rendering the
+ // function inlineable.
+ for _, b := range p {
+ if b < RuneSelf {
+ return rune(b), 1
+ }
+ break
+ }
+ r, size = decodeRuneSlow(p)
+ return
+}
+
+func decodeRuneSlow(p []byte) (r rune, size int) {
n := len(p)
if n < 1 {
return RuneError, 0
@@ -203,6 +217,18 @@ func DecodeRune(p []byte) (r rune, size int) {
// out of range, or is not the shortest possible UTF-8 encoding for the
// value. No other validation is performed.
func DecodeRuneInString(s string) (r rune, size int) {
+ // Inlineable fast path for ASCII characters; see #48195.
+ // This implementation is a bit weird but effective at rendering the
+ // function inlineable.
+ if s != "" && s[0] < RuneSelf {
+ return rune(s[0]), 1
+ } else {
+ r, size = decodeRuneInStringSlow(s)
+ }
+ return
+}
+
+func decodeRuneInStringSlow(s string) (rune, int) {
n := len(s)
if n < 1 {
return RuneError, 0
diff --git a/src/unicode/utf8/utf8_test.go b/src/unicode/utf8/utf8_test.go
index aece0fab73..bf4f074ffd 100644
--- a/src/unicode/utf8/utf8_test.go
+++ b/src/unicode/utf8/utf8_test.go
@@ -747,18 +747,37 @@ func BenchmarkAppendInvalidRuneNegative(b *testing.B) {
func BenchmarkDecodeASCIIRune(b *testing.B) {
a := []byte{'a'}
- for i := 0; i < b.N; i++ {
- DecodeRune(a)
+ for range b.N {
+ runeSink, sizeSink = DecodeRune(a)
}
}
func BenchmarkDecodeJapaneseRune(b *testing.B) {
nihon := []byte("本")
- for i := 0; i < b.N; i++ {
- DecodeRune(nihon)
+ for range b.N {
+ runeSink, sizeSink = DecodeRune(nihon)
+ }
+}
+
+func BenchmarkDecodeASCIIRuneInString(b *testing.B) {
+ a := "a"
+ for range b.N {
+ runeSink, sizeSink = DecodeRuneInString(a)
}
}
+func BenchmarkDecodeJapaneseRuneInString(b *testing.B) {
+ nihon := "本"
+ for range b.N {
+ runeSink, sizeSink = DecodeRuneInString(nihon)
+ }
+}
+
+var (
+ runeSink rune
+ sizeSink int
+)
+
// boolSink is used to reference the return value of benchmarked
// functions to avoid dead code elimination.
var boolSink bool