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"
2021-01-30 19:48:54 +03:00
"fmt"
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
2021-01-24 18:06:16 +03:00
baseIndent int
2020-12-24 21:53:48 +03:00
prefix [ ] byte
indentStr [ ] byte
2020-08-12 12:42:29 +03:00
}
type compiledCode struct {
2021-01-15 10:25:00 +03:00
code * opcode
linked bool // whether recursive code already have linked
curLen uintptr
nextLen uintptr
2020-04-19 13:51:22 +03:00
}
const (
2021-01-30 20:30:48 +03:00
bufSize = 1024
maxAcceptableTypeAddrRange = 1024 * 1024 * 2 // 2 Mib
2020-04-19 13:51:22 +03:00
)
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 {
2021-01-11 13:21:30 +03:00
code * 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 (
2021-01-30 19:48:54 +03:00
encPool sync . Pool
codePool sync . Pool
cachedOpcode unsafe . Pointer // map[uintptr]*opcodeSet
marshalJSONType reflect . Type
marshalTextType reflect . Type
baseTypeAddr uintptr
cachedOpcodeSets [ ] * opcodeSet
2020-04-19 13:51:22 +03:00
)
2021-01-30 19:48:54 +03:00
//go:linkname typelinks reflect.typelinks
func typelinks ( ) ( [ ] unsafe . Pointer , [ ] [ ] int32 )
//go:linkname rtypeOff reflect.rtypeOff
func rtypeOff ( unsafe . Pointer , int32 ) unsafe . Pointer
func setupOpcodeSets ( ) error {
sections , offsets := typelinks ( )
if len ( sections ) != 1 {
return fmt . Errorf ( "failed to get sections" )
}
if len ( offsets ) != 1 {
return fmt . Errorf ( "failed to get offsets" )
}
section := sections [ 0 ]
offset := offsets [ 0 ]
var (
min uintptr = uintptr ( ^ uint ( 0 ) )
max uintptr = 0
)
for i := 0 ; i < len ( offset ) ; i ++ {
addr := uintptr ( rtypeOff ( section , offset [ i ] ) )
if min > addr {
min = addr
}
if max < addr {
max = addr
}
}
addrRange := uintptr ( max ) - uintptr ( min )
if addrRange == 0 {
return fmt . Errorf ( "failed to get address range of types" )
}
2021-01-30 20:30:48 +03:00
if addrRange > maxAcceptableTypeAddrRange {
2021-01-30 19:48:54 +03:00
return fmt . Errorf ( "too big address range %d" , addrRange )
}
cachedOpcodeSets = make ( [ ] * opcodeSet , addrRange )
baseTypeAddr = min
return nil
}
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 ( )
2021-01-30 19:48:54 +03:00
if err := setupOpcodeSets ( ) ; err != nil {
// fallback to slow path
}
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
}
2021-01-26 18:42:19 +03:00
func newEncoder ( ) * Encoder {
enc := encPool . Get ( ) . ( * Encoder )
enc . reset ( )
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-30 13:32:38 +03:00
header := ( * interfaceHeader ) ( unsafe . Pointer ( & v ) )
2021-01-24 09:17:39 +03:00
e . ptr = header . ptr
2020-12-30 13:39:20 +03:00
buf , err := e . encode ( header , v == nil )
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
}
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 ( ) {
2021-01-24 18:06:16 +03:00
e . baseIndent = 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-12-30 13:39:20 +03:00
func ( e * Encoder ) encodeForMarshal ( header * interfaceHeader , isNil bool ) ( [ ] byte , error ) {
buf , err := e . encode ( header , isNil )
2020-12-24 21:53:48 +03:00
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 {
2021-01-26 18:42:19 +03:00
// this line's description is the below.
buf = buf [ : len ( buf ) - 2 ]
copied := make ( [ ] byte , len ( buf ) )
2020-12-24 21:53:48 +03:00
copy ( copied , buf )
2020-05-02 17:35:41 +03:00
return copied , nil
}
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 )
2020-05-02 17:35:41 +03:00
return copied , nil
}
2020-12-30 13:39:20 +03:00
func ( e * Encoder ) encode ( header * interfaceHeader , isNil bool ) ( [ ] byte , error ) {
2020-12-24 21:53:48 +03:00
b := e . buf [ : 0 ]
2020-12-30 13:39:20 +03:00
if isNil {
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
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-01-24 09:17:39 +03:00
codeSet , err := e . compileToGetCodeSet ( typeptr )
if err != nil {
return nil , err
}
2021-01-10 23:16:37 +03:00
2021-01-24 09:17:39 +03:00
ctx := e . ctx
p := uintptr ( header . ptr )
ctx . init ( p , codeSet . codeLength )
if e . enabledIndent {
2021-01-10 23:16:37 +03:00
if e . enabledHTMLEscape {
2021-01-24 09:17:39 +03:00
return e . runEscapedIndent ( ctx , b , codeSet )
} else {
return e . runIndent ( ctx , b , codeSet )
2021-01-10 23:16:37 +03:00
}
2021-01-24 09:17:39 +03:00
}
if e . enabledHTMLEscape {
return e . runEscaped ( ctx , b , codeSet )
}
return e . run ( ctx , b , codeSet )
}
func ( e * Encoder ) compileToGetCodeSet ( typeptr uintptr ) ( * opcodeSet , error ) {
2021-01-30 19:48:54 +03:00
if cachedOpcodeSets == nil {
return e . compileToGetCodeSetSlowPath ( typeptr )
}
if codeSet := cachedOpcodeSets [ typeptr - baseTypeAddr ] ; codeSet != nil {
return codeSet , nil
}
// noescape trick for header.typ ( reflect.*rtype )
copiedType := * ( * * rtype ) ( unsafe . Pointer ( & typeptr ) )
code , err := e . compileHead ( & encodeCompileContext {
typ : copiedType ,
root : true ,
structTypeToCompiledCode : map [ uintptr ] * compiledCode { } ,
} )
if err != nil {
return nil , err
}
code = copyOpcode ( code )
codeLength := code . totalLength ( )
codeSet := & opcodeSet {
code : code ,
codeLength : codeLength ,
}
cachedOpcodeSets [ int ( typeptr - baseTypeAddr ) ] = codeSet
return codeSet , nil
}
func ( e * Encoder ) compileToGetCodeSetSlowPath ( typeptr uintptr ) ( * opcodeSet , error ) {
2021-01-24 09:17:39 +03:00
opcodeMap := loadOpcodeMap ( )
if codeSet , exists := opcodeMap [ typeptr ] ; exists {
return codeSet , nil
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-08-31 15:59:22 +03:00
codeLength := code . totalLength ( )
2020-08-09 11:48:28 +03:00
codeSet := & opcodeSet {
2021-01-11 13:21:30 +03:00
code : code ,
codeLength : codeLength ,
2020-08-09 11:48:28 +03:00
}
2020-12-15 08:46:10 +03:00
storeOpcodeSet ( typeptr , codeSet , opcodeMap )
2021-01-24 09:17:39 +03:00
return codeSet , 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-29 17:17:39 +03:00
func appendStructEnd ( b [ ] byte ) [ ] byte {
return append ( b , '}' , ',' )
}
func ( e * Encoder ) appendStructEndIndent ( b [ ] byte , indent int ) [ ] byte {
b = append ( b , '\n' )
b = append ( b , e . prefix ... )
2021-01-24 18:06:16 +03:00
b = append ( b , bytes . Repeat ( e . indentStr , e . baseIndent + indent ) ... )
2020-12-29 17:17:39 +03:00
return append ( b , '}' , ',' , '\n' )
}
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 ... )
2021-01-24 18:06:16 +03:00
return append ( b , bytes . Repeat ( e . indentStr , e . baseIndent + indent ) ... )
2020-04-28 12:25:51 +03:00
}