summaryrefslogtreecommitdiff
path: root/compat
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2023-02-16 17:20:41 -0700
committerLuke Shumaker <lukeshu@lukeshu.com>2023-02-18 22:45:54 -0700
commit2eb60b8be25a4b0fe3f1c5d5ca302e7e68190bad (patch)
tree0a4001f1e37d8e3a29fa3f569fa7f850c0d9f766 /compat
parent1a5b0561f53441d8a259a5096281699b5af16a6c (diff)
compat/json: Don't do actual JSON parsing in HTMLEscape
Diffstat (limited to 'compat')
-rw-r--r--compat/json/compat.go21
-rw-r--r--compat/json/compat_test.go21
2 files changed, 41 insertions, 1 deletions
diff --git a/compat/json/compat.go b/compat/json/compat.go
index d326514..edc6908 100644
--- a/compat/json/compat.go
+++ b/compat/json/compat.go
@@ -11,10 +11,13 @@ import (
"bytes"
"encoding/json"
"errors"
+ "fmt"
"io"
"strconv"
+ "unicode/utf8"
"git.lukeshu.com/go/lowmemjson"
+ "git.lukeshu.com/go/lowmemjson/internal/jsonstring"
)
//nolint:stylecheck // ST1021 False positive; these aren't comments on individual types.
@@ -144,7 +147,23 @@ func convertReEncodeError(err error) error {
}
func HTMLEscape(dst *bytes.Buffer, src []byte) {
- _, _ = lowmemjson.NewReEncoder(dst, lowmemjson.ReEncoderConfig{}).Write(src)
+ for n := 0; n < len(src); {
+ c, size := utf8.DecodeRune(src[n:])
+ if c == utf8.RuneError && size == 1 {
+ dst.WriteByte(src[n])
+ } else {
+ mode := lowmemjson.EscapeHTMLSafe(c, lowmemjson.BackslashEscapeNone)
+ switch mode {
+ case lowmemjson.BackslashEscapeNone:
+ dst.WriteRune(c)
+ case lowmemjson.BackslashEscapeUnicode:
+ _ = jsonstring.WriteStringUnicodeEscape(dst, c)
+ default:
+ panic(fmt.Errorf("lowmemjson.EscapeHTMLSafe returned an unexpected escape mode=%d", mode))
+ }
+ }
+ n += size
+ }
}
func reencode(dst io.Writer, src []byte, cfg lowmemjson.ReEncoderConfig) error {
diff --git a/compat/json/compat_test.go b/compat/json/compat_test.go
index 128bd1b..0c14a60 100644
--- a/compat/json/compat_test.go
+++ b/compat/json/compat_test.go
@@ -11,6 +11,27 @@ import (
"github.com/stretchr/testify/assert"
)
+func TestCompatHTMLEscape(t *testing.T) {
+ t.Parallel()
+ type testcase struct {
+ In string
+ Out string
+ }
+ testcases := map[string]testcase{
+ "invalid": {In: `x`, Out: `x`},
+ }
+ for tcName, tc := range testcases {
+ tc := tc
+ t.Run(tcName, func(t *testing.T) {
+ t.Parallel()
+ t.Logf("in=%q", tc.In)
+ var dst bytes.Buffer
+ HTMLEscape(&dst, []byte(tc.In))
+ assert.Equal(t, tc.Out, dst.String())
+ })
+ }
+}
+
func TestCompatValid(t *testing.T) {
t.Parallel()
type testcase struct {