From ffc954d3569b474f9007babbf4f1a59378ca4972 Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Sun, 31 Jan 2021 22:53:01 +0900 Subject: [PATCH] Fix MarshalNoEscape --- encode.go | 47 +++++++++++++++++++++++++++++++++++++++++++++++ json.go | 2 +- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/encode.go b/encode.go index a9fc6c7..aab231a 100644 --- a/encode.go +++ b/encode.go @@ -133,6 +133,23 @@ func marshal(v interface{}, opt EncodeOption) ([]byte, error) { return copied, nil } +func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) { + buf, err := encodeNoEscapeWithOpt(v, EncodeOptionHTMLEscape) + if err != nil { + return nil, err + } + + // this line exists to escape call of `runtime.makeslicecopy` . + // if use `make([]byte, len(buf)-1)` and `copy(copied, buf)`, + // dst buffer size and src buffer size are differrent. + // in this case, compiler uses `runtime.makeslicecopy`, but it is slow. + buf = buf[:len(buf)-1] + + copied := make([]byte, len(buf)) + copy(copied, buf) + return copied, nil +} + func marshalIndent(v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) { buf, err := encodeIndentWithOpt(v, prefix, indent, EncodeOptionHTMLEscape) if err != nil { @@ -178,6 +195,36 @@ func encodeWithOpt(v interface{}, opt EncodeOption) ([]byte, error) { return buf, nil } +func encodeNoEscapeWithOpt(v interface{}, opt EncodeOption) ([]byte, error) { + ctx := takeEncodeRuntimeContext() + b := ctx.buf[:0] + if v == nil { + b = encodeNull(b) + b = encodeComma(b) + return b, nil + } + header := (*interfaceHeader)(unsafe.Pointer(&v)) + typ := header.typ + + typeptr := uintptr(unsafe.Pointer(typ)) + codeSet, err := encodeCompileToGetCodeSet(typeptr) + if err != nil { + return nil, err + } + + p := uintptr(header.ptr) + ctx.init(p, codeSet.codeLength) + buf, err := encodeRunCode(ctx, b, codeSet, opt) + if err != nil { + releaseEncodeRuntimeContext(ctx) + return nil, err + } + + ctx.buf = buf + releaseEncodeRuntimeContext(ctx) + return buf, nil +} + func encodeIndentWithOpt(v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) { ctx := takeEncodeRuntimeContext() b := ctx.buf[:0] diff --git a/json.go b/json.go index 662739b..ee21941 100644 --- a/json.go +++ b/json.go @@ -159,7 +159,7 @@ func Marshal(v interface{}) ([]byte, error) { // MarshalNoEscape func MarshalNoEscape(v interface{}) ([]byte, error) { - return marshal(v, EncodeOptionHTMLEscape) + return marshalNoEscape(v, EncodeOptionHTMLEscape) } // MarshalWithOption returns the JSON encoding of v with EncodeOption.