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:
Masaaki Goshima 2021-02-01 12:19:34 +09:00 committed by GitHub
commit c410c7e5fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 5770 additions and 5620 deletions

28
.codecov.yml Normal file
View File

@ -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

View File

@ -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
View File

@ -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)...)
} }

View File

@ -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

View File

@ -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 {

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

61
json.go
View File

@ -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.

View File

@ -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
} }
} }