2020-04-19 13:51:22 +03:00
package json
import (
2020-05-02 17:35:41 +03:00
"bytes"
2020-05-08 14:22:57 +03:00
"encoding"
2020-09-15 17:22:35 +03:00
"encoding/base64"
2020-04-21 08:19:50 +03:00
"io"
2020-08-20 09:36:00 +03:00
"math"
2020-04-19 13:51:22 +03:00
"reflect"
"strconv"
"sync"
2020-12-15 08:46:10 +03:00
"sync/atomic"
2020-04-19 13:51:22 +03:00
"unsafe"
)
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-08-12 12:42:29 +03:00
w io . Writer
buf [ ] byte
enabledIndent bool
enabledHTMLEscape bool
2020-09-16 12:15:47 +03:00
unorderedMap bool
2020-08-12 12:42:29 +03:00
prefix [ ] byte
indentStr [ ] byte
structTypeToCompiledCode map [ uintptr ] * compiledCode
structTypeToCompiledIndentCode map [ uintptr ] * compiledCode
}
type compiledCode struct {
code * opcode
2020-04-19 13:51:22 +03:00
}
const (
bufSize = 1024
)
2020-05-02 17:35:41 +03:00
type opcodeSet struct {
2020-09-01 16:39:20 +03:00
codeIndent * opcode
code * opcode
2020-08-30 11:52:59 +03:00
ctx sync . Pool
2020-05-02 17:35:41 +03:00
}
2020-12-15 08:46:10 +03:00
func loadOpcodeMap ( ) map [ uintptr ] * opcodeSet {
p := atomic . LoadPointer ( & cachedOpcode )
return * ( * map [ uintptr ] * opcodeSet ) ( unsafe . Pointer ( & p ) )
2020-04-28 12:25:51 +03:00
}
2020-12-15 08:46:10 +03:00
func storeOpcodeSet ( typ uintptr , set * opcodeSet , m map [ uintptr ] * opcodeSet ) {
newOpcodeMap := make ( map [ uintptr ] * opcodeSet , len ( m ) + 1 )
newOpcodeMap [ typ ] = set
for k , v := range m {
newOpcodeMap [ k ] = v
}
atomic . StorePointer ( & cachedOpcode , * ( * unsafe . Pointer ) ( unsafe . Pointer ( & newOpcodeMap ) ) )
2020-04-28 12:25:51 +03:00
}
2020-04-19 13:51:22 +03:00
var (
2020-05-04 12:39:17 +03:00
encPool sync . Pool
2020-08-09 11:48:28 +03:00
codePool sync . Pool
2020-12-15 08:46:10 +03:00
cachedOpcode unsafe . Pointer // map[uintptr]*opcodeSet
2020-05-04 12:39:17 +03:00
marshalJSONType reflect . Type
marshalTextType reflect . Type
2020-04-19 13:51:22 +03:00
)
func init ( ) {
encPool = sync . Pool {
New : func ( ) interface { } {
return & Encoder {
2020-08-12 12:42:29 +03:00
buf : make ( [ ] byte , 0 , bufSize ) ,
structTypeToCompiledCode : map [ uintptr ] * compiledCode { } ,
structTypeToCompiledIndentCode : map [ uintptr ] * compiledCode { } ,
2020-04-19 13:51:22 +03:00
}
} ,
}
2020-05-04 12:39:17 +03:00
marshalJSONType = reflect . TypeOf ( ( * Marshaler ) ( nil ) ) . Elem ( )
2020-05-08 14:22:57 +03:00
marshalTextType = reflect . TypeOf ( ( * encoding . TextMarshaler ) ( nil ) ) . Elem ( )
2020-04-19 13:51:22 +03:00
}
2020-04-21 08:19:50 +03:00
// NewEncoder returns a new encoder that writes to w.
func NewEncoder ( w io . Writer ) * Encoder {
2020-04-19 13:51:22 +03:00
enc := encPool . Get ( ) . ( * Encoder )
2020-04-21 08:19:50 +03:00
enc . w = w
enc . reset ( )
2020-04-19 13:51:22 +03:00
return enc
}
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.
func ( e * Encoder ) EncodeWithOption ( v interface { } , opts ... EncodeOption ) error {
for _ , opt := range opts {
if err := opt ( e ) ; err != nil {
return err
}
}
2020-12-19 16:40:03 +03:00
var err error
if e . buf , err = e . encode ( v ) ; err != nil {
2020-04-21 08:19:50 +03:00
return err
}
2020-11-16 13:10:46 +03:00
if e . enabledIndent {
e . buf = e . buf [ : len ( e . buf ) - 2 ]
} else {
e . buf = e . buf [ : len ( e . buf ) - 1 ]
}
2020-09-16 19:26:39 +03:00
e . buf = append ( e . buf , '\n' )
2020-04-21 08:19:50 +03:00
if _ , err := e . w . Write ( e . buf ) ; err != nil {
return err
}
2020-09-16 19:26:39 +03:00
e . buf = e . buf [ : 0 ]
2020-04-21 08:19:50 +03:00
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
}
e . prefix = [ ] byte ( prefix )
e . indentStr = [ ] byte ( indent )
e . enabledIndent = true
2020-04-21 08:19:50 +03:00
}
func ( e * Encoder ) release ( ) {
e . w = nil
2020-08-15 16:45:29 +03:00
encPool . Put ( e )
2020-04-19 13:51:22 +03:00
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) reset ( ) {
2020-04-19 13:51:22 +03:00
e . buf = e . buf [ : 0 ]
2020-05-03 11:41:33 +03:00
e . enabledHTMLEscape = true
e . enabledIndent = false
2020-09-16 12:15:47 +03:00
e . unorderedMap = false
2020-04-19 13:51:22 +03:00
}
2020-05-02 17:35:41 +03:00
func ( e * Encoder ) encodeForMarshal ( v interface { } ) ( [ ] byte , error ) {
2020-12-19 16:40:03 +03:00
var err error
if e . buf , err = e . encode ( v ) ; err != nil {
2020-05-02 17:35:41 +03:00
return nil , err
}
if e . enabledIndent {
2020-11-16 13:10:46 +03:00
copied := make ( [ ] byte , len ( e . buf ) - 2 )
copy ( copied , e . buf )
2020-05-02 17:35:41 +03:00
return copied , nil
}
2020-11-16 13:10:46 +03:00
copied := make ( [ ] byte , len ( e . buf ) - 1 )
2020-05-02 17:35:41 +03:00
copy ( copied , e . buf )
return copied , nil
}
2020-12-19 16:40:03 +03:00
func ( e * Encoder ) encode ( v interface { } ) ( [ ] byte , error ) {
b := e . buf
2020-08-21 05:07:55 +03:00
if v == nil {
2020-12-19 16:40:03 +03:00
b = encodeNull ( b )
2020-11-16 13:10:46 +03:00
if e . enabledIndent {
2020-12-19 16:40:03 +03:00
b = encodeIndentComma ( b )
2020-11-16 13:10:46 +03:00
} else {
2020-12-19 16:40:03 +03:00
b = encodeComma ( b )
2020-11-16 13:10:46 +03:00
}
2020-12-19 16:40:03 +03:00
return b , nil
2020-08-21 05:07:55 +03:00
}
2020-05-02 17:35:41 +03:00
header := ( * interfaceHeader ) ( unsafe . Pointer ( & v ) )
typ := header . typ
2020-05-04 12:39:17 +03:00
2020-05-03 16:19:55 +03:00
typeptr := uintptr ( unsafe . Pointer ( typ ) )
2020-12-15 08:46:10 +03:00
opcodeMap := loadOpcodeMap ( )
if codeSet , exists := opcodeMap [ typeptr ] ; exists {
2020-05-02 17:35:41 +03:00
var code * opcode
if e . enabledIndent {
2020-09-01 16:39:20 +03:00
code = codeSet . codeIndent
2020-05-02 17:35:41 +03:00
} else {
2020-09-01 16:39:20 +03:00
code = codeSet . code
2020-05-02 17:35:41 +03:00
}
2020-08-30 17:58:58 +03:00
ctx := codeSet . ctx . Get ( ) . ( * encodeRuntimeContext )
2020-05-02 17:35:41 +03:00
p := uintptr ( header . ptr )
2020-08-30 11:52:59 +03:00
ctx . init ( p )
2020-12-19 16:40:03 +03:00
b , err := e . run ( ctx , b , code )
2020-08-30 17:58:58 +03:00
codeSet . ctx . Put ( ctx )
2020-12-19 16:40:03 +03:00
return b , err
2020-05-02 17:35:41 +03:00
}
2020-05-03 16:19:55 +03:00
2020-05-08 17:59:49 +03:00
// noescape trick for header.typ ( reflect.*rtype )
2020-11-14 23:27:15 +03:00
copiedType := * ( * * rtype ) ( unsafe . Pointer ( & typeptr ) )
2020-05-03 16:19:55 +03:00
2020-08-29 09:11:31 +03:00
codeIndent , err := e . compileHead ( & encodeCompileContext {
typ : copiedType ,
root : true ,
withIndent : true ,
} )
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
}
2020-08-29 09:11:31 +03:00
code , err := e . compileHead ( & encodeCompileContext {
typ : copiedType ,
root : true ,
withIndent : false ,
} )
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
}
2020-09-15 14:47:41 +03:00
codeIndent = copyOpcode ( codeIndent )
code = copyOpcode ( code )
2020-08-31 15:59:22 +03:00
codeLength := code . totalLength ( )
2020-08-09 11:48:28 +03:00
codeSet := & opcodeSet {
2020-09-01 16:39:20 +03:00
codeIndent : codeIndent ,
code : code ,
2020-08-30 11:52:59 +03:00
ctx : sync . Pool {
New : func ( ) interface { } {
2020-08-30 17:58:58 +03:00
return & encodeRuntimeContext {
2020-09-04 14:28:27 +03:00
ptrs : make ( [ ] uintptr , codeLength ) ,
keepRefs : make ( [ ] unsafe . Pointer , 8 ) ,
2020-08-30 17:58:58 +03:00
}
2020-08-30 11:52:59 +03:00
} ,
} ,
2020-08-09 11:48:28 +03:00
}
2020-12-15 08:46:10 +03:00
storeOpcodeSet ( typeptr , codeSet , opcodeMap )
2020-05-02 17:35:41 +03:00
p := uintptr ( header . ptr )
2020-08-30 17:58:58 +03:00
ctx := codeSet . ctx . Get ( ) . ( * encodeRuntimeContext )
2020-08-30 11:52:59 +03:00
ctx . init ( p )
2020-08-30 21:14:37 +03:00
var c * opcode
2020-05-02 17:35:41 +03:00
if e . enabledIndent {
2020-08-30 21:14:37 +03:00
c = codeIndent
} else {
c = code
2020-08-30 17:58:58 +03:00
}
2020-08-30 21:14:37 +03:00
2020-12-19 16:40:03 +03:00
b , err = e . run ( ctx , b , c )
if err != nil {
2020-08-30 17:58:58 +03:00
codeSet . ctx . Put ( ctx )
2020-12-19 16:40:03 +03:00
return nil , err
2020-05-02 17:35:41 +03:00
}
2020-08-30 17:58:58 +03:00
codeSet . ctx . Put ( ctx )
2020-12-19 16:40:03 +03:00
return b , nil
2020-05-02 17:35:41 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeInt ( b [ ] byte , v int ) [ ] byte {
2020-12-19 21:42:11 +03:00
return appendInt ( b , int64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeInt8 ( b [ ] byte , v int8 ) [ ] byte {
2020-12-19 21:42:11 +03:00
return appendInt ( b , int64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeInt16 ( b [ ] byte , v int16 ) [ ] byte {
2020-12-19 21:42:11 +03:00
return appendInt ( b , int64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeInt32 ( b [ ] byte , v int32 ) [ ] byte {
2020-12-19 21:42:11 +03:00
return appendInt ( b , int64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeInt64 ( b [ ] byte , v int64 ) [ ] byte {
2020-12-19 21:42:11 +03:00
return appendInt ( b , v )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeUint ( b [ ] byte , v uint ) [ ] byte {
2020-12-19 21:42:11 +03:00
return appendUint ( b , uint64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeUint8 ( b [ ] byte , v uint8 ) [ ] byte {
2020-12-19 21:42:11 +03:00
return appendUint ( b , uint64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeUint16 ( b [ ] byte , v uint16 ) [ ] byte {
2020-12-19 21:42:11 +03:00
return appendUint ( b , uint64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeUint32 ( b [ ] byte , v uint32 ) [ ] byte {
2020-12-19 21:42:11 +03:00
return appendUint ( b , uint64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeUint64 ( b [ ] byte , v uint64 ) [ ] byte {
2020-12-19 21:42:11 +03:00
return appendUint ( b , v )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeFloat32 ( b [ ] byte , v float32 ) [ ] byte {
2020-08-20 09:36:00 +03:00
f64 := float64 ( v )
abs := math . Abs ( f64 )
fmt := byte ( 'f' )
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
if abs != 0 {
f32 := float32 ( abs )
if f32 < 1e-6 || f32 >= 1e21 {
fmt = 'e'
}
}
2020-12-19 16:40:03 +03:00
return strconv . AppendFloat ( b , f64 , fmt , - 1 , 32 )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeFloat64 ( b [ ] byte , v float64 ) [ ] byte {
2020-08-20 09:36:00 +03:00
abs := math . Abs ( v )
fmt := byte ( 'f' )
// Note: Must use float32 comparisons for underlying float32 value to get precise cutoffs right.
if abs != 0 {
if abs < 1e-6 || abs >= 1e21 {
fmt = 'e'
}
}
2020-12-19 16:40:03 +03:00
return strconv . AppendFloat ( b , v , fmt , - 1 , 64 )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeBool ( b [ ] byte , v bool ) [ ] byte {
if v {
return append ( b , "true" ... )
}
return append ( b , "false" ... )
}
func encodeBytes ( dst [ ] byte , src [ ] byte ) [ ] byte {
return append ( dst , src ... )
}
func encodeNull ( b [ ] byte ) [ ] byte {
return append ( b , "null" ... )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeComma ( b [ ] byte ) [ ] byte {
return append ( b , ',' )
2020-04-30 05:56:56 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeIndentComma ( b [ ] byte ) [ ] byte {
return append ( b , ',' , '\n' )
2020-05-03 11:41:33 +03:00
}
2020-12-19 16:40:03 +03:00
func ( e * Encoder ) encodeKey ( b [ ] byte , code * opcode ) [ ] byte {
2020-09-16 19:26:39 +03:00
if e . enabledHTMLEscape {
2020-12-19 16:40:03 +03:00
return append ( b , code . escapedKey ... )
2020-09-16 19:26:39 +03:00
}
2020-12-19 16:40:03 +03:00
return append ( b , code . key ... )
2020-09-16 19:26:39 +03:00
}
2020-12-19 16:40:03 +03:00
func ( e * Encoder ) encodeString ( b [ ] byte , s string ) [ ] byte {
2020-05-03 11:41:33 +03:00
if e . enabledHTMLEscape {
2020-12-19 16:40:03 +03:00
return encodeEscapedString ( b , s )
2020-05-03 11:41:33 +03:00
}
2020-12-19 16:40:03 +03:00
return encodeNoEscapedString ( b , s )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func encodeByteSlice ( b [ ] byte , src [ ] byte ) [ ] byte {
encodedLen := base64 . StdEncoding . EncodedLen ( len ( src ) )
b = append ( b , '"' )
pos := len ( b )
remainLen := cap ( b [ pos : ] )
2020-09-15 17:22:35 +03:00
var buf [ ] byte
if remainLen > encodedLen {
2020-12-19 16:40:03 +03:00
buf = b [ pos : pos + encodedLen ]
2020-09-15 17:22:35 +03:00
} else {
buf = make ( [ ] byte , encodedLen )
}
2020-12-19 16:40:03 +03:00
base64 . StdEncoding . Encode ( buf , src )
return append ( append ( b , buf ... ) , '"' )
2020-04-19 13:51:22 +03:00
}
2020-12-19 16:40:03 +03:00
func ( e * Encoder ) encodeIndent ( b [ ] byte , indent int ) [ ] byte {
b = append ( b , e . prefix ... )
return append ( b , bytes . Repeat ( e . indentStr , indent ) ... )
2020-04-28 12:25:51 +03:00
}