2020-04-19 13:51:22 +03:00
package json
import (
2021-06-12 11:06:26 +03:00
"context"
2020-04-21 08:19:50 +03:00
"io"
2022-03-24 23:09:22 +03:00
"os"
2020-04-19 13:51:22 +03:00
"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-05-31 21:09:47 +03:00
"github.com/goccy/go-json/internal/encoder/vm_color"
"github.com/goccy/go-json/internal/encoder/vm_color_indent"
2021-03-18 09:46:55 +03:00
"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
}
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-04-20 14:12:32 +03:00
ctx := encoder . TakeRuntimeContext ( )
2021-06-12 11:06:26 +03:00
ctx . Option . Flag = 0
2021-02-01 05:36:41 +03:00
err := e . encodeWithOption ( ctx , v , optFuncs ... )
2021-04-20 14:12:32 +03:00
encoder . ReleaseRuntimeContext ( ctx )
2021-02-01 05:36:41 +03:00
return err
}
2021-06-12 11:06:26 +03:00
// EncodeContext call Encode with context.Context and EncodeOption.
func ( e * Encoder ) EncodeContext ( ctx context . Context , v interface { } , optFuncs ... EncodeOptionFunc ) error {
rctx := encoder . TakeRuntimeContext ( )
rctx . Option . Flag = 0
rctx . Option . Flag |= encoder . ContextOption
rctx . Option . Context = ctx
2023-10-13 06:33:43 +03:00
err := e . encodeWithOption ( rctx , v , optFuncs ... ) //nolint: contextcheck
2021-06-12 11:06:26 +03:00
encoder . ReleaseRuntimeContext ( rctx )
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
if e . enabledHTMLEscape {
2021-06-01 07:40:15 +03:00
ctx . Option . Flag |= encoder . HTMLEscapeOption
2021-01-31 16:45:59 +03:00
}
2021-12-30 05:54:29 +03:00
ctx . Option . Flag |= encoder . NormalizeUTF8Option
2022-03-24 23:09:22 +03:00
ctx . Option . DebugOut = os . Stdout
2021-01-31 16:45:59 +03:00
for _ , optFunc := range optFuncs {
2021-05-31 16:25:33 +03:00
optFunc ( ctx . Option )
2021-01-31 16:45:59 +03:00
}
var (
buf [ ] byte
err error
)
if e . enabledIndent {
2021-05-31 16:25:33 +03:00
buf , err = encodeIndent ( ctx , v , e . prefix , e . indentStr )
2021-01-31 16:45:59 +03:00
} else {
2021-05-31 16:25:33 +03:00
buf , err = encode ( ctx , v )
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-06-12 11:06:26 +03:00
func marshalContext ( ctx context . Context , v interface { } , optFuncs ... EncodeOptionFunc ) ( [ ] byte , error ) {
rctx := encoder . TakeRuntimeContext ( )
rctx . Option . Flag = 0
2021-12-30 05:54:29 +03:00
rctx . Option . Flag = encoder . HTMLEscapeOption | encoder . NormalizeUTF8Option | encoder . ContextOption
2021-06-12 11:06:26 +03:00
rctx . Option . Context = ctx
for _ , optFunc := range optFuncs {
optFunc ( rctx . Option )
}
2023-10-13 06:33:43 +03:00
buf , err := encode ( rctx , v ) //nolint: contextcheck
2021-06-12 11:06:26 +03:00
if err != nil {
encoder . ReleaseRuntimeContext ( rctx )
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 )
encoder . ReleaseRuntimeContext ( rctx )
return copied , nil
}
2021-05-31 16:25:33 +03:00
func marshal ( v interface { } , optFuncs ... EncodeOptionFunc ) ( [ ] byte , error ) {
2021-04-20 14:12:32 +03:00
ctx := encoder . TakeRuntimeContext ( )
2021-02-01 05:36:41 +03:00
2021-06-01 07:40:15 +03:00
ctx . Option . Flag = 0
2021-12-30 05:54:29 +03:00
ctx . Option . Flag |= ( encoder . HTMLEscapeOption | encoder . NormalizeUTF8Option )
2021-05-31 16:25:33 +03:00
for _ , optFunc := range optFuncs {
optFunc ( ctx . Option )
}
buf , err := encode ( ctx , v )
2020-12-24 21:53:48 +03:00
if err != nil {
2021-04-20 14:12:32 +03:00
encoder . ReleaseRuntimeContext ( 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-04-20 14:12:32 +03:00
encoder . ReleaseRuntimeContext ( ctx )
2020-05-02 17:35:41 +03:00
return copied , nil
}
2021-05-31 16:25:33 +03:00
func marshalNoEscape ( v interface { } ) ( [ ] byte , error ) {
2021-04-20 14:12:32 +03:00
ctx := encoder . TakeRuntimeContext ( )
2021-02-01 05:36:41 +03:00
2021-06-01 07:40:15 +03:00
ctx . Option . Flag = 0
2021-12-30 05:54:29 +03:00
ctx . Option . Flag |= ( encoder . HTMLEscapeOption | encoder . NormalizeUTF8Option )
2021-05-31 16:25:33 +03:00
buf , err := encodeNoEscape ( ctx , v )
2021-01-31 16:53:01 +03:00
if err != nil {
2021-04-20 14:12:32 +03:00
encoder . ReleaseRuntimeContext ( 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-04-20 14:12:32 +03:00
encoder . ReleaseRuntimeContext ( ctx )
2021-01-31 16:53:01 +03:00
return copied , nil
}
2021-05-31 16:25:33 +03:00
func marshalIndent ( v interface { } , prefix , indent string , optFuncs ... EncodeOptionFunc ) ( [ ] byte , error ) {
2021-04-20 14:12:32 +03:00
ctx := encoder . TakeRuntimeContext ( )
2021-02-01 05:36:41 +03:00
2021-06-01 07:40:15 +03:00
ctx . Option . Flag = 0
2021-12-30 05:54:29 +03:00
ctx . Option . Flag |= ( encoder . HTMLEscapeOption | encoder . NormalizeUTF8Option | encoder . IndentOption )
2021-05-31 16:25:33 +03:00
for _ , optFunc := range optFuncs {
optFunc ( ctx . Option )
}
buf , err := encodeIndent ( ctx , v , prefix , indent )
2021-01-31 16:45:59 +03:00
if err != nil {
2021-04-20 14:12:32 +03:00
encoder . ReleaseRuntimeContext ( 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
2021-04-20 14:12:32 +03:00
encoder . ReleaseRuntimeContext ( ctx )
2021-01-31 16:45:59 +03:00
return copied , nil
}
2021-05-31 16:25:33 +03:00
func encode ( ctx * encoder . RuntimeContext , v interface { } ) ( [ ] byte , error ) {
2021-03-17 06:32:23 +03:00
b := ctx . Buf [ : 0 ]
2021-01-31 16:45:59 +03:00
if v == nil {
2021-05-31 19:36:30 +03:00
b = encoder . AppendNull ( ctx , b )
b = encoder . AppendComma ( ctx , 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 ) )
2022-01-03 06:33:51 +03:00
codeSet , err := encoder . CompileToGetCodeSet ( ctx , 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-05-31 19:25:05 +03:00
ctx . Init ( p , codeSet . CodeLength )
2021-03-17 06:32:23 +03:00
ctx . KeepRefs = append ( ctx . KeepRefs , header . ptr )
2021-01-31 16:45:59 +03:00
2021-05-31 16:25:33 +03:00
buf , err := encodeRunCode ( ctx , b , codeSet )
2021-03-17 06:32:23 +03:00
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-05-31 16:25:33 +03:00
func encodeNoEscape ( ctx * encoder . RuntimeContext , v interface { } ) ( [ ] byte , error ) {
2021-03-17 06:32:23 +03:00
b := ctx . Buf [ : 0 ]
2021-01-31 16:53:01 +03:00
if v == nil {
2021-05-31 19:36:30 +03:00
b = encoder . AppendNull ( ctx , b )
b = encoder . AppendComma ( ctx , 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 ) )
2022-01-03 06:33:51 +03:00
codeSet , err := encoder . CompileToGetCodeSet ( ctx , typeptr )
2021-01-31 16:53:01 +03:00
if err != nil {
return nil , err
}
p := uintptr ( header . ptr )
2021-05-31 19:25:05 +03:00
ctx . Init ( p , codeSet . CodeLength )
2021-05-31 16:25:33 +03:00
buf , err := encodeRunCode ( ctx , b , codeSet )
2021-01-31 16:53:01 +03:00
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-05-31 16:25:33 +03:00
func encodeIndent ( ctx * encoder . RuntimeContext , v interface { } , prefix , indent string ) ( [ ] byte , error ) {
2021-03-18 09:46:55 +03:00
b := ctx . Buf [ : 0 ]
2021-01-31 16:45:59 +03:00
if v == nil {
2021-05-31 19:36:30 +03:00
b = encoder . AppendNull ( ctx , b )
b = encoder . AppendCommaIndent ( ctx , 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 ) )
2022-01-03 06:33:51 +03:00
codeSet , err := encoder . CompileToGetCodeSet ( ctx , 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-05-31 19:25:05 +03:00
ctx . Init ( p , codeSet . CodeLength )
2021-05-31 16:25:33 +03:00
buf , err := encodeRunIndentCode ( ctx , b , codeSet , prefix , indent )
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-05-31 16:25:33 +03:00
func encodeRunCode ( ctx * encoder . RuntimeContext , b [ ] byte , codeSet * encoder . OpcodeSet ) ( [ ] byte , error ) {
2021-06-01 07:40:15 +03:00
if ( ctx . Option . Flag & encoder . DebugOption ) != 0 {
if ( ctx . Option . Flag & encoder . ColorizeOption ) != 0 {
2021-05-31 21:09:47 +03:00
return vm_color . DebugRun ( ctx , b , codeSet )
}
2021-05-31 19:25:05 +03:00
return vm . DebugRun ( ctx , b , codeSet )
2021-01-31 16:45:59 +03:00
}
2021-06-01 07:40:15 +03:00
if ( ctx . Option . Flag & encoder . ColorizeOption ) != 0 {
2021-05-31 21:09:47 +03:00
return vm_color . Run ( ctx , b , codeSet )
}
2021-05-31 16:25:33 +03:00
return vm . Run ( ctx , b , codeSet )
2021-01-31 16:45:59 +03:00
}
2021-05-31 16:25:33 +03:00
func encodeRunIndentCode ( ctx * encoder . RuntimeContext , b [ ] byte , codeSet * encoder . OpcodeSet , prefix , indent string ) ( [ ] byte , error ) {
2021-03-18 09:46:55 +03:00
ctx . Prefix = [ ] byte ( prefix )
ctx . IndentStr = [ ] byte ( indent )
2021-06-01 07:40:15 +03:00
if ( ctx . Option . Flag & encoder . DebugOption ) != 0 {
if ( ctx . Option . Flag & encoder . ColorizeOption ) != 0 {
2021-05-31 21:09:47 +03:00
return vm_color_indent . DebugRun ( ctx , b , codeSet )
}
2021-05-31 19:25:05 +03:00
return vm_indent . DebugRun ( ctx , b , codeSet )
2021-03-11 13:29:35 +03:00
}
2021-06-01 07:40:15 +03:00
if ( ctx . Option . Flag & encoder . ColorizeOption ) != 0 {
2021-05-31 21:09:47 +03:00
return vm_color_indent . Run ( ctx , b , codeSet )
}
2021-05-31 16:25:33 +03:00
return vm_indent . Run ( ctx , b , codeSet )
2021-03-11 13:29:35 +03:00
}