mirror of https://github.com/goccy/go-json.git
Merge pull request #107 from goccy/feature/refactor-encoder
Refactor the encoder to fix a bug where streaming encoders can't reuse buffer
This commit is contained in:
commit
c410c7e5fa
|
@ -0,0 +1,28 @@
|
||||||
|
codecov:
|
||||||
|
require_ci_to_pass: yes
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
precision: 2
|
||||||
|
round: down
|
||||||
|
range: "70...100"
|
||||||
|
|
||||||
|
status:
|
||||||
|
project:
|
||||||
|
default:
|
||||||
|
target: 70%
|
||||||
|
threshold: 2%
|
||||||
|
patch: off
|
||||||
|
changes: no
|
||||||
|
|
||||||
|
parsers:
|
||||||
|
gcov:
|
||||||
|
branch_detection:
|
||||||
|
conditional: yes
|
||||||
|
loop: yes
|
||||||
|
method: no
|
||||||
|
macro: no
|
||||||
|
|
||||||
|
comment:
|
||||||
|
layout: "header,diff"
|
||||||
|
behavior: default
|
||||||
|
require_changes: no
|
|
@ -1,6 +1,7 @@
|
||||||
package benchmark
|
package benchmark
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -559,6 +560,51 @@ func Benchmark_Encode_Interface_GoJson(b *testing.B) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_Encode_Bool_EncodingJson(b *testing.B) {
|
func Benchmark_Encode_Bool_EncodingJson(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := json.NewEncoder(&buf)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if err := enc.Encode(true); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_Encode_Bool_JsonIter(b *testing.B) {
|
||||||
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
b.ReportAllocs()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := json.NewEncoder(&buf)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if err := enc.Encode(true); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_Encode_Bool_SegmentioJson(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := segmentiojson.NewEncoder(&buf)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if err := enc.Encode(true); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_Encode_Bool_GoJson(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := gojson.NewEncoder(&buf)
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if err := enc.Encode(true); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Benchmark_Marshal_Bool_EncodingJson(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
if _, err := json.Marshal(true); err != nil {
|
if _, err := json.Marshal(true); err != nil {
|
||||||
|
@ -567,7 +613,7 @@ func Benchmark_Encode_Bool_EncodingJson(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_Encode_Bool_JsonIter(b *testing.B) {
|
func Benchmark_Marshal_Bool_JsonIter(b *testing.B) {
|
||||||
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
var json = jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
@ -577,7 +623,7 @@ func Benchmark_Encode_Bool_JsonIter(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_Encode_Bool_Jettison(b *testing.B) {
|
func Benchmark_Marshal_Bool_Jettison(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
if _, err := jettison.Marshal(true); err != nil {
|
if _, err := jettison.Marshal(true); err != nil {
|
||||||
|
@ -586,7 +632,7 @@ func Benchmark_Encode_Bool_Jettison(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_Encode_Bool_SegmentioJson(b *testing.B) {
|
func Benchmark_Marshal_Bool_SegmentioJson(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
if _, err := segmentiojson.Marshal(true); err != nil {
|
if _, err := segmentiojson.Marshal(true); err != nil {
|
||||||
|
@ -595,7 +641,7 @@ func Benchmark_Encode_Bool_SegmentioJson(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_Encode_Bool_GoJson(b *testing.B) {
|
func Benchmark_Marshal_Bool_GoJson(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
if _, err := gojson.Marshal(true); err != nil {
|
if _, err := gojson.Marshal(true); err != nil {
|
||||||
|
|
389
encode.go
389
encode.go
|
@ -2,154 +2,58 @@ package json
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An Encoder writes JSON values to an output stream.
|
// An Encoder writes JSON values to an output stream.
|
||||||
type Encoder struct {
|
type Encoder struct {
|
||||||
w io.Writer
|
w io.Writer
|
||||||
ctx *encodeRuntimeContext
|
|
||||||
ptr unsafe.Pointer
|
|
||||||
buf []byte
|
|
||||||
enabledIndent bool
|
enabledIndent bool
|
||||||
enabledHTMLEscape bool
|
enabledHTMLEscape bool
|
||||||
unorderedMap bool
|
prefix string
|
||||||
baseIndent int
|
indentStr string
|
||||||
prefix []byte
|
|
||||||
indentStr []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type compiledCode struct {
|
|
||||||
code *opcode
|
|
||||||
linked bool // whether recursive code already have linked
|
|
||||||
curLen uintptr
|
|
||||||
nextLen uintptr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
bufSize = 1024
|
bufSize = 1024
|
||||||
maxAcceptableTypeAddrRange = 1024 * 1024 * 2 // 2 Mib
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type EncodeOption int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
opCodeEscapedType = iota
|
EncodeOptionHTMLEscape EncodeOption = 1 << iota
|
||||||
opCodeEscapedIndentType
|
EncodeOptionIndent
|
||||||
opCodeNoEscapeType
|
EncodeOptionUnorderedMap
|
||||||
opCodeNoEscapeIndentType
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type opcodeSet struct {
|
|
||||||
code *opcode
|
|
||||||
codeLength int
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadOpcodeMap() map[uintptr]*opcodeSet {
|
|
||||||
p := atomic.LoadPointer(&cachedOpcode)
|
|
||||||
return *(*map[uintptr]*opcodeSet)(unsafe.Pointer(&p))
|
|
||||||
}
|
|
||||||
|
|
||||||
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)))
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
encPool sync.Pool
|
encRuntimeContextPool = sync.Pool{
|
||||||
codePool sync.Pool
|
|
||||||
cachedOpcode unsafe.Pointer // map[uintptr]*opcodeSet
|
|
||||||
marshalJSONType reflect.Type
|
|
||||||
marshalTextType reflect.Type
|
|
||||||
baseTypeAddr uintptr
|
|
||||||
cachedOpcodeSets []*opcodeSet
|
|
||||||
)
|
|
||||||
|
|
||||||
//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")
|
|
||||||
}
|
|
||||||
if addrRange > maxAcceptableTypeAddrRange {
|
|
||||||
return fmt.Errorf("too big address range %d", addrRange)
|
|
||||||
}
|
|
||||||
cachedOpcodeSets = make([]*opcodeSet, addrRange)
|
|
||||||
baseTypeAddr = min
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
encPool = sync.Pool{
|
|
||||||
New: func() interface{} {
|
New: func() interface{} {
|
||||||
return &Encoder{
|
return &encodeRuntimeContext{
|
||||||
ctx: &encodeRuntimeContext{
|
buf: make([]byte, 0, bufSize),
|
||||||
ptrs: make([]uintptr, 128),
|
ptrs: make([]uintptr, 128),
|
||||||
keepRefs: make([]unsafe.Pointer, 0, 8),
|
keepRefs: make([]unsafe.Pointer, 0, 8),
|
||||||
},
|
|
||||||
buf: make([]byte, 0, bufSize),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
marshalJSONType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
)
|
||||||
marshalTextType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
|
||||||
if err := setupOpcodeSets(); err != nil {
|
func takeEncodeRuntimeContext() *encodeRuntimeContext {
|
||||||
// fallback to slow path
|
return encRuntimeContextPool.Get().(*encodeRuntimeContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func releaseEncodeRuntimeContext(ctx *encodeRuntimeContext) {
|
||||||
|
encRuntimeContextPool.Put(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEncoder returns a new encoder that writes to w.
|
// NewEncoder returns a new encoder that writes to w.
|
||||||
func NewEncoder(w io.Writer) *Encoder {
|
func NewEncoder(w io.Writer) *Encoder {
|
||||||
enc := encPool.Get().(*Encoder)
|
return &Encoder{w: w, enabledHTMLEscape: true}
|
||||||
enc.w = w
|
|
||||||
enc.reset()
|
|
||||||
return enc
|
|
||||||
}
|
|
||||||
|
|
||||||
func newEncoder() *Encoder {
|
|
||||||
enc := encPool.Get().(*Encoder)
|
|
||||||
enc.reset()
|
|
||||||
return enc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode writes the JSON encoding of v to the stream, followed by a newline character.
|
// Encode writes the JSON encoding of v to the stream, followed by a newline character.
|
||||||
|
@ -160,15 +64,32 @@ func (e *Encoder) Encode(v interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeWithOption call Encode with EncodeOption.
|
// EncodeWithOption call Encode with EncodeOption.
|
||||||
func (e *Encoder) EncodeWithOption(v interface{}, opts ...EncodeOption) error {
|
func (e *Encoder) EncodeWithOption(v interface{}, optFuncs ...EncodeOptionFunc) error {
|
||||||
for _, opt := range opts {
|
ctx := takeEncodeRuntimeContext()
|
||||||
if err := opt(e); err != nil {
|
|
||||||
|
err := e.encodeWithOption(ctx, v, optFuncs...)
|
||||||
|
|
||||||
|
releaseEncodeRuntimeContext(ctx)
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) encodeWithOption(ctx *encodeRuntimeContext, v interface{}, optFuncs ...EncodeOptionFunc) error {
|
||||||
|
var opt EncodeOption
|
||||||
|
if e.enabledHTMLEscape {
|
||||||
|
opt |= EncodeOptionHTMLEscape
|
||||||
}
|
}
|
||||||
|
for _, optFunc := range optFuncs {
|
||||||
|
opt = optFunc(opt)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
buf []byte
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
if e.enabledIndent {
|
||||||
|
buf, err = encodeIndent(ctx, v, e.prefix, e.indentStr, opt)
|
||||||
|
} else {
|
||||||
|
buf, err = encode(ctx, v, opt)
|
||||||
}
|
}
|
||||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
|
||||||
e.ptr = header.ptr
|
|
||||||
buf, err := e.encode(header, v == nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -181,7 +102,6 @@ func (e *Encoder) EncodeWithOption(v interface{}, opts ...EncodeOption) error {
|
||||||
if _, err := e.w.Write(buf); err != nil {
|
if _, err := e.w.Write(buf); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
e.buf = buf[:0]
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,140 +120,171 @@ func (e *Encoder) SetIndent(prefix, indent string) {
|
||||||
e.enabledIndent = false
|
e.enabledIndent = false
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
e.prefix = []byte(prefix)
|
e.prefix = prefix
|
||||||
e.indentStr = []byte(indent)
|
e.indentStr = indent
|
||||||
e.enabledIndent = true
|
e.enabledIndent = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) release() {
|
func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
|
||||||
e.w = nil
|
ctx := takeEncodeRuntimeContext()
|
||||||
encPool.Put(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) reset() {
|
buf, err := encode(ctx, v, EncodeOptionHTMLEscape)
|
||||||
e.baseIndent = 0
|
|
||||||
e.enabledHTMLEscape = true
|
|
||||||
e.enabledIndent = false
|
|
||||||
e.unorderedMap = false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) encodeForMarshal(header *interfaceHeader, isNil bool) ([]byte, error) {
|
|
||||||
buf, err := e.encode(header, isNil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
releaseEncodeRuntimeContext(ctx)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
e.buf = buf
|
|
||||||
|
|
||||||
if e.enabledIndent {
|
|
||||||
// this line's description is the below.
|
|
||||||
buf = buf[:len(buf)-2]
|
|
||||||
|
|
||||||
copied := make([]byte, len(buf))
|
|
||||||
copy(copied, buf)
|
|
||||||
return copied, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// this line exists to escape call of `runtime.makeslicecopy` .
|
// this line exists to escape call of `runtime.makeslicecopy` .
|
||||||
// if use `make([]byte, len(buf)-1)` and `copy(copied, buf)`,
|
// if use `make([]byte, len(buf)-1)` and `copy(copied, buf)`,
|
||||||
// dst buffer size and src buffer size are differrent.
|
// dst buffer size and src buffer size are differrent.
|
||||||
// in this case, compiler uses `runtime.makeslicecopy`, but it is slow.
|
// in this case, compiler uses `runtime.makeslicecopy`, but it is slow.
|
||||||
buf = buf[:len(buf)-1]
|
buf = buf[:len(buf)-1]
|
||||||
|
|
||||||
copied := make([]byte, len(buf))
|
copied := make([]byte, len(buf))
|
||||||
copy(copied, buf)
|
copy(copied, buf)
|
||||||
|
|
||||||
|
releaseEncodeRuntimeContext(ctx)
|
||||||
return copied, nil
|
return copied, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) encode(header *interfaceHeader, isNil bool) ([]byte, error) {
|
func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
|
||||||
b := e.buf[:0]
|
ctx := takeEncodeRuntimeContext()
|
||||||
if isNil {
|
|
||||||
b = encodeNull(b)
|
buf, err := encodeNoEscape(ctx, v, EncodeOptionHTMLEscape)
|
||||||
if e.enabledIndent {
|
if err != nil {
|
||||||
b = encodeIndentComma(b)
|
releaseEncodeRuntimeContext(ctx)
|
||||||
} else {
|
return nil, err
|
||||||
b = encodeComma(b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
releaseEncodeRuntimeContext(ctx)
|
||||||
|
return copied, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalIndent(v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) {
|
||||||
|
ctx := takeEncodeRuntimeContext()
|
||||||
|
|
||||||
|
buf, err := encodeIndent(ctx, v, prefix, indent, EncodeOptionHTMLEscape)
|
||||||
|
if err != nil {
|
||||||
|
releaseEncodeRuntimeContext(ctx)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = buf[:len(buf)-2]
|
||||||
|
copied := make([]byte, len(buf))
|
||||||
|
copy(copied, buf)
|
||||||
|
|
||||||
|
releaseEncodeRuntimeContext(ctx)
|
||||||
|
return copied, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(ctx *encodeRuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {
|
||||||
|
b := ctx.buf[:0]
|
||||||
|
if v == nil {
|
||||||
|
b = encodeNull(b)
|
||||||
|
b = encodeComma(b)
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
||||||
typ := header.typ
|
typ := header.typ
|
||||||
|
|
||||||
typeptr := uintptr(unsafe.Pointer(typ))
|
typeptr := uintptr(unsafe.Pointer(typ))
|
||||||
codeSet, err := e.compileToGetCodeSet(typeptr)
|
codeSet, err := encodeCompileToGetCodeSet(typeptr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := e.ctx
|
|
||||||
p := uintptr(header.ptr)
|
p := uintptr(header.ptr)
|
||||||
ctx.init(p, codeSet.codeLength)
|
ctx.init(p, codeSet.codeLength)
|
||||||
if e.enabledIndent {
|
buf, err := encodeRunCode(ctx, b, codeSet, opt)
|
||||||
if e.enabledHTMLEscape {
|
|
||||||
return e.runEscapedIndent(ctx, b, codeSet)
|
|
||||||
} else {
|
|
||||||
return e.runIndent(ctx, b, codeSet)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if e.enabledHTMLEscape {
|
|
||||||
return e.runEscaped(ctx, b, codeSet)
|
|
||||||
}
|
|
||||||
return e.run(ctx, b, codeSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Encoder) compileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
|
ctx.keepRefs = append(ctx.keepRefs, header.ptr)
|
||||||
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
code = copyOpcode(code)
|
|
||||||
codeLength := code.totalLength()
|
ctx.buf = buf
|
||||||
codeSet := &opcodeSet{
|
return buf, nil
|
||||||
code: code,
|
|
||||||
codeLength: codeLength,
|
|
||||||
}
|
|
||||||
cachedOpcodeSets[int(typeptr-baseTypeAddr)] = codeSet
|
|
||||||
return codeSet, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileToGetCodeSetSlowPath(typeptr uintptr) (*opcodeSet, error) {
|
func encodeNoEscape(ctx *encodeRuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {
|
||||||
opcodeMap := loadOpcodeMap()
|
b := ctx.buf[:0]
|
||||||
if codeSet, exists := opcodeMap[typeptr]; exists {
|
if v == nil {
|
||||||
return codeSet, nil
|
b = encodeNull(b)
|
||||||
|
b = encodeComma(b)
|
||||||
|
return b, nil
|
||||||
}
|
}
|
||||||
|
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
||||||
|
typ := header.typ
|
||||||
|
|
||||||
// noescape trick for header.typ ( reflect.*rtype )
|
typeptr := uintptr(unsafe.Pointer(typ))
|
||||||
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
|
codeSet, err := encodeCompileToGetCodeSet(typeptr)
|
||||||
|
|
||||||
code, err := e.compileHead(&encodeCompileContext{
|
|
||||||
typ: copiedType,
|
|
||||||
root: true,
|
|
||||||
structTypeToCompiledCode: map[uintptr]*compiledCode{},
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
code = copyOpcode(code)
|
|
||||||
codeLength := code.totalLength()
|
p := uintptr(header.ptr)
|
||||||
codeSet := &opcodeSet{
|
ctx.init(p, codeSet.codeLength)
|
||||||
code: code,
|
buf, err := encodeRunCode(ctx, b, codeSet, opt)
|
||||||
codeLength: codeLength,
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
storeOpcodeSet(typeptr, codeSet, opcodeMap)
|
|
||||||
return codeSet, nil
|
ctx.buf = buf
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeIndent(ctx *encodeRuntimeContext, v interface{}, prefix, indent string, opt EncodeOption) ([]byte, error) {
|
||||||
|
b := ctx.buf[:0]
|
||||||
|
if v == nil {
|
||||||
|
b = encodeNull(b)
|
||||||
|
b = encodeIndentComma(b)
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
||||||
|
typ := header.typ
|
||||||
|
|
||||||
|
typeptr := uintptr(unsafe.Pointer(typ))
|
||||||
|
codeSet, err := encodeCompileToGetCodeSet(typeptr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p := uintptr(header.ptr)
|
||||||
|
ctx.init(p, codeSet.codeLength)
|
||||||
|
buf, err := encodeRunIndentCode(ctx, b, codeSet, prefix, indent, opt)
|
||||||
|
|
||||||
|
ctx.keepRefs = append(ctx.keepRefs, header.ptr)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.buf = buf
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeRunCode(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, opt EncodeOption) ([]byte, error) {
|
||||||
|
if (opt & EncodeOptionHTMLEscape) != 0 {
|
||||||
|
return encodeRunEscaped(ctx, b, codeSet, opt)
|
||||||
|
}
|
||||||
|
return encodeRun(ctx, b, codeSet, opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeRunIndentCode(ctx *encodeRuntimeContext, b []byte, codeSet *opcodeSet, prefix, indent string, opt EncodeOption) ([]byte, error) {
|
||||||
|
ctx.prefix = []byte(prefix)
|
||||||
|
ctx.indentStr = []byte(indent)
|
||||||
|
if (opt & EncodeOptionHTMLEscape) != 0 {
|
||||||
|
return encodeRunEscapedIndent(ctx, b, codeSet, opt)
|
||||||
|
}
|
||||||
|
return encodeRunIndent(ctx, b, codeSet, opt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func encodeFloat32(b []byte, v float32) []byte {
|
func encodeFloat32(b []byte, v float32) []byte {
|
||||||
|
@ -389,10 +340,10 @@ func appendStructEnd(b []byte) []byte {
|
||||||
return append(b, '}', ',')
|
return append(b, '}', ',')
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) appendStructEndIndent(b []byte, indent int) []byte {
|
func appendStructEndIndent(ctx *encodeRuntimeContext, b []byte, indent int) []byte {
|
||||||
b = append(b, '\n')
|
b = append(b, '\n')
|
||||||
b = append(b, e.prefix...)
|
b = append(b, ctx.prefix...)
|
||||||
b = append(b, bytes.Repeat(e.indentStr, e.baseIndent+indent)...)
|
b = append(b, bytes.Repeat(ctx.indentStr, ctx.baseIndent+indent)...)
|
||||||
return append(b, '}', ',', '\n')
|
return append(b, '}', ',', '\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,7 +362,7 @@ func encodeByteSlice(b []byte, src []byte) []byte {
|
||||||
return append(append(b, buf...), '"')
|
return append(append(b, buf...), '"')
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) encodeIndent(b []byte, indent int) []byte {
|
func appendIndent(ctx *encodeRuntimeContext, b []byte, indent int) []byte {
|
||||||
b = append(b, e.prefix...)
|
b = append(b, ctx.prefix...)
|
||||||
return append(b, bytes.Repeat(e.indentStr, e.baseIndent+indent)...)
|
return append(b, bytes.Repeat(ctx.indentStr, ctx.baseIndent+indent)...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,178 @@
|
||||||
package json
|
package json
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e *Encoder) compileHead(ctx *encodeCompileContext) (*opcode, error) {
|
type compiledCode struct {
|
||||||
|
code *opcode
|
||||||
|
linked bool // whether recursive code already have linked
|
||||||
|
curLen uintptr
|
||||||
|
nextLen uintptr
|
||||||
|
}
|
||||||
|
|
||||||
|
type opcodeSet struct {
|
||||||
|
code *opcode
|
||||||
|
codeLength int
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
marshalJSONType = reflect.TypeOf((*Marshaler)(nil)).Elem()
|
||||||
|
marshalTextType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
|
||||||
|
cachedOpcode unsafe.Pointer // map[uintptr]*opcodeSet
|
||||||
|
baseTypeAddr uintptr
|
||||||
|
cachedOpcodeSets []*opcodeSet
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxAcceptableTypeAddrRange = 1024 * 1024 * 2 // 2 Mib
|
||||||
|
)
|
||||||
|
|
||||||
|
//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++ {
|
||||||
|
typ := (*rtype)(rtypeOff(section, offset[i]))
|
||||||
|
addr := uintptr(unsafe.Pointer(typ))
|
||||||
|
if min > addr {
|
||||||
|
min = addr
|
||||||
|
}
|
||||||
|
if max < addr {
|
||||||
|
max = addr
|
||||||
|
}
|
||||||
|
if typ.Kind() == reflect.Ptr {
|
||||||
|
addr = uintptr(unsafe.Pointer(typ.Elem()))
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
if addrRange > maxAcceptableTypeAddrRange {
|
||||||
|
return fmt.Errorf("too big address range %d", addrRange)
|
||||||
|
}
|
||||||
|
cachedOpcodeSets = make([]*opcodeSet, addrRange)
|
||||||
|
baseTypeAddr = min
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if err := setupOpcodeSets(); err != nil {
|
||||||
|
// fallback to slow path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeCompileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
|
||||||
|
if cachedOpcodeSets == nil {
|
||||||
|
return encodeCompileToGetCodeSetSlowPath(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 := encodeCompileHead(&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 encodeCompileToGetCodeSetSlowPath(typeptr uintptr) (*opcodeSet, error) {
|
||||||
|
opcodeMap := loadOpcodeMap()
|
||||||
|
if codeSet, exists := opcodeMap[typeptr]; exists {
|
||||||
|
return codeSet, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// noescape trick for header.typ ( reflect.*rtype )
|
||||||
|
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
|
||||||
|
|
||||||
|
code, err := encodeCompileHead(&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,
|
||||||
|
}
|
||||||
|
storeOpcodeSet(typeptr, codeSet, opcodeMap)
|
||||||
|
return codeSet, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadOpcodeMap() map[uintptr]*opcodeSet {
|
||||||
|
p := atomic.LoadPointer(&cachedOpcode)
|
||||||
|
return *(*map[uintptr]*opcodeSet)(unsafe.Pointer(&p))
|
||||||
|
}
|
||||||
|
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeCompileHead(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
typ := ctx.typ
|
typ := ctx.typ
|
||||||
switch {
|
switch {
|
||||||
case typ.Implements(marshalJSONType):
|
case typ.Implements(marshalJSONType):
|
||||||
return e.compileMarshalJSON(ctx)
|
return encodeCompileMarshalJSON(ctx)
|
||||||
case rtype_ptrTo(typ).Implements(marshalJSONType):
|
case rtype_ptrTo(typ).Implements(marshalJSONType):
|
||||||
return e.compileMarshalJSONPtr(ctx)
|
return encodeCompileMarshalJSONPtr(ctx)
|
||||||
case typ.Implements(marshalTextType):
|
case typ.Implements(marshalTextType):
|
||||||
return e.compileMarshalText(ctx)
|
return encodeCompileMarshalText(ctx)
|
||||||
case rtype_ptrTo(typ).Implements(marshalTextType):
|
case rtype_ptrTo(typ).Implements(marshalTextType):
|
||||||
return e.compileMarshalTextPtr(ctx)
|
return encodeCompileMarshalTextPtr(ctx)
|
||||||
}
|
}
|
||||||
isPtr := false
|
isPtr := false
|
||||||
orgType := typ
|
orgType := typ
|
||||||
|
@ -26,32 +181,32 @@ func (e *Encoder) compileHead(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
isPtr = true
|
isPtr = true
|
||||||
}
|
}
|
||||||
if typ.Kind() == reflect.Map {
|
if typ.Kind() == reflect.Map {
|
||||||
return e.compileMap(ctx.withType(typ), isPtr)
|
return encodeCompileMap(ctx.withType(typ), isPtr)
|
||||||
} else if typ.Kind() == reflect.Struct {
|
} else if typ.Kind() == reflect.Struct {
|
||||||
code, err := e.compileStruct(ctx.withType(typ), isPtr)
|
code, err := encodeCompileStruct(ctx.withType(typ), isPtr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
e.convertHeadOnlyCode(code, isPtr)
|
encodeConvertHeadOnlyCode(code, isPtr)
|
||||||
e.optimizeStructEnd(code)
|
encodeOptimizeStructEnd(code)
|
||||||
e.linkRecursiveCode(code)
|
encodeLinkRecursiveCode(code)
|
||||||
return code, nil
|
return code, nil
|
||||||
} else if isPtr && typ.Implements(marshalTextType) {
|
} else if isPtr && typ.Implements(marshalTextType) {
|
||||||
typ = orgType
|
typ = orgType
|
||||||
} else if isPtr && typ.Implements(marshalJSONType) {
|
} else if isPtr && typ.Implements(marshalJSONType) {
|
||||||
typ = orgType
|
typ = orgType
|
||||||
}
|
}
|
||||||
code, err := e.compile(ctx.withType(typ))
|
code, err := encodeCompile(ctx.withType(typ))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
e.convertHeadOnlyCode(code, isPtr)
|
encodeConvertHeadOnlyCode(code, isPtr)
|
||||||
e.optimizeStructEnd(code)
|
encodeOptimizeStructEnd(code)
|
||||||
e.linkRecursiveCode(code)
|
encodeLinkRecursiveCode(code)
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) linkRecursiveCode(c *opcode) {
|
func encodeLinkRecursiveCode(c *opcode) {
|
||||||
for code := c; code.op != opEnd && code.op != opStructFieldRecursiveEnd; {
|
for code := c; code.op != opEnd && code.op != opStructFieldRecursiveEnd; {
|
||||||
switch code.op {
|
switch code.op {
|
||||||
case opStructFieldRecursive,
|
case opStructFieldRecursive,
|
||||||
|
@ -82,7 +237,7 @@ func (e *Encoder) linkRecursiveCode(c *opcode) {
|
||||||
code.jmp.nextLen = nextTotalLength
|
code.jmp.nextLen = nextTotalLength
|
||||||
code.jmp.linked = true
|
code.jmp.linked = true
|
||||||
|
|
||||||
e.linkRecursiveCode(code.jmp.code)
|
encodeLinkRecursiveCode(code.jmp.code)
|
||||||
code = code.next
|
code = code.next
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -95,7 +250,7 @@ func (e *Encoder) linkRecursiveCode(c *opcode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) optimizeStructEnd(c *opcode) {
|
func encodeOptimizeStructEnd(c *opcode) {
|
||||||
for code := c; code.op != opEnd; {
|
for code := c; code.op != opEnd; {
|
||||||
if code.op == opStructFieldRecursive {
|
if code.op == opStructFieldRecursive {
|
||||||
// ignore if exists recursive operation
|
// ignore if exists recursive operation
|
||||||
|
@ -136,7 +291,7 @@ func (e *Encoder) optimizeStructEnd(c *opcode) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) convertHeadOnlyCode(c *opcode, isPtrHead bool) {
|
func encodeConvertHeadOnlyCode(c *opcode, isPtrHead bool) {
|
||||||
if c.nextField == nil {
|
if c.nextField == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -145,19 +300,19 @@ func (e *Encoder) convertHeadOnlyCode(c *opcode, isPtrHead bool) {
|
||||||
}
|
}
|
||||||
switch c.op {
|
switch c.op {
|
||||||
case opStructFieldHead:
|
case opStructFieldHead:
|
||||||
e.convertHeadOnlyCode(c.next, false)
|
encodeConvertHeadOnlyCode(c.next, false)
|
||||||
if !strings.Contains(c.next.op.String(), "Only") {
|
if !strings.Contains(c.next.op.String(), "Only") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.op = opStructFieldHeadOnly
|
c.op = opStructFieldHeadOnly
|
||||||
case opStructFieldHeadOmitEmpty:
|
case opStructFieldHeadOmitEmpty:
|
||||||
e.convertHeadOnlyCode(c.next, false)
|
encodeConvertHeadOnlyCode(c.next, false)
|
||||||
if !strings.Contains(c.next.op.String(), "Only") {
|
if !strings.Contains(c.next.op.String(), "Only") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.op = opStructFieldHeadOmitEmptyOnly
|
c.op = opStructFieldHeadOmitEmptyOnly
|
||||||
case opStructFieldHeadStringTag:
|
case opStructFieldHeadStringTag:
|
||||||
e.convertHeadOnlyCode(c.next, false)
|
encodeConvertHeadOnlyCode(c.next, false)
|
||||||
if !strings.Contains(c.next.op.String(), "Only") {
|
if !strings.Contains(c.next.op.String(), "Only") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -185,7 +340,7 @@ func (e *Encoder) convertHeadOnlyCode(c *opcode, isPtrHead bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) implementsMarshaler(typ *rtype) bool {
|
func encodeImplementsMarshaler(typ *rtype) bool {
|
||||||
switch {
|
switch {
|
||||||
case typ.Implements(marshalJSONType):
|
case typ.Implements(marshalJSONType):
|
||||||
return true
|
return true
|
||||||
|
@ -199,115 +354,115 @@ func (e *Encoder) implementsMarshaler(typ *rtype) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compile(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompile(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
typ := ctx.typ
|
typ := ctx.typ
|
||||||
switch {
|
switch {
|
||||||
case typ.Implements(marshalJSONType):
|
case typ.Implements(marshalJSONType):
|
||||||
return e.compileMarshalJSON(ctx)
|
return encodeCompileMarshalJSON(ctx)
|
||||||
case rtype_ptrTo(typ).Implements(marshalJSONType):
|
case rtype_ptrTo(typ).Implements(marshalJSONType):
|
||||||
return e.compileMarshalJSONPtr(ctx)
|
return encodeCompileMarshalJSONPtr(ctx)
|
||||||
case typ.Implements(marshalTextType):
|
case typ.Implements(marshalTextType):
|
||||||
return e.compileMarshalText(ctx)
|
return encodeCompileMarshalText(ctx)
|
||||||
case rtype_ptrTo(typ).Implements(marshalTextType):
|
case rtype_ptrTo(typ).Implements(marshalTextType):
|
||||||
return e.compileMarshalTextPtr(ctx)
|
return encodeCompileMarshalTextPtr(ctx)
|
||||||
}
|
}
|
||||||
switch typ.Kind() {
|
switch typ.Kind() {
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
return e.compilePtr(ctx)
|
return encodeCompilePtr(ctx)
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
elem := typ.Elem()
|
elem := typ.Elem()
|
||||||
if !e.implementsMarshaler(elem) && elem.Kind() == reflect.Uint8 {
|
if !encodeImplementsMarshaler(elem) && elem.Kind() == reflect.Uint8 {
|
||||||
return e.compileBytes(ctx)
|
return encodeCompileBytes(ctx)
|
||||||
}
|
}
|
||||||
return e.compileSlice(ctx)
|
return encodeCompileSlice(ctx)
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
return e.compileArray(ctx)
|
return encodeCompileArray(ctx)
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
return e.compileMap(ctx, true)
|
return encodeCompileMap(ctx, true)
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return e.compileStruct(ctx, false)
|
return encodeCompileStruct(ctx, false)
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
return e.compileInterface(ctx)
|
return encodeCompileInterface(ctx)
|
||||||
case reflect.Int:
|
case reflect.Int:
|
||||||
return e.compileInt(ctx)
|
return encodeCompileInt(ctx)
|
||||||
case reflect.Int8:
|
case reflect.Int8:
|
||||||
return e.compileInt8(ctx)
|
return encodeCompileInt8(ctx)
|
||||||
case reflect.Int16:
|
case reflect.Int16:
|
||||||
return e.compileInt16(ctx)
|
return encodeCompileInt16(ctx)
|
||||||
case reflect.Int32:
|
case reflect.Int32:
|
||||||
return e.compileInt32(ctx)
|
return encodeCompileInt32(ctx)
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
return e.compileInt64(ctx)
|
return encodeCompileInt64(ctx)
|
||||||
case reflect.Uint:
|
case reflect.Uint:
|
||||||
return e.compileUint(ctx)
|
return encodeCompileUint(ctx)
|
||||||
case reflect.Uint8:
|
case reflect.Uint8:
|
||||||
return e.compileUint8(ctx)
|
return encodeCompileUint8(ctx)
|
||||||
case reflect.Uint16:
|
case reflect.Uint16:
|
||||||
return e.compileUint16(ctx)
|
return encodeCompileUint16(ctx)
|
||||||
case reflect.Uint32:
|
case reflect.Uint32:
|
||||||
return e.compileUint32(ctx)
|
return encodeCompileUint32(ctx)
|
||||||
case reflect.Uint64:
|
case reflect.Uint64:
|
||||||
return e.compileUint64(ctx)
|
return encodeCompileUint64(ctx)
|
||||||
case reflect.Uintptr:
|
case reflect.Uintptr:
|
||||||
return e.compileUint(ctx)
|
return encodeCompileUint(ctx)
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
return e.compileFloat32(ctx)
|
return encodeCompileFloat32(ctx)
|
||||||
case reflect.Float64:
|
case reflect.Float64:
|
||||||
return e.compileFloat64(ctx)
|
return encodeCompileFloat64(ctx)
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
return e.compileString(ctx)
|
return encodeCompileString(ctx)
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
return e.compileBool(ctx)
|
return encodeCompileBool(ctx)
|
||||||
}
|
}
|
||||||
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
|
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileKey(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileKey(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
typ := ctx.typ
|
typ := ctx.typ
|
||||||
switch {
|
switch {
|
||||||
case rtype_ptrTo(typ).Implements(marshalJSONType):
|
case rtype_ptrTo(typ).Implements(marshalJSONType):
|
||||||
return e.compileMarshalJSONPtr(ctx)
|
return encodeCompileMarshalJSONPtr(ctx)
|
||||||
case rtype_ptrTo(typ).Implements(marshalTextType):
|
case rtype_ptrTo(typ).Implements(marshalTextType):
|
||||||
return e.compileMarshalTextPtr(ctx)
|
return encodeCompileMarshalTextPtr(ctx)
|
||||||
}
|
}
|
||||||
switch typ.Kind() {
|
switch typ.Kind() {
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
return e.compilePtr(ctx)
|
return encodeCompilePtr(ctx)
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
return e.compileInterface(ctx)
|
return encodeCompileInterface(ctx)
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
return e.compileString(ctx)
|
return encodeCompileString(ctx)
|
||||||
case reflect.Int:
|
case reflect.Int:
|
||||||
return e.compileIntString(ctx)
|
return encodeCompileIntString(ctx)
|
||||||
case reflect.Int8:
|
case reflect.Int8:
|
||||||
return e.compileInt8String(ctx)
|
return encodeCompileInt8String(ctx)
|
||||||
case reflect.Int16:
|
case reflect.Int16:
|
||||||
return e.compileInt16String(ctx)
|
return encodeCompileInt16String(ctx)
|
||||||
case reflect.Int32:
|
case reflect.Int32:
|
||||||
return e.compileInt32String(ctx)
|
return encodeCompileInt32String(ctx)
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
return e.compileInt64String(ctx)
|
return encodeCompileInt64String(ctx)
|
||||||
case reflect.Uint:
|
case reflect.Uint:
|
||||||
return e.compileUintString(ctx)
|
return encodeCompileUintString(ctx)
|
||||||
case reflect.Uint8:
|
case reflect.Uint8:
|
||||||
return e.compileUint8String(ctx)
|
return encodeCompileUint8String(ctx)
|
||||||
case reflect.Uint16:
|
case reflect.Uint16:
|
||||||
return e.compileUint16String(ctx)
|
return encodeCompileUint16String(ctx)
|
||||||
case reflect.Uint32:
|
case reflect.Uint32:
|
||||||
return e.compileUint32String(ctx)
|
return encodeCompileUint32String(ctx)
|
||||||
case reflect.Uint64:
|
case reflect.Uint64:
|
||||||
return e.compileUint64String(ctx)
|
return encodeCompileUint64String(ctx)
|
||||||
case reflect.Uintptr:
|
case reflect.Uintptr:
|
||||||
return e.compileUintString(ctx)
|
return encodeCompileUintString(ctx)
|
||||||
}
|
}
|
||||||
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
|
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compilePtr(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompilePtr(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
ptrOpcodeIndex := ctx.opcodeIndex
|
ptrOpcodeIndex := ctx.opcodeIndex
|
||||||
ptrIndex := ctx.ptrIndex
|
ptrIndex := ctx.ptrIndex
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
code, err := e.compile(ctx.withType(ctx.typ.Elem()))
|
code, err := encodeCompile(ctx.withType(ctx.typ.Elem()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -324,187 +479,187 @@ func (e *Encoder) compilePtr(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
return newOpCodeWithNext(c, opPtr, code), nil
|
return newOpCodeWithNext(c, opPtr, code), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileMarshalJSON(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileMarshalJSON(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opMarshalJSON)
|
code := newOpCode(ctx, opMarshalJSON)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileMarshalJSONPtr(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileMarshalJSONPtr(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalJSON)
|
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalJSON)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileMarshalText(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileMarshalText(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opMarshalText)
|
code := newOpCode(ctx, opMarshalText)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileMarshalTextPtr(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileMarshalTextPtr(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalText)
|
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalText)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileInt(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileInt(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opInt)
|
code := newOpCode(ctx, opInt)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileInt8(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileInt8(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opInt8)
|
code := newOpCode(ctx, opInt8)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileInt16(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileInt16(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opInt16)
|
code := newOpCode(ctx, opInt16)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileInt32(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileInt32(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opInt32)
|
code := newOpCode(ctx, opInt32)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileInt64(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileInt64(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opInt64)
|
code := newOpCode(ctx, opInt64)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileUint(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileUint(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opUint)
|
code := newOpCode(ctx, opUint)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileUint8(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileUint8(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opUint8)
|
code := newOpCode(ctx, opUint8)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileUint16(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileUint16(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opUint16)
|
code := newOpCode(ctx, opUint16)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileUint32(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileUint32(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opUint32)
|
code := newOpCode(ctx, opUint32)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileUint64(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileUint64(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opUint64)
|
code := newOpCode(ctx, opUint64)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileIntString(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileIntString(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opIntString)
|
code := newOpCode(ctx, opIntString)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileInt8String(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileInt8String(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opInt8String)
|
code := newOpCode(ctx, opInt8String)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileInt16String(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileInt16String(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opInt16String)
|
code := newOpCode(ctx, opInt16String)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileInt32String(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileInt32String(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opInt32String)
|
code := newOpCode(ctx, opInt32String)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileInt64String(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileInt64String(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opInt64String)
|
code := newOpCode(ctx, opInt64String)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileUintString(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileUintString(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opUintString)
|
code := newOpCode(ctx, opUintString)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileUint8String(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileUint8String(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opUint8String)
|
code := newOpCode(ctx, opUint8String)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileUint16String(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileUint16String(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opUint16String)
|
code := newOpCode(ctx, opUint16String)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileUint32String(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileUint32String(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opUint32String)
|
code := newOpCode(ctx, opUint32String)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileUint64String(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileUint64String(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opUint64String)
|
code := newOpCode(ctx, opUint64String)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileFloat32(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileFloat32(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opFloat32)
|
code := newOpCode(ctx, opFloat32)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileFloat64(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileFloat64(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opFloat64)
|
code := newOpCode(ctx, opFloat64)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileString(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileString(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opString)
|
code := newOpCode(ctx, opString)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileBool(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileBool(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opBool)
|
code := newOpCode(ctx, opBool)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileBytes(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileBytes(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newOpCode(ctx, opBytes)
|
code := newOpCode(ctx, opBytes)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileInterface(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileInterface(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
code := newInterfaceCode(ctx)
|
code := newInterfaceCode(ctx)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileSlice(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileSlice(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
ctx.root = false
|
ctx.root = false
|
||||||
elem := ctx.typ.Elem()
|
elem := ctx.typ.Elem()
|
||||||
size := elem.Size()
|
size := elem.Size()
|
||||||
|
@ -512,7 +667,7 @@ func (e *Encoder) compileSlice(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
header := newSliceHeaderCode(ctx)
|
header := newSliceHeaderCode(ctx)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
|
|
||||||
code, err := e.compile(ctx.withType(ctx.typ.Elem()).incIndent())
|
code, err := encodeCompile(ctx.withType(ctx.typ.Elem()).incIndent())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -536,7 +691,7 @@ func (e *Encoder) compileSlice(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
return (*opcode)(unsafe.Pointer(header)), nil
|
return (*opcode)(unsafe.Pointer(header)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileArray(ctx *encodeCompileContext) (*opcode, error) {
|
func encodeCompileArray(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
ctx.root = false
|
ctx.root = false
|
||||||
typ := ctx.typ
|
typ := ctx.typ
|
||||||
elem := typ.Elem()
|
elem := typ.Elem()
|
||||||
|
@ -546,7 +701,7 @@ func (e *Encoder) compileArray(ctx *encodeCompileContext) (*opcode, error) {
|
||||||
header := newArrayHeaderCode(ctx, alen)
|
header := newArrayHeaderCode(ctx, alen)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
|
|
||||||
code, err := e.compile(ctx.withType(elem).incIndent())
|
code, err := encodeCompile(ctx.withType(elem).incIndent())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -585,7 +740,7 @@ func mapiternext(it unsafe.Pointer)
|
||||||
//go:noescape
|
//go:noescape
|
||||||
func maplen(m unsafe.Pointer) int
|
func maplen(m unsafe.Pointer) int
|
||||||
|
|
||||||
func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode, error) {
|
func encodeCompileMap(ctx *encodeCompileContext, withLoad bool) (*opcode, error) {
|
||||||
// header => code => value => code => key => code => value => code => end
|
// header => code => value => code => key => code => value => code => end
|
||||||
// ^ |
|
// ^ |
|
||||||
// |_______________________|
|
// |_______________________|
|
||||||
|
@ -595,7 +750,7 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
|
||||||
|
|
||||||
typ := ctx.typ
|
typ := ctx.typ
|
||||||
keyType := ctx.typ.Key()
|
keyType := ctx.typ.Key()
|
||||||
keyCode, err := e.compileKey(ctx.withType(keyType))
|
keyCode, err := encodeCompileKey(ctx.withType(keyType))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -604,7 +759,7 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
|
|
||||||
valueType := typ.Elem()
|
valueType := typ.Elem()
|
||||||
valueCode, err := e.compile(ctx.withType(valueType))
|
valueCode, err := encodeCompile(ctx.withType(valueType))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -633,7 +788,7 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
|
||||||
return (*opcode)(unsafe.Pointer(header)), nil
|
return (*opcode)(unsafe.Pointer(header)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) typeToHeaderType(ctx *encodeCompileContext, code *opcode) opType {
|
func encodeTypeToHeaderType(ctx *encodeCompileContext, code *opcode) opType {
|
||||||
switch code.op {
|
switch code.op {
|
||||||
case opPtr:
|
case opPtr:
|
||||||
ptrNum := 1
|
ptrNum := 1
|
||||||
|
@ -757,7 +912,7 @@ func (e *Encoder) typeToHeaderType(ctx *encodeCompileContext, code *opcode) opTy
|
||||||
return opStructFieldHead
|
return opStructFieldHead
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) typeToFieldType(ctx *encodeCompileContext, code *opcode) opType {
|
func encodeTypeToFieldType(ctx *encodeCompileContext, code *opcode) opType {
|
||||||
switch code.op {
|
switch code.op {
|
||||||
case opPtr:
|
case opPtr:
|
||||||
ptrNum := 1
|
ptrNum := 1
|
||||||
|
@ -881,8 +1036,8 @@ func (e *Encoder) typeToFieldType(ctx *encodeCompileContext, code *opcode) opTyp
|
||||||
return opStructField
|
return opStructField
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) optimizeStructHeader(ctx *encodeCompileContext, code *opcode, tag *structTag) opType {
|
func encodeOptimizeStructHeader(ctx *encodeCompileContext, code *opcode, tag *structTag) opType {
|
||||||
headType := e.typeToHeaderType(ctx, code)
|
headType := encodeTypeToHeaderType(ctx, code)
|
||||||
switch {
|
switch {
|
||||||
case tag.isOmitEmpty:
|
case tag.isOmitEmpty:
|
||||||
headType = headType.headToOmitEmptyHead()
|
headType = headType.headToOmitEmptyHead()
|
||||||
|
@ -892,8 +1047,8 @@ func (e *Encoder) optimizeStructHeader(ctx *encodeCompileContext, code *opcode,
|
||||||
return headType
|
return headType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) optimizeStructField(ctx *encodeCompileContext, code *opcode, tag *structTag) opType {
|
func encodeOptimizeStructField(ctx *encodeCompileContext, code *opcode, tag *structTag) opType {
|
||||||
fieldType := e.typeToFieldType(ctx, code)
|
fieldType := encodeTypeToFieldType(ctx, code)
|
||||||
switch {
|
switch {
|
||||||
case tag.isOmitEmpty:
|
case tag.isOmitEmpty:
|
||||||
fieldType = fieldType.fieldToOmitEmptyField()
|
fieldType = fieldType.fieldToOmitEmptyField()
|
||||||
|
@ -903,24 +1058,24 @@ func (e *Encoder) optimizeStructField(ctx *encodeCompileContext, code *opcode, t
|
||||||
return fieldType
|
return fieldType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) recursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
|
func encodeRecursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
|
||||||
code := newRecursiveCode(ctx, jmp)
|
code := newRecursiveCode(ctx, jmp)
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compiledCode(ctx *encodeCompileContext) *opcode {
|
func encodeCompiledCode(ctx *encodeCompileContext) *opcode {
|
||||||
typ := ctx.typ
|
typ := ctx.typ
|
||||||
typeptr := uintptr(unsafe.Pointer(typ))
|
typeptr := uintptr(unsafe.Pointer(typ))
|
||||||
if compiledCode, exists := ctx.structTypeToCompiledCode[typeptr]; exists {
|
if compiledCode, exists := ctx.structTypeToCompiledCode[typeptr]; exists {
|
||||||
return e.recursiveCode(ctx, compiledCode)
|
return encodeRecursiveCode(ctx, compiledCode)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) structHeader(ctx *encodeCompileContext, fieldCode *opcode, valueCode *opcode, tag *structTag) *opcode {
|
func encodeStructHeader(ctx *encodeCompileContext, fieldCode *opcode, valueCode *opcode, tag *structTag) *opcode {
|
||||||
fieldCode.indent--
|
fieldCode.indent--
|
||||||
op := e.optimizeStructHeader(ctx, valueCode, tag)
|
op := encodeOptimizeStructHeader(ctx, valueCode, tag)
|
||||||
fieldCode.op = op
|
fieldCode.op = op
|
||||||
fieldCode.ptrNum = valueCode.ptrNum
|
fieldCode.ptrNum = valueCode.ptrNum
|
||||||
switch op {
|
switch op {
|
||||||
|
@ -943,9 +1098,9 @@ func (e *Encoder) structHeader(ctx *encodeCompileContext, fieldCode *opcode, val
|
||||||
return (*opcode)(unsafe.Pointer(fieldCode))
|
return (*opcode)(unsafe.Pointer(fieldCode))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) structField(ctx *encodeCompileContext, fieldCode *opcode, valueCode *opcode, tag *structTag) *opcode {
|
func encodeStructField(ctx *encodeCompileContext, fieldCode *opcode, valueCode *opcode, tag *structTag) *opcode {
|
||||||
code := (*opcode)(unsafe.Pointer(fieldCode))
|
code := (*opcode)(unsafe.Pointer(fieldCode))
|
||||||
op := e.optimizeStructField(ctx, valueCode, tag)
|
op := encodeOptimizeStructField(ctx, valueCode, tag)
|
||||||
fieldCode.op = op
|
fieldCode.op = op
|
||||||
fieldCode.ptrNum = valueCode.ptrNum
|
fieldCode.ptrNum = valueCode.ptrNum
|
||||||
switch op {
|
switch op {
|
||||||
|
@ -968,7 +1123,7 @@ func (e *Encoder) structField(ctx *encodeCompileContext, fieldCode *opcode, valu
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) isNotExistsField(head *opcode) bool {
|
func encodeIsNotExistsField(head *opcode) bool {
|
||||||
if head == nil {
|
if head == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -990,10 +1145,10 @@ func (e *Encoder) isNotExistsField(head *opcode) bool {
|
||||||
if head.next.op.codeType() != codeStructField {
|
if head.next.op.codeType() != codeStructField {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return e.isNotExistsField(head.next)
|
return encodeIsNotExistsField(head.next)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) optimizeAnonymousFields(head *opcode) {
|
func encodeOptimizeAnonymousFields(head *opcode) {
|
||||||
code := head
|
code := head
|
||||||
var prev *opcode
|
var prev *opcode
|
||||||
removedFields := map[*opcode]struct{}{}
|
removedFields := map[*opcode]struct{}{}
|
||||||
|
@ -1004,13 +1159,13 @@ func (e *Encoder) optimizeAnonymousFields(head *opcode) {
|
||||||
if code.op == opStructField {
|
if code.op == opStructField {
|
||||||
codeType := code.next.op.codeType()
|
codeType := code.next.op.codeType()
|
||||||
if codeType == codeStructField {
|
if codeType == codeStructField {
|
||||||
if e.isNotExistsField(code.next) {
|
if encodeIsNotExistsField(code.next) {
|
||||||
code.next = code.nextField
|
code.next = code.nextField
|
||||||
diff := code.next.displayIdx - code.displayIdx
|
diff := code.next.displayIdx - code.displayIdx
|
||||||
for i := 0; i < diff; i++ {
|
for i := 0; i < diff; i++ {
|
||||||
code.next.decOpcodeIndex()
|
code.next.decOpcodeIndex()
|
||||||
}
|
}
|
||||||
linkPrevToNextField(code, removedFields)
|
encodeLinkPrevToNextField(code, removedFields)
|
||||||
code = prev
|
code = prev
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1027,7 +1182,7 @@ type structFieldPair struct {
|
||||||
linked bool
|
linked bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, named string, valueCode *opcode) map[string][]structFieldPair {
|
func encodeAnonymousStructFieldPairMap(typ *rtype, tags structTags, named string, valueCode *opcode) map[string][]structFieldPair {
|
||||||
anonymousFields := map[string][]structFieldPair{}
|
anonymousFields := map[string][]structFieldPair{}
|
||||||
f := valueCode
|
f := valueCode
|
||||||
var prevAnonymousField *opcode
|
var prevAnonymousField *opcode
|
||||||
|
@ -1050,7 +1205,7 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, named
|
||||||
for i := 0; i < diff; i++ {
|
for i := 0; i < diff; i++ {
|
||||||
f.nextField.decOpcodeIndex()
|
f.nextField.decOpcodeIndex()
|
||||||
}
|
}
|
||||||
linkPrevToNextField(f, removedFields)
|
encodeLinkPrevToNextField(f, removedFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
if f.displayKey == "" {
|
if f.displayKey == "" {
|
||||||
|
@ -1069,7 +1224,7 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, named
|
||||||
isTaggedKey: f.isTaggedKey,
|
isTaggedKey: f.isTaggedKey,
|
||||||
})
|
})
|
||||||
if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField {
|
if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField {
|
||||||
for k, v := range e.anonymousStructFieldPairMap(typ, tags, named, f.next) {
|
for k, v := range encodeAnonymousStructFieldPairMap(typ, tags, named, f.next) {
|
||||||
anonymousFields[k] = append(anonymousFields[k], v...)
|
anonymousFields[k] = append(anonymousFields[k], v...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1082,7 +1237,7 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, named
|
||||||
return anonymousFields
|
return anonymousFields
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPair) {
|
func encodeOptimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPair) {
|
||||||
removedFields := map[*opcode]struct{}{}
|
removedFields := map[*opcode]struct{}{}
|
||||||
for _, fieldPairs := range anonymousFields {
|
for _, fieldPairs := range anonymousFields {
|
||||||
if len(fieldPairs) == 1 {
|
if len(fieldPairs) == 1 {
|
||||||
|
@ -1104,7 +1259,7 @@ func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]s
|
||||||
fieldPair.curField.nextField.decOpcodeIndex()
|
fieldPair.curField.nextField.decOpcodeIndex()
|
||||||
}
|
}
|
||||||
removedFields[fieldPair.curField] = struct{}{}
|
removedFields[fieldPair.curField] = struct{}{}
|
||||||
linkPrevToNextField(fieldPair.curField, removedFields)
|
encodeLinkPrevToNextField(fieldPair.curField, removedFields)
|
||||||
}
|
}
|
||||||
fieldPair.linked = true
|
fieldPair.linked = true
|
||||||
}
|
}
|
||||||
|
@ -1122,7 +1277,7 @@ func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]s
|
||||||
for i := 0; i < diff; i++ {
|
for i := 0; i < diff; i++ {
|
||||||
fieldPair.curField.nextField.decOpcodeIndex()
|
fieldPair.curField.nextField.decOpcodeIndex()
|
||||||
}
|
}
|
||||||
linkPrevToNextField(fieldPair.curField, removedFields)
|
encodeLinkPrevToNextField(fieldPair.curField, removedFields)
|
||||||
}
|
}
|
||||||
fieldPair.linked = true
|
fieldPair.linked = true
|
||||||
}
|
}
|
||||||
|
@ -1135,9 +1290,9 @@ func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error) {
|
func encodeCompileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error) {
|
||||||
ctx.root = false
|
ctx.root = false
|
||||||
if code := e.compiledCode(ctx); code != nil {
|
if code := encodeCompiledCode(ctx); code != nil {
|
||||||
return code, nil
|
return code, nil
|
||||||
}
|
}
|
||||||
typ := ctx.typ
|
typ := ctx.typ
|
||||||
|
@ -1179,7 +1334,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
||||||
fieldOpcodeIndex := ctx.opcodeIndex
|
fieldOpcodeIndex := ctx.opcodeIndex
|
||||||
fieldPtrIndex := ctx.ptrIndex
|
fieldPtrIndex := ctx.ptrIndex
|
||||||
ctx.incIndex()
|
ctx.incIndex()
|
||||||
valueCode, err := e.compile(ctx.withType(fieldType))
|
valueCode, err := encodeCompile(ctx.withType(fieldType))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1195,7 +1350,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
||||||
if tag.isTaggedKey {
|
if tag.isTaggedKey {
|
||||||
tagKey = tag.key
|
tagKey = tag.key
|
||||||
}
|
}
|
||||||
for k, v := range e.anonymousStructFieldPairMap(typ, tags, tagKey, valueCode) {
|
for k, v := range encodeAnonymousStructFieldPairMap(typ, tags, tagKey, valueCode) {
|
||||||
anonymousFields[k] = append(anonymousFields[k], v...)
|
anonymousFields[k] = append(anonymousFields[k], v...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1216,13 +1371,13 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
||||||
}
|
}
|
||||||
if fieldIdx == 0 {
|
if fieldIdx == 0 {
|
||||||
fieldCode.headIdx = fieldCode.idx
|
fieldCode.headIdx = fieldCode.idx
|
||||||
code = e.structHeader(ctx, fieldCode, valueCode, tag)
|
code = encodeStructHeader(ctx, fieldCode, valueCode, tag)
|
||||||
head = fieldCode
|
head = fieldCode
|
||||||
prevField = fieldCode
|
prevField = fieldCode
|
||||||
} else {
|
} else {
|
||||||
fieldCode.headIdx = head.headIdx
|
fieldCode.headIdx = head.headIdx
|
||||||
code.next = fieldCode
|
code.next = fieldCode
|
||||||
code = e.structField(ctx, fieldCode, valueCode, tag)
|
code = encodeStructField(ctx, fieldCode, valueCode, tag)
|
||||||
prevField.nextField = fieldCode
|
prevField.nextField = fieldCode
|
||||||
fieldCode.prevField = prevField
|
fieldCode.prevField = prevField
|
||||||
prevField = fieldCode
|
prevField = fieldCode
|
||||||
|
@ -1265,8 +1420,8 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
|
||||||
|
|
||||||
head.end = structEndCode
|
head.end = structEndCode
|
||||||
code.next = structEndCode
|
code.next = structEndCode
|
||||||
e.optimizeConflictAnonymousFields(anonymousFields)
|
encodeOptimizeConflictAnonymousFields(anonymousFields)
|
||||||
e.optimizeAnonymousFields(head)
|
encodeOptimizeAnonymousFields(head)
|
||||||
ret := (*opcode)(unsafe.Pointer(head))
|
ret := (*opcode)(unsafe.Pointer(head))
|
||||||
compiled.code = ret
|
compiled.code = ret
|
||||||
|
|
||||||
|
|
|
@ -142,9 +142,13 @@ func (c *encodeCompileContext) decPtrIndex() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type encodeRuntimeContext struct {
|
type encodeRuntimeContext struct {
|
||||||
|
buf []byte
|
||||||
ptrs []uintptr
|
ptrs []uintptr
|
||||||
keepRefs []unsafe.Pointer
|
keepRefs []unsafe.Pointer
|
||||||
seenPtr []uintptr
|
seenPtr []uintptr
|
||||||
|
baseIndent int
|
||||||
|
prefix []byte
|
||||||
|
indentStr []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *encodeRuntimeContext) init(p uintptr, codelen int) {
|
func (c *encodeRuntimeContext) init(p uintptr, codelen int) {
|
||||||
|
@ -154,6 +158,7 @@ func (c *encodeRuntimeContext) init(p uintptr, codelen int) {
|
||||||
c.ptrs[0] = p
|
c.ptrs[0] = p
|
||||||
c.keepRefs = c.keepRefs[:0]
|
c.keepRefs = c.keepRefs[:0]
|
||||||
c.seenPtr = c.seenPtr[:0]
|
c.seenPtr = c.seenPtr[:0]
|
||||||
|
c.baseIndent = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *encodeRuntimeContext) ptr() uintptr {
|
func (c *encodeRuntimeContext) ptr() uintptr {
|
||||||
|
|
|
@ -327,7 +327,7 @@ func nextField(code *opcode, removedFields map[*opcode]struct{}) *opcode {
|
||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
func linkPrevToNextField(cur *opcode, removedFields map[*opcode]struct{}) {
|
func encodeLinkPrevToNextField(cur *opcode, removedFields map[*opcode]struct{}) {
|
||||||
prev := prevField(cur.prevField, removedFields)
|
prev := prevField(cur.prevField, removedFields)
|
||||||
prev.nextField = nextField(cur.nextField, removedFields)
|
prev.nextField = nextField(cur.nextField, removedFields)
|
||||||
code := prev
|
code := prev
|
||||||
|
|
2100
encode_vm.go
2100
encode_vm.go
File diff suppressed because it is too large
Load Diff
2051
encode_vm_escaped.go
2051
encode_vm_escaped.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
3122
encode_vm_indent.go
3122
encode_vm_indent.go
File diff suppressed because it is too large
Load Diff
61
json.go
61
json.go
|
@ -4,7 +4,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"strconv"
|
"strconv"
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Marshaler is the interface implemented by types that
|
// Marshaler is the interface implemented by types that
|
||||||
|
@ -160,34 +159,16 @@ func Marshal(v interface{}) ([]byte, error) {
|
||||||
|
|
||||||
// MarshalNoEscape
|
// MarshalNoEscape
|
||||||
func MarshalNoEscape(v interface{}) ([]byte, error) {
|
func MarshalNoEscape(v interface{}) ([]byte, error) {
|
||||||
enc := newEncoder()
|
return marshalNoEscape(v, EncodeOptionHTMLEscape)
|
||||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
|
||||||
bytes, err := enc.encodeForMarshal(header, v == nil)
|
|
||||||
if err != nil {
|
|
||||||
enc.release()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
enc.release()
|
|
||||||
return bytes, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalWithOption returns the JSON encoding of v with EncodeOption.
|
// MarshalWithOption returns the JSON encoding of v with EncodeOption.
|
||||||
func MarshalWithOption(v interface{}, opts ...EncodeOption) ([]byte, error) {
|
func MarshalWithOption(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
|
||||||
enc := newEncoder()
|
opt := EncodeOptionHTMLEscape
|
||||||
for _, opt := range opts {
|
for _, optFunc := range optFuncs {
|
||||||
if err := opt(enc); err != nil {
|
opt = optFunc(opt)
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
}
|
return marshal(v, opt)
|
||||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
|
||||||
enc.ptr = header.ptr
|
|
||||||
bytes, err := enc.encodeForMarshal(header, v == nil)
|
|
||||||
if err != nil {
|
|
||||||
enc.release()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
enc.release()
|
|
||||||
return bytes, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalIndent is like Marshal but applies Indent to format the output.
|
// MarshalIndent is like Marshal but applies Indent to format the output.
|
||||||
|
@ -198,24 +179,12 @@ func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalIndentWithOption is like Marshal but applies Indent to format the output with EncodeOption.
|
// MarshalIndentWithOption is like Marshal but applies Indent to format the output with EncodeOption.
|
||||||
func MarshalIndentWithOption(v interface{}, prefix, indent string, opts ...EncodeOption) ([]byte, error) {
|
func MarshalIndentWithOption(v interface{}, prefix, indent string, optFuncs ...EncodeOptionFunc) ([]byte, error) {
|
||||||
var b *bytes.Buffer
|
opt := EncodeOptionHTMLEscape | EncodeOptionIndent
|
||||||
enc := NewEncoder(b)
|
for _, optFunc := range optFuncs {
|
||||||
for _, opt := range opts {
|
opt = optFunc(opt)
|
||||||
if err := opt(enc); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
}
|
return marshalIndent(v, prefix, indent, opt)
|
||||||
enc.SetIndent(prefix, indent)
|
|
||||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
|
||||||
enc.ptr = header.ptr
|
|
||||||
bytes, err := enc.encodeForMarshal(header, v == nil)
|
|
||||||
if err != nil {
|
|
||||||
enc.release()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
enc.release()
|
|
||||||
return bytes, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unmarshal parses the JSON-encoded data and stores the result
|
// Unmarshal parses the JSON-encoded data and stores the result
|
||||||
|
@ -408,12 +377,8 @@ func HTMLEscape(dst *bytes.Buffer, src []byte) {
|
||||||
if err := dec.Decode(&v); err != nil {
|
if err := dec.Decode(&v); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
enc := NewEncoder(dst)
|
buf, _ := marshal(v, EncodeOptionHTMLEscape)
|
||||||
enc.SetEscapeHTML(true)
|
dst.Write(buf)
|
||||||
header := (*interfaceHeader)(unsafe.Pointer(&v))
|
|
||||||
enc.ptr = header.ptr
|
|
||||||
enc.buf, _ = enc.encode(header, v == nil)
|
|
||||||
dst.Write(enc.buf[:len(enc.buf)-1]) // remove last ',' character
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid reports whether data is a valid JSON encoding.
|
// Valid reports whether data is a valid JSON encoding.
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
package json
|
package json
|
||||||
|
|
||||||
type EncodeOption func(*Encoder) error
|
type EncodeOptionFunc func(EncodeOption) EncodeOption
|
||||||
|
|
||||||
func UnorderedMap() EncodeOption {
|
func UnorderedMap() func(EncodeOption) EncodeOption {
|
||||||
return func(e *Encoder) error {
|
return func(opt EncodeOption) EncodeOption {
|
||||||
e.unorderedMap = true
|
return opt | EncodeOptionUnorderedMap
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue