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-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"
"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
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-04-30 07:52:24 +03:00
type opcodeMap struct {
2020-04-28 12:25:51 +03:00
sync . Map
}
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-05-03 16:19:55 +03:00
func ( m * opcodeMap ) get ( k uintptr ) * opcodeSet {
2020-04-28 12:25:51 +03:00
if v , ok := m . Load ( k ) ; ok {
2020-05-02 17:35:41 +03:00
return v . ( * opcodeSet )
2020-04-28 12:25:51 +03:00
}
return nil
}
2020-05-03 16:19:55 +03:00
func ( m * opcodeMap ) set ( k uintptr , op * opcodeSet ) {
2020-04-28 12:25:51 +03:00
m . Store ( k , op )
}
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-05-04 12:39:17 +03:00
cachedOpcode opcodeMap
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-04-30 07:52:24 +03:00
cachedOpcode = opcodeMap { }
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-04-24 14:23:26 +03:00
if err := e . encode ( v ) ; err != nil {
2020-04-21 08:19:50 +03:00
return err
}
if _ , err := e . w . Write ( e . buf ) ; err != nil {
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
}
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-04-19 13:51:22 +03:00
}
2020-05-02 17:35:41 +03:00
func ( e * Encoder ) encodeForMarshal ( v interface { } ) ( [ ] byte , error ) {
if err := e . encode ( v ) ; err != nil {
return nil , err
}
if e . enabledIndent {
last := len ( e . buf ) - 1
if e . buf [ last ] == '\n' {
last --
}
length := last + 1
copied := make ( [ ] byte , length )
copy ( copied , e . buf [ 0 : length ] )
return copied , nil
}
copied := make ( [ ] byte , len ( e . buf ) )
copy ( copied , e . buf )
return copied , nil
}
func ( e * Encoder ) encode ( v interface { } ) error {
2020-08-21 05:07:55 +03:00
if v == nil {
e . encodeNull ( )
return nil
}
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 ) )
if codeSet := cachedOpcode . get ( typeptr ) ; codeSet != nil {
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-08-30 21:14:37 +03:00
seenPtr := map [ uintptr ] struct { } { }
2020-09-01 16:26:26 +03:00
err := e . run ( ctx , 0 , seenPtr , code )
2020-08-30 17:58:58 +03:00
codeSet . ctx . Put ( ctx )
return 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-05-03 16:19:55 +03:00
copiedType := ( * rtype ) ( unsafe . Pointer ( typeptr ) )
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 {
return err
}
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 {
return err
}
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-08-30 21:14:37 +03:00
ptrs : make ( [ ] uintptr , codeLength ) ,
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-05-03 16:19:55 +03:00
cachedOpcode . set ( typeptr , codeSet )
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
seenPtr := map [ uintptr ] struct { } { }
2020-09-01 16:26:26 +03:00
if err := e . run ( ctx , 0 , seenPtr , c ) ; err != nil {
2020-08-30 17:58:58 +03:00
codeSet . ctx . Put ( ctx )
return err
2020-05-02 17:35:41 +03:00
}
2020-08-30 17:58:58 +03:00
codeSet . ctx . Put ( ctx )
2020-08-30 21:14:37 +03:00
return nil
2020-05-02 17:35:41 +03:00
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeInt ( v int ) {
e . encodeInt64 ( int64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeInt8 ( v int8 ) {
e . encodeInt64 ( int64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeInt16 ( v int16 ) {
e . encodeInt64 ( int64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeInt32 ( v int32 ) {
e . encodeInt64 ( int64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeInt64 ( v int64 ) {
2020-04-19 13:51:22 +03:00
e . buf = strconv . AppendInt ( e . buf , v , 10 )
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeUint ( v uint ) {
e . encodeUint64 ( uint64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeUint8 ( v uint8 ) {
e . encodeUint64 ( uint64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeUint16 ( v uint16 ) {
e . encodeUint64 ( uint64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeUint32 ( v uint32 ) {
e . encodeUint64 ( uint64 ( v ) )
2020-04-19 13:51:22 +03:00
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeUint64 ( v uint64 ) {
2020-04-19 13:51:22 +03:00
e . buf = strconv . AppendUint ( e . buf , v , 10 )
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeFloat32 ( v float32 ) {
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'
}
}
e . buf = strconv . AppendFloat ( e . buf , f64 , fmt , - 1 , 32 )
2020-04-19 13:51:22 +03:00
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeFloat64 ( v float64 ) {
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'
}
}
e . buf = strconv . AppendFloat ( e . buf , v , fmt , - 1 , 64 )
2020-04-19 13:51:22 +03:00
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeBool ( v bool ) {
2020-04-19 13:51:22 +03:00
e . buf = strconv . AppendBool ( e . buf , v )
}
2020-04-30 05:56:56 +03:00
func ( e * Encoder ) encodeBytes ( b [ ] byte ) {
e . buf = append ( e . buf , b ... )
}
2020-05-03 11:41:33 +03:00
func ( e * Encoder ) encodeNull ( ) {
e . buf = append ( e . buf , 'n' , 'u' , 'l' , 'l' )
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeString ( s string ) {
2020-05-03 11:41:33 +03:00
if e . enabledHTMLEscape {
e . encodeEscapedString ( s )
} else {
e . encodeNoEscapedString ( s )
}
2020-04-19 13:51:22 +03:00
}
2020-04-21 08:19:50 +03:00
func ( e * Encoder ) encodeByte ( b byte ) {
2020-04-19 13:51:22 +03:00
e . buf = append ( e . buf , b )
}
2020-05-02 17:35:41 +03:00
func ( e * Encoder ) encodeIndent ( indent int ) {
e . buf = append ( e . buf , e . prefix ... )
e . buf = append ( e . buf , bytes . Repeat ( e . indentStr , indent ) ... )
2020-04-28 12:25:51 +03:00
}