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-12-24 21:53:48 +03:00
w io . Writer
ctx * encodeRuntimeContext
ptr unsafe . Pointer
buf [ ] byte
enabledIndent bool
enabledHTMLEscape bool
unorderedMap bool
prefix [ ] byte
indentStr [ ] byte
2020-08-12 12:42:29 +03:00
}
type compiledCode struct {
code * opcode
2020-04-19 13:51:22 +03:00
}
const (
bufSize = 1024
)
2020-12-25 16:26:59 +03:00
const (
opCodeEscapedType = iota
opCodeEscapedIndentType
opCodeNoEscapeType
opCodeNoEscapeIndentType
)
2020-05-02 17:35:41 +03:00
type opcodeSet struct {
2020-12-25 16:26:59 +03:00
escapedCode * opcode
escapedCodeIndent * opcode
code * opcode
codeIndent * opcode
codeLength int
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-12-20 14:09:41 +03:00
ctx : & encodeRuntimeContext {
ptrs : make ( [ ] uintptr , 128 ) ,
keepRefs : make ( [ ] unsafe . Pointer , 0 , 8 ) ,
} ,
2020-12-24 21:53:48 +03:00
buf : make ( [ ] byte , 0 , bufSize ) ,
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-24 21:53:48 +03:00
buf , err := e . encode ( v )
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
}
2020-12-24 21:53:48 +03:00
e . buf = 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-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-24 21:53:48 +03:00
buf , err := e . encode ( v )
if err != nil {
2020-05-02 17:35:41 +03:00
return nil , err
}
2020-12-24 21:53:48 +03:00
e . buf = buf
2020-05-02 17:35:41 +03:00
if e . enabledIndent {
2020-12-24 21:53:48 +03:00
copied := make ( [ ] byte , len ( buf ) - 2 )
copy ( copied , buf )
2020-05-02 17:35:41 +03:00
return copied , nil
}
2020-12-24 21:53:48 +03:00
copied := make ( [ ] byte , len ( buf ) - 1 )
copy ( copied , buf )
2020-05-02 17:35:41 +03:00
return copied , nil
}
2020-12-19 16:40:03 +03:00
func ( e * Encoder ) encode ( v interface { } ) ( [ ] byte , error ) {
2020-12-24 21:53:48 +03:00
b := e . buf [ : 0 ]
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-12-25 16:26:59 +03:00
if e . enabledHTMLEscape {
code = codeSet . escapedCodeIndent
} else {
code = codeSet . codeIndent
}
2020-05-02 17:35:41 +03:00
} else {
2020-12-25 16:26:59 +03:00
if e . enabledHTMLEscape {
code = codeSet . escapedCode
} else {
code = codeSet . code
}
2020-05-02 17:35:41 +03:00
}
2020-12-20 14:09:41 +03:00
ctx := e . ctx
2020-05-02 17:35:41 +03:00
p := uintptr ( header . ptr )
2020-12-20 14:09:41 +03:00
ctx . init ( p , codeSet . codeLength )
return e . run ( ctx , b , code )
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
code , err := e . compileHead ( & encodeCompileContext {
2020-12-24 21:53:48 +03:00
typ : copiedType ,
root : true ,
structTypeToCompiledCode : map [ uintptr ] * compiledCode { } ,
2020-08-29 09:11:31 +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
}
2020-09-15 14:47:41 +03:00
code = copyOpcode ( code )
2020-12-25 11:03:56 +03:00
codeIndent := toIndent ( code )
2020-08-31 15:59:22 +03:00
codeLength := code . totalLength ( )
2020-08-09 11:48:28 +03:00
codeSet := & opcodeSet {
2020-12-25 16:26:59 +03:00
escapedCode : toEscaped ( code ) ,
escapedCodeIndent : toEscaped ( codeIndent ) ,
code : code ,
codeIndent : codeIndent ,
codeLength : codeLength ,
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-12-20 14:09:41 +03:00
ctx := e . ctx
ctx . init ( p , codeLength )
2020-08-30 21:14:37 +03:00
var c * opcode
2020-05-02 17:35:41 +03:00
if e . enabledIndent {
2020-12-25 16:26:59 +03:00
if e . enabledHTMLEscape {
c = codeSet . escapedCodeIndent
} else {
c = codeSet . codeIndent
}
2020-08-30 21:14:37 +03:00
} else {
2020-12-25 16:26:59 +03:00
if e . enabledHTMLEscape {
c = codeSet . escapedCode
} else {
c = codeSet . 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 {
return nil , err
2020-05-02 17:35:41 +03:00
}
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 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 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
}