From: Michael Schurter Date: Sat, 3 Jun 2017 20:36:54 +0000 (-0700) Subject: encoding/json: reduce allocations by Decoder for \uXXXX X-Git-Tag: go1.10beta1~171 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=7eb7f8f5a75fcff520b5b93a7831fa7044d86887;p=gostls13.git encoding/json: reduce allocations by Decoder for \uXXXX Manually convert hex escape sequence to rune instead of calling strconv.ParseUint. This inlines the unhex func from docs (and many other packages). name old time/op new time/op delta UnicodeDecoder-4 468ns ± 1% 402ns ± 1% -14.26% (p=0.000 n=10+10) name old speed new speed delta UnicodeDecoder-4 29.9MB/s ± 1% 34.8MB/s ± 1% +16.59% (p=0.000 n=10+10) name old alloc/op new alloc/op delta UnicodeDecoder-4 44.0B ± 0% 36.0B ± 0% -18.18% (p=0.000 n=10+10) name old allocs/op new allocs/op delta UnicodeDecoder-4 4.00 ± 0% 2.00 ± 0% -50.00% (p=0.000 n=10+10) Fixes #20567 Change-Id: If350978d5bb98ff517485752184d02249f5d1f3a Reviewed-on: https://go-review.googlesource.com/44738 Run-TryBot: Russ Cox Reviewed-by: Russ Cox TryBot-Result: Gobot Gobot --- diff --git a/src/encoding/json/bench_test.go b/src/encoding/json/bench_test.go index 85d7ae043b..42439eb705 100644 --- a/src/encoding/json/bench_test.go +++ b/src/encoding/json/bench_test.go @@ -133,6 +133,21 @@ func BenchmarkCodeDecoder(b *testing.B) { b.SetBytes(int64(len(codeJSON))) } +func BenchmarkUnicodeDecoder(b *testing.B) { + j := []byte(`"\uD83D\uDE01"`) + b.SetBytes(int64(len(j))) + r := bytes.NewReader(j) + dec := NewDecoder(r) + var out string + b.ResetTimer() + for i := 0; i < b.N; i++ { + if err := dec.Decode(&out); err != nil { + b.Fatal("Decode:", err) + } + r.Seek(0, 0) + } +} + func BenchmarkDecoderStream(b *testing.B) { b.StopTimer() var buf bytes.Buffer diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go index 70179e60ac..4f98916105 100644 --- a/src/encoding/json/decode.go +++ b/src/encoding/json/decode.go @@ -1148,11 +1148,21 @@ func getu4(s []byte) rune { if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { return -1 } - r, err := strconv.ParseUint(string(s[2:6]), 16, 64) - if err != nil { - return -1 + var r rune + for _, c := range s[2:6] { + switch { + case '0' <= c && c <= '9': + c = c - '0' + case 'a' <= c && c <= 'f': + c = c - 'a' + 10 + case 'A' <= c && c <= 'F': + c = c - 'A' + 10 + default: + return -1 + } + r = r*16 + rune(c) } - return rune(r) + return r } // unquote converts a quoted JSON string literal s into an actual string t.