]> Cypherpunks repositories - gostls13.git/commitdiff
encoding/json: use append for HTMLEscape
authorJoe Tsai <joetsai@digital-static.net>
Mon, 20 Feb 2023 08:42:27 +0000 (00:42 -0800)
committerGopher Robot <gobot@golang.org>
Mon, 27 Feb 2023 17:33:26 +0000 (17:33 +0000)
Use append for HTMLEscape similar to Indent and Compact.
Move it to indent.go alongside Compact, as it shares similar logic.
In a future CL, we will modify appendCompact to be written in terms
of appendHTMLEscape, but we need to first move the JSON decoder logic
out of the main loop of appendCompact.

Change-Id: I131c64cd53d5d2b4ca798b37349aeefe17b418c7
Reviewed-on: https://go-review.googlesource.com/c/go/+/471198
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Joseph Tsai <joetsai@digital-static.net>
Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
Auto-Submit: Joseph Tsai <joetsai@digital-static.net>

src/encoding/json/encode.go
src/encoding/json/indent.go

index ff0e40d532e87323318afbf9ffaf322566b65246..de639aa008aa278c5720ba417f2323a3b2125fa2 100644 (file)
@@ -183,41 +183,6 @@ func MarshalIndent(v any, prefix, indent string) ([]byte, error) {
        return b2, nil
 }
 
-// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029
-// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029
-// so that the JSON will be safe to embed inside HTML <script> tags.
-// For historical reasons, web browsers don't honor standard HTML
-// escaping within <script> tags, so an alternative JSON encoding must
-// be used.
-func HTMLEscape(dst *bytes.Buffer, src []byte) {
-       // The characters can only appear in string literals,
-       // so just scan the string one byte at a time.
-       start := 0
-       for i, c := range src {
-               if c == '<' || c == '>' || c == '&' {
-                       if start < i {
-                               dst.Write(src[start:i])
-                       }
-                       dst.WriteString(`\u00`)
-                       dst.WriteByte(hex[c>>4])
-                       dst.WriteByte(hex[c&0xF])
-                       start = i + 1
-               }
-               // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
-               if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
-                       if start < i {
-                               dst.Write(src[start:i])
-                       }
-                       dst.WriteString(`\u202`)
-                       dst.WriteByte(hex[src[i+2]&0xF])
-                       start = i + 3
-               }
-       }
-       if start < len(src) {
-               dst.Write(src[start:])
-       }
-}
-
 // Marshaler is the interface implemented by types that
 // can marshal themselves into valid JSON.
 type Marshaler interface {
@@ -1119,8 +1084,8 @@ func typeFields(t reflect.Type) structFields {
        // Fields found.
        var fields []field
 
-       // Buffer to run HTMLEscape on field names.
-       var nameEscBuf bytes.Buffer
+       // Buffer to run appendHTMLEscape on field names.
+       var nameEscBuf []byte
 
        for len(next) > 0 {
                current, next = next, current[:0]
@@ -1199,11 +1164,8 @@ func typeFields(t reflect.Type) structFields {
                                        field.equalFold = foldFunc(field.nameBytes)
 
                                        // Build nameEscHTML and nameNonEsc ahead of time.
-                                       nameEscBuf.Reset()
-                                       nameEscBuf.WriteString(`"`)
-                                       HTMLEscape(&nameEscBuf, field.nameBytes)
-                                       nameEscBuf.WriteString(`":`)
-                                       field.nameEscHTML = nameEscBuf.String()
+                                       nameEscBuf = appendHTMLEscape(nameEscBuf[:0], field.nameBytes)
+                                       field.nameEscHTML = `"` + string(nameEscBuf) + `":`
                                        field.nameNonEsc = `"` + field.name + `":`
 
                                        fields = append(fields, field)
index 375f71605a8c9a87e5031b5d2229989e1ee774d4..99951208a0514f2b914e4dff5e9dc24548ad38fb 100644 (file)
@@ -11,6 +11,36 @@ func availableBuffer(b *bytes.Buffer) []byte {
        return b.Bytes()[b.Len():]
 }
 
+// HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029
+// characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029
+// so that the JSON will be safe to embed inside HTML <script> tags.
+// For historical reasons, web browsers don't honor standard HTML
+// escaping within <script> tags, so an alternative JSON encoding must be used.
+func HTMLEscape(dst *bytes.Buffer, src []byte) {
+       dst.Grow(len(src))
+       dst.Write(appendHTMLEscape(availableBuffer(dst), src))
+}
+
+func appendHTMLEscape(dst, src []byte) []byte {
+       // The characters can only appear in string literals,
+       // so just scan the string one byte at a time.
+       start := 0
+       for i, c := range src {
+               if c == '<' || c == '>' || c == '&' {
+                       dst = append(dst, src[start:i]...)
+                       dst = append(dst, '\\', 'u', '0', '0', hex[c>>4], hex[c&0xF])
+                       start = i + 1
+               }
+               // Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
+               if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
+                       dst = append(dst, src[start:i]...)
+                       dst = append(dst, '\\', 'u', '2', '0', '2', hex[src[i+2]&0xF])
+                       start = i + len("\u2029")
+               }
+       }
+       return append(dst, src[start:]...)
+}
+
 // Compact appends to dst the JSON-encoded src with
 // insignificant space characters elided.
 func Compact(dst *bytes.Buffer, src []byte) error {