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
import (
"bytes"
"encoding/json"
"testing"
@ -559,6 +560,51 @@ func Benchmark_Encode_Interface_GoJson(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()
for i := 0; i < b.N; i++ {
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
b.ReportAllocs()
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()
for i := 0; i < b.N; i++ {
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()
for i := 0; i < b.N; i++ {
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()
for i := 0; i < b.N; i++ {
if _, err := gojson.Marshal(true); err != nil {

399
encode.go
View File

@ -2,154 +2,58 @@ package json
import (
"bytes"
"encoding"
"encoding/base64"
"fmt"
"io"
"math"
"reflect"
"strconv"
"sync"
"sync/atomic"
"unsafe"
)
// An Encoder writes JSON values to an output stream.
type Encoder struct {
w io.Writer
ctx *encodeRuntimeContext
ptr unsafe.Pointer
buf []byte
enabledIndent bool
enabledHTMLEscape bool
unorderedMap bool
baseIndent int
prefix []byte
indentStr []byte
}
type compiledCode struct {
code *opcode
linked bool // whether recursive code already have linked
curLen uintptr
nextLen uintptr
prefix string
indentStr string
}
const (
bufSize = 1024
maxAcceptableTypeAddrRange = 1024 * 1024 * 2 // 2 Mib
bufSize = 1024
)
type EncodeOption int
const (
opCodeEscapedType = iota
opCodeEscapedIndentType
opCodeNoEscapeType
opCodeNoEscapeIndentType
EncodeOptionHTMLEscape EncodeOption = 1 << iota
EncodeOptionIndent
EncodeOptionUnorderedMap
)
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 (
encPool 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{
encRuntimeContextPool = sync.Pool{
New: func() interface{} {
return &Encoder{
ctx: &encodeRuntimeContext{
ptrs: make([]uintptr, 128),
keepRefs: make([]unsafe.Pointer, 0, 8),
},
buf: make([]byte, 0, bufSize),
return &encodeRuntimeContext{
buf: make([]byte, 0, bufSize),
ptrs: make([]uintptr, 128),
keepRefs: make([]unsafe.Pointer, 0, 8),
}
},
}
marshalJSONType = reflect.TypeOf((*Marshaler)(nil)).Elem()
marshalTextType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem()
if err := setupOpcodeSets(); err != nil {
// fallback to slow path
}
)
func takeEncodeRuntimeContext() *encodeRuntimeContext {
return encRuntimeContextPool.Get().(*encodeRuntimeContext)
}
func releaseEncodeRuntimeContext(ctx *encodeRuntimeContext) {
encRuntimeContextPool.Put(ctx)
}
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
enc := encPool.Get().(*Encoder)
enc.w = w
enc.reset()
return enc
}
func newEncoder() *Encoder {
enc := encPool.Get().(*Encoder)
enc.reset()
return enc
return &Encoder{w: w, enabledHTMLEscape: true}
}
// 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.
func (e *Encoder) EncodeWithOption(v interface{}, opts ...EncodeOption) error {
for _, opt := range opts {
if err := opt(e); err != nil {
return err
}
func (e *Encoder) EncodeWithOption(v interface{}, optFuncs ...EncodeOptionFunc) error {
ctx := takeEncodeRuntimeContext()
err := e.encodeWithOption(ctx, v, optFuncs...)
releaseEncodeRuntimeContext(ctx)
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 {
return err
}
@ -181,7 +102,6 @@ func (e *Encoder) EncodeWithOption(v interface{}, opts ...EncodeOption) error {
if _, err := e.w.Write(buf); err != nil {
return err
}
e.buf = buf[:0]
return nil
}
@ -200,140 +120,171 @@ func (e *Encoder) SetIndent(prefix, indent string) {
e.enabledIndent = false
return
}
e.prefix = []byte(prefix)
e.indentStr = []byte(indent)
e.prefix = prefix
e.indentStr = indent
e.enabledIndent = true
}
func (e *Encoder) release() {
e.w = nil
encPool.Put(e)
}
func marshal(v interface{}, opt EncodeOption) ([]byte, error) {
ctx := takeEncodeRuntimeContext()
func (e *Encoder) reset() {
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)
buf, err := encode(ctx, v, EncodeOptionHTMLEscape)
if err != nil {
releaseEncodeRuntimeContext(ctx)
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` .
// 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 (e *Encoder) encode(header *interfaceHeader, isNil bool) ([]byte, error) {
b := e.buf[:0]
if isNil {
func marshalNoEscape(v interface{}, opt EncodeOption) ([]byte, error) {
ctx := takeEncodeRuntimeContext()
buf, err := encodeNoEscape(ctx, v, EncodeOptionHTMLEscape)
if err != nil {
releaseEncodeRuntimeContext(ctx)
return nil, err
}
// 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)
if e.enabledIndent {
b = encodeIndentComma(b)
} else {
b = encodeComma(b)
}
b = encodeComma(b)
return b, nil
}
header := (*interfaceHeader)(unsafe.Pointer(&v))
typ := header.typ
typeptr := uintptr(unsafe.Pointer(typ))
codeSet, err := e.compileToGetCodeSet(typeptr)
codeSet, err := encodeCompileToGetCodeSet(typeptr)
if err != nil {
return nil, err
}
ctx := e.ctx
p := uintptr(header.ptr)
ctx.init(p, codeSet.codeLength)
if e.enabledIndent {
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)
}
buf, err := encodeRunCode(ctx, b, codeSet, opt)
func (e *Encoder) compileToGetCodeSet(typeptr uintptr) (*opcodeSet, error) {
if cachedOpcodeSets == nil {
return e.compileToGetCodeSetSlowPath(typeptr)
}
if codeSet := cachedOpcodeSets[typeptr-baseTypeAddr]; codeSet != nil {
return codeSet, nil
}
ctx.keepRefs = append(ctx.keepRefs, header.ptr)
// noescape trick for header.typ ( reflect.*rtype )
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
code, err := e.compileHead(&encodeCompileContext{
typ: copiedType,
root: true,
structTypeToCompiledCode: map[uintptr]*compiledCode{},
})
if err != nil {
return nil, err
}
code = copyOpcode(code)
codeLength := code.totalLength()
codeSet := &opcodeSet{
code: code,
codeLength: codeLength,
}
cachedOpcodeSets[int(typeptr-baseTypeAddr)] = codeSet
return codeSet, nil
ctx.buf = buf
return buf, nil
}
func (e *Encoder) compileToGetCodeSetSlowPath(typeptr uintptr) (*opcodeSet, error) {
opcodeMap := loadOpcodeMap()
if codeSet, exists := opcodeMap[typeptr]; exists {
return codeSet, nil
func encodeNoEscape(ctx *encodeRuntimeContext, v interface{}, opt EncodeOption) ([]byte, error) {
b := ctx.buf[:0]
if v == 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 )
copiedType := *(**rtype)(unsafe.Pointer(&typeptr))
code, err := e.compileHead(&encodeCompileContext{
typ: copiedType,
root: true,
structTypeToCompiledCode: map[uintptr]*compiledCode{},
})
typeptr := uintptr(unsafe.Pointer(typ))
codeSet, err := encodeCompileToGetCodeSet(typeptr)
if err != nil {
return nil, err
}
code = copyOpcode(code)
codeLength := code.totalLength()
codeSet := &opcodeSet{
code: code,
codeLength: codeLength,
p := uintptr(header.ptr)
ctx.init(p, codeSet.codeLength)
buf, err := encodeRunCode(ctx, b, codeSet, opt)
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 {
@ -389,10 +340,10 @@ func appendStructEnd(b []byte) []byte {
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, e.prefix...)
b = append(b, bytes.Repeat(e.indentStr, e.baseIndent+indent)...)
b = append(b, ctx.prefix...)
b = append(b, bytes.Repeat(ctx.indentStr, ctx.baseIndent+indent)...)
return append(b, '}', ',', '\n')
}
@ -411,7 +362,7 @@ func encodeByteSlice(b []byte, src []byte) []byte {
return append(append(b, buf...), '"')
}
func (e *Encoder) encodeIndent(b []byte, indent int) []byte {
b = append(b, e.prefix...)
return append(b, bytes.Repeat(e.indentStr, e.baseIndent+indent)...)
func appendIndent(ctx *encodeRuntimeContext, b []byte, indent int) []byte {
b = append(b, ctx.prefix...)
return append(b, bytes.Repeat(ctx.indentStr, ctx.baseIndent+indent)...)
}

View File

@ -1,23 +1,178 @@
package json
import (
"encoding"
"fmt"
"reflect"
"strings"
"sync/atomic"
"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
switch {
case typ.Implements(marshalJSONType):
return e.compileMarshalJSON(ctx)
return encodeCompileMarshalJSON(ctx)
case rtype_ptrTo(typ).Implements(marshalJSONType):
return e.compileMarshalJSONPtr(ctx)
return encodeCompileMarshalJSONPtr(ctx)
case typ.Implements(marshalTextType):
return e.compileMarshalText(ctx)
return encodeCompileMarshalText(ctx)
case rtype_ptrTo(typ).Implements(marshalTextType):
return e.compileMarshalTextPtr(ctx)
return encodeCompileMarshalTextPtr(ctx)
}
isPtr := false
orgType := typ
@ -26,32 +181,32 @@ func (e *Encoder) compileHead(ctx *encodeCompileContext) (*opcode, error) {
isPtr = true
}
if typ.Kind() == reflect.Map {
return e.compileMap(ctx.withType(typ), isPtr)
return encodeCompileMap(ctx.withType(typ), isPtr)
} else if typ.Kind() == reflect.Struct {
code, err := e.compileStruct(ctx.withType(typ), isPtr)
code, err := encodeCompileStruct(ctx.withType(typ), isPtr)
if err != nil {
return nil, err
}
e.convertHeadOnlyCode(code, isPtr)
e.optimizeStructEnd(code)
e.linkRecursiveCode(code)
encodeConvertHeadOnlyCode(code, isPtr)
encodeOptimizeStructEnd(code)
encodeLinkRecursiveCode(code)
return code, nil
} else if isPtr && typ.Implements(marshalTextType) {
typ = orgType
} else if isPtr && typ.Implements(marshalJSONType) {
typ = orgType
}
code, err := e.compile(ctx.withType(typ))
code, err := encodeCompile(ctx.withType(typ))
if err != nil {
return nil, err
}
e.convertHeadOnlyCode(code, isPtr)
e.optimizeStructEnd(code)
e.linkRecursiveCode(code)
encodeConvertHeadOnlyCode(code, isPtr)
encodeOptimizeStructEnd(code)
encodeLinkRecursiveCode(code)
return code, nil
}
func (e *Encoder) linkRecursiveCode(c *opcode) {
func encodeLinkRecursiveCode(c *opcode) {
for code := c; code.op != opEnd && code.op != opStructFieldRecursiveEnd; {
switch code.op {
case opStructFieldRecursive,
@ -82,7 +237,7 @@ func (e *Encoder) linkRecursiveCode(c *opcode) {
code.jmp.nextLen = nextTotalLength
code.jmp.linked = true
e.linkRecursiveCode(code.jmp.code)
encodeLinkRecursiveCode(code.jmp.code)
code = code.next
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; {
if code.op == opStructFieldRecursive {
// 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 {
return
}
@ -145,19 +300,19 @@ func (e *Encoder) convertHeadOnlyCode(c *opcode, isPtrHead bool) {
}
switch c.op {
case opStructFieldHead:
e.convertHeadOnlyCode(c.next, false)
encodeConvertHeadOnlyCode(c.next, false)
if !strings.Contains(c.next.op.String(), "Only") {
return
}
c.op = opStructFieldHeadOnly
case opStructFieldHeadOmitEmpty:
e.convertHeadOnlyCode(c.next, false)
encodeConvertHeadOnlyCode(c.next, false)
if !strings.Contains(c.next.op.String(), "Only") {
return
}
c.op = opStructFieldHeadOmitEmptyOnly
case opStructFieldHeadStringTag:
e.convertHeadOnlyCode(c.next, false)
encodeConvertHeadOnlyCode(c.next, false)
if !strings.Contains(c.next.op.String(), "Only") {
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 {
case typ.Implements(marshalJSONType):
return true
@ -199,115 +354,115 @@ func (e *Encoder) implementsMarshaler(typ *rtype) bool {
return false
}
func (e *Encoder) compile(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompile(ctx *encodeCompileContext) (*opcode, error) {
typ := ctx.typ
switch {
case typ.Implements(marshalJSONType):
return e.compileMarshalJSON(ctx)
return encodeCompileMarshalJSON(ctx)
case rtype_ptrTo(typ).Implements(marshalJSONType):
return e.compileMarshalJSONPtr(ctx)
return encodeCompileMarshalJSONPtr(ctx)
case typ.Implements(marshalTextType):
return e.compileMarshalText(ctx)
return encodeCompileMarshalText(ctx)
case rtype_ptrTo(typ).Implements(marshalTextType):
return e.compileMarshalTextPtr(ctx)
return encodeCompileMarshalTextPtr(ctx)
}
switch typ.Kind() {
case reflect.Ptr:
return e.compilePtr(ctx)
return encodeCompilePtr(ctx)
case reflect.Slice:
elem := typ.Elem()
if !e.implementsMarshaler(elem) && elem.Kind() == reflect.Uint8 {
return e.compileBytes(ctx)
if !encodeImplementsMarshaler(elem) && elem.Kind() == reflect.Uint8 {
return encodeCompileBytes(ctx)
}
return e.compileSlice(ctx)
return encodeCompileSlice(ctx)
case reflect.Array:
return e.compileArray(ctx)
return encodeCompileArray(ctx)
case reflect.Map:
return e.compileMap(ctx, true)
return encodeCompileMap(ctx, true)
case reflect.Struct:
return e.compileStruct(ctx, false)
return encodeCompileStruct(ctx, false)
case reflect.Interface:
return e.compileInterface(ctx)
return encodeCompileInterface(ctx)
case reflect.Int:
return e.compileInt(ctx)
return encodeCompileInt(ctx)
case reflect.Int8:
return e.compileInt8(ctx)
return encodeCompileInt8(ctx)
case reflect.Int16:
return e.compileInt16(ctx)
return encodeCompileInt16(ctx)
case reflect.Int32:
return e.compileInt32(ctx)
return encodeCompileInt32(ctx)
case reflect.Int64:
return e.compileInt64(ctx)
return encodeCompileInt64(ctx)
case reflect.Uint:
return e.compileUint(ctx)
return encodeCompileUint(ctx)
case reflect.Uint8:
return e.compileUint8(ctx)
return encodeCompileUint8(ctx)
case reflect.Uint16:
return e.compileUint16(ctx)
return encodeCompileUint16(ctx)
case reflect.Uint32:
return e.compileUint32(ctx)
return encodeCompileUint32(ctx)
case reflect.Uint64:
return e.compileUint64(ctx)
return encodeCompileUint64(ctx)
case reflect.Uintptr:
return e.compileUint(ctx)
return encodeCompileUint(ctx)
case reflect.Float32:
return e.compileFloat32(ctx)
return encodeCompileFloat32(ctx)
case reflect.Float64:
return e.compileFloat64(ctx)
return encodeCompileFloat64(ctx)
case reflect.String:
return e.compileString(ctx)
return encodeCompileString(ctx)
case reflect.Bool:
return e.compileBool(ctx)
return encodeCompileBool(ctx)
}
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
}
func (e *Encoder) compileKey(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileKey(ctx *encodeCompileContext) (*opcode, error) {
typ := ctx.typ
switch {
case rtype_ptrTo(typ).Implements(marshalJSONType):
return e.compileMarshalJSONPtr(ctx)
return encodeCompileMarshalJSONPtr(ctx)
case rtype_ptrTo(typ).Implements(marshalTextType):
return e.compileMarshalTextPtr(ctx)
return encodeCompileMarshalTextPtr(ctx)
}
switch typ.Kind() {
case reflect.Ptr:
return e.compilePtr(ctx)
return encodeCompilePtr(ctx)
case reflect.Interface:
return e.compileInterface(ctx)
return encodeCompileInterface(ctx)
case reflect.String:
return e.compileString(ctx)
return encodeCompileString(ctx)
case reflect.Int:
return e.compileIntString(ctx)
return encodeCompileIntString(ctx)
case reflect.Int8:
return e.compileInt8String(ctx)
return encodeCompileInt8String(ctx)
case reflect.Int16:
return e.compileInt16String(ctx)
return encodeCompileInt16String(ctx)
case reflect.Int32:
return e.compileInt32String(ctx)
return encodeCompileInt32String(ctx)
case reflect.Int64:
return e.compileInt64String(ctx)
return encodeCompileInt64String(ctx)
case reflect.Uint:
return e.compileUintString(ctx)
return encodeCompileUintString(ctx)
case reflect.Uint8:
return e.compileUint8String(ctx)
return encodeCompileUint8String(ctx)
case reflect.Uint16:
return e.compileUint16String(ctx)
return encodeCompileUint16String(ctx)
case reflect.Uint32:
return e.compileUint32String(ctx)
return encodeCompileUint32String(ctx)
case reflect.Uint64:
return e.compileUint64String(ctx)
return encodeCompileUint64String(ctx)
case reflect.Uintptr:
return e.compileUintString(ctx)
return encodeCompileUintString(ctx)
}
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
}
func (e *Encoder) compilePtr(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompilePtr(ctx *encodeCompileContext) (*opcode, error) {
ptrOpcodeIndex := ctx.opcodeIndex
ptrIndex := ctx.ptrIndex
ctx.incIndex()
code, err := e.compile(ctx.withType(ctx.typ.Elem()))
code, err := encodeCompile(ctx.withType(ctx.typ.Elem()))
if err != nil {
return nil, err
}
@ -324,187 +479,187 @@ func (e *Encoder) compilePtr(ctx *encodeCompileContext) (*opcode, error) {
return newOpCodeWithNext(c, opPtr, code), nil
}
func (e *Encoder) compileMarshalJSON(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileMarshalJSON(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opMarshalJSON)
ctx.incIndex()
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)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileMarshalText(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileMarshalText(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opMarshalText)
ctx.incIndex()
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)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInt(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileInt(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInt8(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileInt8(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt8)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInt16(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileInt16(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt16)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInt32(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileInt32(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt32)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInt64(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileInt64(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt64)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileUint(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint8(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileUint8(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint8)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint16(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileUint16(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint16)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint32(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileUint32(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint32)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint64(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileUint64(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint64)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileIntString(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileIntString(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opIntString)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInt8String(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileInt8String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt8String)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInt16String(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileInt16String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt16String)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInt32String(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileInt32String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt32String)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInt64String(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileInt64String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opInt64String)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUintString(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileUintString(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUintString)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint8String(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileUint8String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint8String)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint16String(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileUint16String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint16String)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint32String(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileUint32String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint32String)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileUint64String(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileUint64String(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opUint64String)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileFloat32(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileFloat32(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opFloat32)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileFloat64(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileFloat64(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opFloat64)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileString(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileString(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opString)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileBool(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileBool(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opBool)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileBytes(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileBytes(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opBytes)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileInterface(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileInterface(ctx *encodeCompileContext) (*opcode, error) {
code := newInterfaceCode(ctx)
ctx.incIndex()
return code, nil
}
func (e *Encoder) compileSlice(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileSlice(ctx *encodeCompileContext) (*opcode, error) {
ctx.root = false
elem := ctx.typ.Elem()
size := elem.Size()
@ -512,7 +667,7 @@ func (e *Encoder) compileSlice(ctx *encodeCompileContext) (*opcode, error) {
header := newSliceHeaderCode(ctx)
ctx.incIndex()
code, err := e.compile(ctx.withType(ctx.typ.Elem()).incIndent())
code, err := encodeCompile(ctx.withType(ctx.typ.Elem()).incIndent())
if err != nil {
return nil, err
}
@ -536,7 +691,7 @@ func (e *Encoder) compileSlice(ctx *encodeCompileContext) (*opcode, error) {
return (*opcode)(unsafe.Pointer(header)), nil
}
func (e *Encoder) compileArray(ctx *encodeCompileContext) (*opcode, error) {
func encodeCompileArray(ctx *encodeCompileContext) (*opcode, error) {
ctx.root = false
typ := ctx.typ
elem := typ.Elem()
@ -546,7 +701,7 @@ func (e *Encoder) compileArray(ctx *encodeCompileContext) (*opcode, error) {
header := newArrayHeaderCode(ctx, alen)
ctx.incIndex()
code, err := e.compile(ctx.withType(elem).incIndent())
code, err := encodeCompile(ctx.withType(elem).incIndent())
if err != nil {
return nil, err
}
@ -585,7 +740,7 @@ func mapiternext(it unsafe.Pointer)
//go:noescape
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
// ^ |
// |_______________________|
@ -595,7 +750,7 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
typ := ctx.typ
keyType := ctx.typ.Key()
keyCode, err := e.compileKey(ctx.withType(keyType))
keyCode, err := encodeCompileKey(ctx.withType(keyType))
if err != nil {
return nil, err
}
@ -604,7 +759,7 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
ctx.incIndex()
valueType := typ.Elem()
valueCode, err := e.compile(ctx.withType(valueType))
valueCode, err := encodeCompile(ctx.withType(valueType))
if err != nil {
return nil, err
}
@ -633,7 +788,7 @@ func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode,
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 {
case opPtr:
ptrNum := 1
@ -757,7 +912,7 @@ func (e *Encoder) typeToHeaderType(ctx *encodeCompileContext, code *opcode) opTy
return opStructFieldHead
}
func (e *Encoder) typeToFieldType(ctx *encodeCompileContext, code *opcode) opType {
func encodeTypeToFieldType(ctx *encodeCompileContext, code *opcode) opType {
switch code.op {
case opPtr:
ptrNum := 1
@ -881,8 +1036,8 @@ func (e *Encoder) typeToFieldType(ctx *encodeCompileContext, code *opcode) opTyp
return opStructField
}
func (e *Encoder) optimizeStructHeader(ctx *encodeCompileContext, code *opcode, tag *structTag) opType {
headType := e.typeToHeaderType(ctx, code)
func encodeOptimizeStructHeader(ctx *encodeCompileContext, code *opcode, tag *structTag) opType {
headType := encodeTypeToHeaderType(ctx, code)
switch {
case tag.isOmitEmpty:
headType = headType.headToOmitEmptyHead()
@ -892,8 +1047,8 @@ func (e *Encoder) optimizeStructHeader(ctx *encodeCompileContext, code *opcode,
return headType
}
func (e *Encoder) optimizeStructField(ctx *encodeCompileContext, code *opcode, tag *structTag) opType {
fieldType := e.typeToFieldType(ctx, code)
func encodeOptimizeStructField(ctx *encodeCompileContext, code *opcode, tag *structTag) opType {
fieldType := encodeTypeToFieldType(ctx, code)
switch {
case tag.isOmitEmpty:
fieldType = fieldType.fieldToOmitEmptyField()
@ -903,24 +1058,24 @@ func (e *Encoder) optimizeStructField(ctx *encodeCompileContext, code *opcode, t
return fieldType
}
func (e *Encoder) recursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
func encodeRecursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
code := newRecursiveCode(ctx, jmp)
ctx.incIndex()
return code
}
func (e *Encoder) compiledCode(ctx *encodeCompileContext) *opcode {
func encodeCompiledCode(ctx *encodeCompileContext) *opcode {
typ := ctx.typ
typeptr := uintptr(unsafe.Pointer(typ))
if compiledCode, exists := ctx.structTypeToCompiledCode[typeptr]; exists {
return e.recursiveCode(ctx, compiledCode)
return encodeRecursiveCode(ctx, compiledCode)
}
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--
op := e.optimizeStructHeader(ctx, valueCode, tag)
op := encodeOptimizeStructHeader(ctx, valueCode, tag)
fieldCode.op = op
fieldCode.ptrNum = valueCode.ptrNum
switch op {
@ -943,9 +1098,9 @@ func (e *Encoder) structHeader(ctx *encodeCompileContext, fieldCode *opcode, val
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))
op := e.optimizeStructField(ctx, valueCode, tag)
op := encodeOptimizeStructField(ctx, valueCode, tag)
fieldCode.op = op
fieldCode.ptrNum = valueCode.ptrNum
switch op {
@ -968,7 +1123,7 @@ func (e *Encoder) structField(ctx *encodeCompileContext, fieldCode *opcode, valu
return code
}
func (e *Encoder) isNotExistsField(head *opcode) bool {
func encodeIsNotExistsField(head *opcode) bool {
if head == nil {
return false
}
@ -990,10 +1145,10 @@ func (e *Encoder) isNotExistsField(head *opcode) bool {
if head.next.op.codeType() != codeStructField {
return false
}
return e.isNotExistsField(head.next)
return encodeIsNotExistsField(head.next)
}
func (e *Encoder) optimizeAnonymousFields(head *opcode) {
func encodeOptimizeAnonymousFields(head *opcode) {
code := head
var prev *opcode
removedFields := map[*opcode]struct{}{}
@ -1004,13 +1159,13 @@ func (e *Encoder) optimizeAnonymousFields(head *opcode) {
if code.op == opStructField {
codeType := code.next.op.codeType()
if codeType == codeStructField {
if e.isNotExistsField(code.next) {
if encodeIsNotExistsField(code.next) {
code.next = code.nextField
diff := code.next.displayIdx - code.displayIdx
for i := 0; i < diff; i++ {
code.next.decOpcodeIndex()
}
linkPrevToNextField(code, removedFields)
encodeLinkPrevToNextField(code, removedFields)
code = prev
}
}
@ -1027,7 +1182,7 @@ type structFieldPair struct {
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{}
f := valueCode
var prevAnonymousField *opcode
@ -1050,7 +1205,7 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, named
for i := 0; i < diff; i++ {
f.nextField.decOpcodeIndex()
}
linkPrevToNextField(f, removedFields)
encodeLinkPrevToNextField(f, removedFields)
}
if f.displayKey == "" {
@ -1069,7 +1224,7 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, named
isTaggedKey: f.isTaggedKey,
})
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...)
}
}
@ -1082,7 +1237,7 @@ func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, named
return anonymousFields
}
func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPair) {
func encodeOptimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPair) {
removedFields := map[*opcode]struct{}{}
for _, fieldPairs := range anonymousFields {
if len(fieldPairs) == 1 {
@ -1104,7 +1259,7 @@ func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]s
fieldPair.curField.nextField.decOpcodeIndex()
}
removedFields[fieldPair.curField] = struct{}{}
linkPrevToNextField(fieldPair.curField, removedFields)
encodeLinkPrevToNextField(fieldPair.curField, removedFields)
}
fieldPair.linked = true
}
@ -1122,7 +1277,7 @@ func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]s
for i := 0; i < diff; i++ {
fieldPair.curField.nextField.decOpcodeIndex()
}
linkPrevToNextField(fieldPair.curField, removedFields)
encodeLinkPrevToNextField(fieldPair.curField, removedFields)
}
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
if code := e.compiledCode(ctx); code != nil {
if code := encodeCompiledCode(ctx); code != nil {
return code, nil
}
typ := ctx.typ
@ -1179,7 +1334,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
fieldOpcodeIndex := ctx.opcodeIndex
fieldPtrIndex := ctx.ptrIndex
ctx.incIndex()
valueCode, err := e.compile(ctx.withType(fieldType))
valueCode, err := encodeCompile(ctx.withType(fieldType))
if err != nil {
return nil, err
}
@ -1195,7 +1350,7 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
if tag.isTaggedKey {
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...)
}
}
@ -1216,13 +1371,13 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
}
if fieldIdx == 0 {
fieldCode.headIdx = fieldCode.idx
code = e.structHeader(ctx, fieldCode, valueCode, tag)
code = encodeStructHeader(ctx, fieldCode, valueCode, tag)
head = fieldCode
prevField = fieldCode
} else {
fieldCode.headIdx = head.headIdx
code.next = fieldCode
code = e.structField(ctx, fieldCode, valueCode, tag)
code = encodeStructField(ctx, fieldCode, valueCode, tag)
prevField.nextField = fieldCode
fieldCode.prevField = prevField
prevField = fieldCode
@ -1265,8 +1420,8 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
head.end = structEndCode
code.next = structEndCode
e.optimizeConflictAnonymousFields(anonymousFields)
e.optimizeAnonymousFields(head)
encodeOptimizeConflictAnonymousFields(anonymousFields)
encodeOptimizeAnonymousFields(head)
ret := (*opcode)(unsafe.Pointer(head))
compiled.code = ret

View File

@ -142,9 +142,13 @@ func (c *encodeCompileContext) decPtrIndex() {
}
type encodeRuntimeContext struct {
ptrs []uintptr
keepRefs []unsafe.Pointer
seenPtr []uintptr
buf []byte
ptrs []uintptr
keepRefs []unsafe.Pointer
seenPtr []uintptr
baseIndent int
prefix []byte
indentStr []byte
}
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.keepRefs = c.keepRefs[:0]
c.seenPtr = c.seenPtr[:0]
c.baseIndent = 0
}
func (c *encodeRuntimeContext) ptr() uintptr {

View File

@ -327,7 +327,7 @@ func nextField(code *opcode, removedFields map[*opcode]struct{}) *opcode {
return code
}
func linkPrevToNextField(cur *opcode, removedFields map[*opcode]struct{}) {
func encodeLinkPrevToNextField(cur *opcode, removedFields map[*opcode]struct{}) {
prev := prevField(cur.prevField, removedFields)
prev.nextField = nextField(cur.nextField, removedFields)
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"
"errors"
"strconv"
"unsafe"
)
// Marshaler is the interface implemented by types that
@ -160,34 +159,16 @@ func Marshal(v interface{}) ([]byte, error) {
// MarshalNoEscape
func MarshalNoEscape(v interface{}) ([]byte, error) {
enc := newEncoder()
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
return marshalNoEscape(v, EncodeOptionHTMLEscape)
}
// MarshalWithOption returns the JSON encoding of v with EncodeOption.
func MarshalWithOption(v interface{}, opts ...EncodeOption) ([]byte, error) {
enc := newEncoder()
for _, opt := range opts {
if err := opt(enc); err != nil {
return nil, err
}
func MarshalWithOption(v interface{}, optFuncs ...EncodeOptionFunc) ([]byte, error) {
opt := EncodeOptionHTMLEscape
for _, optFunc := range optFuncs {
opt = optFunc(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
return marshal(v, opt)
}
// 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.
func MarshalIndentWithOption(v interface{}, prefix, indent string, opts ...EncodeOption) ([]byte, error) {
var b *bytes.Buffer
enc := NewEncoder(b)
for _, opt := range opts {
if err := opt(enc); err != nil {
return nil, err
}
func MarshalIndentWithOption(v interface{}, prefix, indent string, optFuncs ...EncodeOptionFunc) ([]byte, error) {
opt := EncodeOptionHTMLEscape | EncodeOptionIndent
for _, optFunc := range optFuncs {
opt = optFunc(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
return marshalIndent(v, prefix, indent, opt)
}
// 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 {
return
}
enc := NewEncoder(dst)
enc.SetEscapeHTML(true)
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
buf, _ := marshal(v, EncodeOptionHTMLEscape)
dst.Write(buf)
}
// Valid reports whether data is a valid JSON encoding.

View File

@ -1,10 +1,9 @@
package json
type EncodeOption func(*Encoder) error
type EncodeOptionFunc func(EncodeOption) EncodeOption
func UnorderedMap() EncodeOption {
return func(e *Encoder) error {
e.unorderedMap = true
return nil
func UnorderedMap() func(EncodeOption) EncodeOption {
return func(opt EncodeOption) EncodeOption {
return opt | EncodeOptionUnorderedMap
}
}