2020-04-19 13:51:22 +03:00
package json
import (
2020-04-21 08:19:50 +03:00
"io"
2020-04-19 13:51:22 +03:00
"sync"
"unsafe"
2021-03-15 20:50:19 +03:00
2021-03-16 18:22:19 +03:00
"github.com/goccy/go-json/internal/encoder"
"github.com/goccy/go-json/internal/encoder/vm"
2021-03-17 06:32:23 +03:00
"github.com/goccy/go-json/internal/encoder/vm_escaped"
2021-03-18 09:46:55 +03:00
"github.com/goccy/go-json/internal/encoder/vm_escaped_indent"
"github.com/goccy/go-json/internal/encoder/vm_indent"
2020-04-19 13:51:22 +03:00
)
2020-04-21 08:19:50 +03:00
// An Encoder writes JSON values to an output stream.
2020-04-19 13:51:22 +03:00
type Encoder struct {
2020-12-24 21:53:48 +03:00
w io . Writer
enabledIndent bool
enabledHTMLEscape bool
2021-01-31 16:45:59 +03:00
prefix string
indentStr string
2020-04-19 13:51:22 +03:00
}
const (
2021-01-31 16:45:59 +03:00
bufSize = 1024
2020-04-19 13:51:22 +03:00
)
2021-01-31 16:45:59 +03:00
type EncodeOption int
2020-12-25 16:26:59 +03:00
const (
2021-01-31 16:45:59 +03:00
EncodeOptionHTMLEscape EncodeOption = 1 << iota
EncodeOptionIndent
EncodeOptionUnorderedMap
2020-12-25 16:26:59 +03:00
)
2020-04-19 13:51:22 +03:00
var (
2021-01-31 16:45:59 +03:00
encRuntimeContextPool = sync . Pool {
2021-03-17 06:32:23 +03:00
New : func ( ) interface { } {
return & encoder . RuntimeContext {
Buf : make ( [ ] byte , 0 , bufSize ) ,
Ptrs : make ( [ ] uintptr , 128 ) ,
KeepRefs : make ( [ ] unsafe . Pointer , 0 , 8 ) ,
}
} ,
}
2021-01-31 16:45:59 +03:00
)
2021-03-18 09:46:55 +03:00
func takeEncodeRuntimeContext ( ) * encoder . RuntimeContext {
return encRuntimeContextPool . Get ( ) . ( * encoder . RuntimeContext )
2020-04-19 13:51:22 +03:00
}
2021-03-18 09:46:55 +03:00
func releaseEncodeRuntimeContext ( ctx * encoder . RuntimeContext ) {
2021-01-31 16:45:59 +03:00
encRuntimeContextPool . Put ( ctx )
2020-04-19 13:51:22 +03:00
}
2021-01-31 16:45:59 +03:00
// NewEncoder returns a new encoder that writes to w.
func NewEncoder ( w io . Writer ) * Encoder {
return & Encoder { w : w , enabledHTMLEscape : true }
2021-01-26 18:42:19 +03:00
}
2020-04-21 08:19:50 +03:00
// Encode writes the JSON encoding of v to the stream, followed by a newline character.
//
// See the documentation for Marshal for details about the conversion of Go values to JSON.
func ( e * Encoder ) Encode ( v interface { } ) error {
2020-09-16 12:15:47 +03:00
return e . EncodeWithOption ( v )
}
// EncodeWithOption call Encode with EncodeOption.
2021-01-31 16:45:59 +03:00
func ( e * Encoder ) EncodeWithOption ( v interface { } , optFuncs ... EncodeOptionFunc ) error {
2021-02-01 05:36:41 +03:00
ctx := takeEncodeRuntimeContext ( )
err := e . encodeWithOption ( ctx , v , optFuncs ... )
releaseEncodeRuntimeContext ( ctx )
return err
}
2021-03-18 09:46:55 +03:00
func ( e * Encoder ) encodeWithOption ( ctx * encoder . RuntimeContext , v interface { } , optFuncs ... EncodeOptionFunc ) error {
2021-01-31 16:45:59 +03:00
var opt EncodeOption
if e . enabledHTMLEscape {
opt |= EncodeOptionHTMLEscape
}
for _ , optFunc := range optFuncs {
opt = optFunc ( opt )
}
var (
buf [ ] byte
err error
)
if e . enabledIndent {
2021-02-01 05:36:41 +03:00
buf , err = encodeIndent ( ctx , v , e . prefix , e . indentStr , opt )
2021-01-31 16:45:59 +03:00
} else {
2021-02-01 05:36:41 +03:00
buf , err = encode ( ctx , v , opt )
2020-09-16 12:15:47 +03:00
}
2020-12-24 21:53:48 +03:00
if err != nil {
2020-04-21 08:19:50 +03:00
return err
}
2020-11-16 13:10:46 +03:00
if e . enabledIndent {
2020-12-24 21:53:48 +03:00
buf = buf [ : len ( buf ) - 2 ]
2020-11-16 13:10:46 +03:00
} else {
2020-12-24 21:53:48 +03:00
buf = buf [ : len ( buf ) - 1 ]
2020-11-16 13:10:46 +03:00
}
2020-12-24 21:53:48 +03:00
buf = append ( buf , '\n' )
if _ , err := e . w . Write ( buf ) ; err != nil {
2020-04-21 08:19:50 +03:00
return err
}
return nil
}
// SetEscapeHTML specifies whether problematic HTML characters should be escaped inside JSON quoted strings.
// The default behavior is to escape &, <, and > to \u0026, \u003c, and \u003e to avoid certain safety problems that can arise when embedding JSON in HTML.
//
// In non-HTML settings where the escaping interferes with the readability of the output, SetEscapeHTML(false) disables this behavior.
func ( e * Encoder ) SetEscapeHTML ( on bool ) {
2020-05-03 11:41:33 +03:00
e . enabledHTMLEscape = on
2020-04-21 08:19:50 +03:00
}
// SetIndent instructs the encoder to format each subsequent encoded value as if indented by the package-level function Indent(dst, src, prefix, indent).
// Calling SetIndent("", "") disables indentation.
func ( e * Encoder ) SetIndent ( prefix , indent string ) {
2020-05-02 17:35:41 +03:00
if prefix == "" && indent == "" {
e . enabledIndent = false
return
}
2021-01-31 16:45:59 +03:00
e . prefix = prefix
e . indentStr = indent
2020-05-02 17:35:41 +03:00
e . enabledIndent = true
2020-04-21 08:19:50 +03:00
}
2021-01-31 16:45:59 +03:00
func marshal ( v interface { } , opt EncodeOption ) ( [ ] byte , error ) {
2021-03-18 09:46:55 +03:00
ctx := takeEncodeRuntimeContext ( )
2021-02-01 05:36:41 +03:00
2021-02-01 16:31:39 +03:00
buf , err := encode ( ctx , v , opt | EncodeOptionHTMLEscape )
2020-12-24 21:53:48 +03:00
if err != nil {
2021-03-18 09:46:55 +03:00
releaseEncodeRuntimeContext ( ctx )
2020-05-02 17:35:41 +03:00
return nil , err
}
2020-12-24 21:53:48 +03:00
2021-01-26 18:42:19 +03:00
// 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 ) )
2020-12-24 21:53:48 +03:00
copy ( copied , buf )
2021-02-01 05:36:41 +03:00
2021-03-18 09:46:55 +03:00
releaseEncodeRuntimeContext ( ctx )
2020-05-02 17:35:41 +03:00
return copied , nil
}
2021-01-31 16:53:01 +03:00
func marshalNoEscape ( v interface { } , opt EncodeOption ) ( [ ] byte , error ) {
2021-03-18 09:46:55 +03:00
ctx := takeEncodeRuntimeContext ( )
2021-02-01 05:36:41 +03:00
2021-02-01 16:31:39 +03:00
buf , err := encodeNoEscape ( ctx , v , opt | EncodeOptionHTMLEscape )
2021-01-31 16:53:01 +03:00
if err != nil {
2021-03-18 09:46:55 +03:00
releaseEncodeRuntimeContext ( ctx )
2021-01-31 16:53:01 +03:00
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 )
2021-02-01 05:36:41 +03:00
2021-03-18 09:46:55 +03:00
releaseEncodeRuntimeContext ( ctx )
2021-01-31 16:53:01 +03:00
return copied , nil
}
2021-01-31 16:45:59 +03:00
func marshalIndent ( v interface { } , prefix , indent string , opt EncodeOption ) ( [ ] byte , error ) {
2021-02-01 05:36:41 +03:00
ctx := takeEncodeRuntimeContext ( )
2021-02-01 16:31:39 +03:00
buf , err := encodeIndent ( ctx , v , prefix , indent , opt | EncodeOptionHTMLEscape )
2021-01-31 16:45:59 +03:00
if err != nil {
2021-02-01 05:36:41 +03:00
releaseEncodeRuntimeContext ( ctx )
2021-01-31 16:45:59 +03:00
return nil , err
}
2021-02-01 05:36:41 +03:00
buf = buf [ : len ( buf ) - 2 ]
2021-01-31 16:45:59 +03:00
copied := make ( [ ] byte , len ( buf ) )
copy ( copied , buf )
2021-02-01 05:36:41 +03:00
releaseEncodeRuntimeContext ( ctx )
2021-01-31 16:45:59 +03:00
return copied , nil
}
2021-03-17 06:32:23 +03:00
func encode ( ctx * encoder . RuntimeContext , v interface { } , opt EncodeOption ) ( [ ] byte , error ) {
b := ctx . Buf [ : 0 ]
2021-01-31 16:45:59 +03:00
if v == nil {
2021-03-18 09:46:55 +03:00
b = encoder . AppendNull ( b )
b = encoder . AppendComma ( b )
2020-12-19 16:40:03 +03:00
return b , nil
2020-08-21 05:07:55 +03:00
}
2021-03-13 08:12:31 +03:00
header := ( * emptyInterface ) ( unsafe . Pointer ( & v ) )
2020-05-02 17:35:41 +03:00
typ := header . typ
2020-05-04 12:39:17 +03:00
2020-05-03 16:19:55 +03:00
typeptr := uintptr ( unsafe . Pointer ( typ ) )
2021-03-17 06:32:23 +03:00
codeSet , err := encoder . CompileToGetCodeSet ( typeptr )
2021-01-24 09:17:39 +03:00
if err != nil {
return nil , err
}
2021-01-10 23:16:37 +03:00
2021-01-24 09:17:39 +03:00
p := uintptr ( header . ptr )
2021-03-17 06:32:23 +03:00
ctx . Init ( p , codeSet . CodeLength )
ctx . KeepRefs = append ( ctx . KeepRefs , header . ptr )
2021-01-31 16:45:59 +03:00
2021-03-17 06:32:23 +03:00
buf , err := encodeRunCode ( ctx , b , codeSet , opt )
if err != nil {
return nil , err
2021-01-24 09:17:39 +03:00
}
2021-03-17 06:32:23 +03:00
ctx . Buf = buf
return buf , nil
2021-01-24 09:17:39 +03:00
}
2021-03-17 06:32:23 +03:00
func encodeNoEscape ( ctx * encoder . RuntimeContext , v interface { } , opt EncodeOption ) ( [ ] byte , error ) {
b := ctx . Buf [ : 0 ]
2021-01-31 16:53:01 +03:00
if v == nil {
2021-03-18 09:46:55 +03:00
b = encoder . AppendNull ( b )
b = encoder . AppendComma ( b )
2021-01-31 16:53:01 +03:00
return b , nil
}
2021-03-13 08:12:31 +03:00
header := ( * emptyInterface ) ( unsafe . Pointer ( & v ) )
2021-01-31 16:53:01 +03:00
typ := header . typ
typeptr := uintptr ( unsafe . Pointer ( typ ) )
2021-03-17 06:32:23 +03:00
codeSet , err := encoder . CompileToGetCodeSet ( typeptr )
2021-01-31 16:53:01 +03:00
if err != nil {
return nil , err
}
p := uintptr ( header . ptr )
2021-03-17 06:32:23 +03:00
ctx . Init ( p , codeSet . CodeLength )
2021-01-31 16:53:01 +03:00
buf , err := encodeRunCode ( ctx , b , codeSet , opt )
if err != nil {
return nil , err
}
2021-03-17 06:32:23 +03:00
ctx . Buf = buf
2021-01-31 16:53:01 +03:00
return buf , nil
}
2021-03-18 09:46:55 +03:00
func encodeIndent ( ctx * encoder . RuntimeContext , v interface { } , prefix , indent string , opt EncodeOption ) ( [ ] byte , error ) {
b := ctx . Buf [ : 0 ]
2021-01-31 16:45:59 +03:00
if v == nil {
2021-03-18 09:46:55 +03:00
b = encoder . AppendNull ( b )
b = encoder . AppendCommaIndent ( b )
2021-01-31 16:45:59 +03:00
return b , nil
2021-01-30 19:48:54 +03:00
}
2021-03-13 08:12:31 +03:00
header := ( * emptyInterface ) ( unsafe . Pointer ( & v ) )
2021-01-31 16:45:59 +03:00
typ := header . typ
2021-01-30 19:48:54 +03:00
2021-01-31 16:45:59 +03:00
typeptr := uintptr ( unsafe . Pointer ( typ ) )
2021-03-18 09:46:55 +03:00
codeSet , err := encoder . CompileToGetCodeSet ( typeptr )
2021-01-30 19:48:54 +03:00
if err != nil {
return nil , err
}
2021-01-31 16:45:59 +03:00
p := uintptr ( header . ptr )
2021-03-18 09:46:55 +03:00
ctx . Init ( p , codeSet . CodeLength )
2021-01-31 16:45:59 +03:00
buf , err := encodeRunIndentCode ( ctx , b , codeSet , prefix , indent , opt )
2020-05-03 16:19:55 +03:00
2021-03-18 09:46:55 +03:00
ctx . KeepRefs = append ( ctx . KeepRefs , header . ptr )
2020-05-03 16:19:55 +03:00
2020-05-02 17:35:41 +03:00
if err != nil {
2020-12-19 16:40:03 +03:00
return nil , err
2020-05-02 17:35:41 +03:00
}
2021-01-31 16:45:59 +03:00
2021-03-18 09:46:55 +03:00
ctx . Buf = buf
2021-01-31 16:45:59 +03:00
return buf , nil
}
2021-03-17 06:32:23 +03:00
func encodeRunCode ( ctx * encoder . RuntimeContext , b [ ] byte , codeSet * encoder . OpcodeSet , opt EncodeOption ) ( [ ] byte , error ) {
2021-01-31 16:45:59 +03:00
if ( opt & EncodeOptionHTMLEscape ) != 0 {
2021-03-17 06:32:23 +03:00
return vm_escaped . Run ( ctx , b , codeSet , encoder . Option ( opt ) )
2021-01-31 16:45:59 +03:00
}
2021-03-17 06:32:23 +03:00
return vm . Run ( ctx , b , codeSet , encoder . Option ( opt ) )
2021-01-31 16:45:59 +03:00
}
2021-03-18 09:46:55 +03:00
func encodeRunIndentCode ( ctx * encoder . RuntimeContext , b [ ] byte , codeSet * encoder . OpcodeSet , prefix , indent string , opt EncodeOption ) ( [ ] byte , error ) {
ctx . Prefix = [ ] byte ( prefix )
ctx . IndentStr = [ ] byte ( indent )
2021-01-31 16:45:59 +03:00
if ( opt & EncodeOptionHTMLEscape ) != 0 {
2021-03-18 09:46:55 +03:00
return vm_escaped_indent . Run ( ctx , b , codeSet , encoder . Option ( opt ) )
2021-03-11 13:29:35 +03:00
}
2021-03-18 09:46:55 +03:00
return vm_indent . Run ( ctx , b , codeSet , encoder . Option ( opt ) )
2021-03-11 13:29:35 +03:00
}