]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/json: fix and optimize marshal for quoted string
authorLucas Bremgartner <lucas@bremis.ch>
Tue, 10 Sep 2019 18:52:02 +0000 (18:52 +0000)
committerDaniel Martí <mvdan@mvdan.cc>
Wed, 11 Sep 2019 12:26:35 +0000 (12:26 +0000)
Since Go 1.2 every string can be marshaled to JSON without error even if it
contains invalid UTF-8 byte sequences. Therefore there is no need to use
Marshal again for the only reason of enclosing the string in double quotes.
Not using Marshal here also removes the error check as there has not been a
way for Marshal to fail anyway.

name          old time/op    new time/op    delta
Issue34127-4     360ns ± 3%     200ns ± 3%  -44.56%  (p=0.008 n=5+5)

name          old alloc/op   new alloc/op   delta
Issue34127-4     56.0B ± 0%     40.0B ± 0%  -28.57%  (p=0.008 n=5+5)

name          old allocs/op  new allocs/op  delta
Issue34127-4      3.00 ± 0%      2.00 ± 0%  -33.33%  (p=0.008 n=5+5)

Fixes #34154

Change-Id: Ib60dc11980f9b20d8bef2982de7168943d632263
GitHub-Last-Rev: 9b0ac1d4c5318b6bf9ed7930320f2bd755f9939c
GitHub-Pull-Request: golang/go#34127
Reviewed-on: https://go-review.googlesource.com/c/go/+/193604
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Run-TryBot: Daniel Martí <mvdan@mvdan.cc>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/encoding/json/bench_test.go
src/encoding/json/encode.go
src/encoding/json/stream_test.go

index f2592e3dbdf55f04b41006ea9c3272bd3b52d2a6..f92d39f0c657aa395409024d6e3189e0f267f1d4 100644 (file)
@@ -297,6 +297,22 @@ func BenchmarkIssue10335(b *testing.B) {
        })
 }
 
+func BenchmarkIssue34127(b *testing.B) {
+       b.ReportAllocs()
+       j := struct {
+               Bar string `json:"bar,string"`
+       }{
+               Bar: `foobar`,
+       }
+       b.RunParallel(func(pb *testing.PB) {
+               for pb.Next() {
+                       if _, err := Marshal(&j); err != nil {
+                               b.Fatal(err)
+                       }
+               }
+       })
+}
+
 func BenchmarkUnmapped(b *testing.B) {
        b.ReportAllocs()
        j := []byte(`{"s": "hello", "y": 2, "o": {"x": 0}, "a": [1, 99, {"x": 1}]}`)
index 2e1f56f88215dfdea1d1e2792f4997aa1710b826..0758b2fc9e82b685480a3c60df027ce3de4b816b 100644 (file)
@@ -600,11 +600,11 @@ func stringEncoder(e *encodeState, v reflect.Value, opts encOpts) {
                return
        }
        if opts.quoted {
-               sb, err := Marshal(v.String())
-               if err != nil {
-                       e.error(err)
-               }
-               e.string(string(sb), opts.escapeHTML)
+               b := make([]byte, 0, v.Len()+2)
+               b = append(b, '"')
+               b = append(b, []byte(v.String())...)
+               b = append(b, '"')
+               e.stringBytes(b, opts.escapeHTML)
        } else {
                e.string(v.String(), opts.escapeHTML)
        }
index e3317ddeb0b5d38eb41ab451a9bbe8af3ea7a896..ebb4f231d151892c981f495470825dee12f8cf57 100644 (file)
@@ -118,6 +118,11 @@ func TestEncoderSetEscapeHTML(t *testing.T) {
                Ptr    strPtrMarshaler
        }{`"<str>"`, `"<str>"`}
 
+       // https://golang.org/issue/34154
+       stringOption := struct {
+               Bar string `json:"bar,string"`
+       }{`<html>foobar</html>`}
+
        for _, tt := range []struct {
                name       string
                v          interface{}
@@ -137,6 +142,11 @@ func TestEncoderSetEscapeHTML(t *testing.T) {
                        `{"NonPtr":"\u003cstr\u003e","Ptr":"\u003cstr\u003e"}`,
                        `{"NonPtr":"<str>","Ptr":"<str>"}`,
                },
+               {
+                       "stringOption", stringOption,
+                       `{"bar":"\"\u003chtml\u003efoobar\u003c/html\u003e\""}`,
+                       `{"bar":"\"<html>foobar</html>\""}`,
+               },
        } {
                var buf bytes.Buffer
                enc := NewEncoder(&buf)