go-json/encode_compile.go

1014 lines
25 KiB
Go
Raw Normal View History

2020-04-29 18:31:50 +03:00
package json
import (
2020-09-16 19:26:39 +03:00
"bytes"
2020-04-29 18:31:50 +03:00
"fmt"
"reflect"
"unsafe"
)
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileHead(ctx *encodeCompileContext) (*opcode, error) {
typ := ctx.typ
2020-08-18 07:36:36 +03:00
switch {
case typ.Implements(marshalJSONType):
2020-08-30 11:32:26 +03:00
return e.compileMarshalJSON(ctx)
2020-08-18 07:36:36 +03:00
case rtype_ptrTo(typ).Implements(marshalJSONType):
2020-08-30 11:32:26 +03:00
return e.compileMarshalJSONPtr(ctx)
2020-08-18 07:36:36 +03:00
case typ.Implements(marshalTextType):
2020-08-30 11:32:26 +03:00
return e.compileMarshalText(ctx)
2020-08-18 07:36:36 +03:00
case rtype_ptrTo(typ).Implements(marshalTextType):
2020-08-30 11:32:26 +03:00
return e.compileMarshalTextPtr(ctx)
2020-05-04 12:39:17 +03:00
}
isPtr := false
2020-05-04 12:39:17 +03:00
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
isPtr = true
2020-05-04 12:39:17 +03:00
}
if typ.Kind() == reflect.Map {
2020-08-29 09:11:31 +03:00
return e.compileMap(ctx.withType(typ), isPtr)
} else if typ.Kind() == reflect.Struct {
2020-08-29 09:11:31 +03:00
return e.compileStruct(ctx.withType(typ), isPtr)
}
2020-08-29 09:11:31 +03:00
return e.compile(ctx.withType(typ))
2020-05-04 12:39:17 +03:00
}
func (e *Encoder) implementsMarshaler(typ *rtype) bool {
switch {
case typ.Implements(marshalJSONType):
return true
case rtype_ptrTo(typ).Implements(marshalJSONType):
return true
case typ.Implements(marshalTextType):
return true
case rtype_ptrTo(typ).Implements(marshalTextType):
return true
}
return false
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compile(ctx *encodeCompileContext) (*opcode, error) {
typ := ctx.typ
2020-08-18 07:36:36 +03:00
switch {
case typ.Implements(marshalJSONType):
2020-08-30 11:32:26 +03:00
return e.compileMarshalJSON(ctx)
2020-08-18 07:36:36 +03:00
case rtype_ptrTo(typ).Implements(marshalJSONType):
2020-08-30 11:32:26 +03:00
return e.compileMarshalJSONPtr(ctx)
2020-08-18 07:36:36 +03:00
case typ.Implements(marshalTextType):
2020-08-30 11:32:26 +03:00
return e.compileMarshalText(ctx)
2020-08-18 07:36:36 +03:00
case rtype_ptrTo(typ).Implements(marshalTextType):
2020-08-30 11:32:26 +03:00
return e.compileMarshalTextPtr(ctx)
2020-05-04 12:39:17 +03:00
}
2020-04-29 18:31:50 +03:00
switch typ.Kind() {
case reflect.Ptr:
2020-08-29 09:11:31 +03:00
return e.compilePtr(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Slice:
elem := typ.Elem()
if !e.implementsMarshaler(elem) && elem.Kind() == reflect.Uint8 {
2020-08-29 09:11:31 +03:00
return e.compileBytes(ctx)
2020-08-19 04:34:11 +03:00
}
2020-08-29 09:11:31 +03:00
return e.compileSlice(ctx)
case reflect.Array:
2020-08-29 09:11:31 +03:00
return e.compileArray(ctx)
case reflect.Map:
2020-08-29 09:11:31 +03:00
return e.compileMap(ctx, true)
2020-04-29 18:31:50 +03:00
case reflect.Struct:
2020-08-29 09:11:31 +03:00
return e.compileStruct(ctx, false)
2020-08-12 10:54:15 +03:00
case reflect.Interface:
2020-08-29 09:11:31 +03:00
return e.compileInterface(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Int:
2020-08-29 09:11:31 +03:00
return e.compileInt(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Int8:
2020-08-29 09:11:31 +03:00
return e.compileInt8(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Int16:
2020-08-29 09:11:31 +03:00
return e.compileInt16(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Int32:
2020-08-29 09:11:31 +03:00
return e.compileInt32(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Int64:
2020-08-29 09:11:31 +03:00
return e.compileInt64(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Uint:
2020-08-29 09:11:31 +03:00
return e.compileUint(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Uint8:
2020-08-29 09:11:31 +03:00
return e.compileUint8(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Uint16:
2020-08-29 09:11:31 +03:00
return e.compileUint16(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Uint32:
2020-08-29 09:11:31 +03:00
return e.compileUint32(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Uint64:
2020-08-29 09:11:31 +03:00
return e.compileUint64(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Uintptr:
2020-08-29 09:11:31 +03:00
return e.compileUint(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Float32:
2020-08-29 09:11:31 +03:00
return e.compileFloat32(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Float64:
2020-08-29 09:11:31 +03:00
return e.compileFloat64(ctx)
2020-04-29 18:31:50 +03:00
case reflect.String:
2020-08-29 09:11:31 +03:00
return e.compileString(ctx)
2020-04-29 18:31:50 +03:00
case reflect.Bool:
2020-08-29 09:11:31 +03:00
return e.compileBool(ctx)
2020-04-29 18:31:50 +03:00
}
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileKey(ctx *encodeCompileContext) (*opcode, error) {
typ := ctx.typ
2020-08-20 11:47:38 +03:00
switch {
case typ.Implements(marshalJSONType):
2020-08-30 11:32:26 +03:00
return e.compileMarshalJSON(ctx)
2020-08-20 11:47:38 +03:00
case rtype_ptrTo(typ).Implements(marshalJSONType):
2020-08-30 11:32:26 +03:00
return e.compileMarshalJSONPtr(ctx)
2020-08-20 11:47:38 +03:00
case typ.Implements(marshalTextType):
2020-08-30 11:32:26 +03:00
return e.compileMarshalText(ctx)
2020-08-20 11:47:38 +03:00
case rtype_ptrTo(typ).Implements(marshalTextType):
2020-08-30 11:32:26 +03:00
return e.compileMarshalTextPtr(ctx)
2020-08-20 11:47:38 +03:00
}
switch typ.Kind() {
case reflect.Ptr:
2020-08-29 09:11:31 +03:00
return e.compilePtr(ctx)
2020-08-20 11:47:38 +03:00
case reflect.Interface:
2020-08-29 09:11:31 +03:00
return e.compileInterface(ctx)
2020-08-20 11:47:38 +03:00
case reflect.String:
2020-08-29 09:11:31 +03:00
return e.compileString(ctx)
2020-08-20 11:47:38 +03:00
}
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
}
2020-08-30 11:32:26 +03:00
func (e *Encoder) compilePtr(ctx *encodeCompileContext) (*opcode, error) {
ptrOpcodeIndex := ctx.opcodeIndex
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
code, err := e.compile(ctx.withType(ctx.typ.Elem()))
if err != nil {
return nil, err
}
2020-08-15 11:41:38 +03:00
ptrHeadOp := code.op.headToPtrHead()
if code.op != ptrHeadOp {
code.op = ptrHeadOp
2020-08-30 11:32:26 +03:00
code.decOpcodeIndex()
2020-09-01 16:26:26 +03:00
ctx.decIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-30 05:56:56 +03:00
}
2020-08-30 11:32:26 +03:00
c := ctx.context()
c.opcodeIndex = ptrOpcodeIndex
return newOpCodeWithNext(c, opPtr, code), nil
2020-04-30 05:56:56 +03:00
}
2020-08-30 11:32:26 +03:00
func (e *Encoder) compileMarshalJSON(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opMarshalJSON)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
}
func (e *Encoder) compileMarshalJSONPtr(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalJSON)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
}
func (e *Encoder) compileMarshalText(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx, opMarshalText)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
}
func (e *Encoder) compileMarshalTextPtr(ctx *encodeCompileContext) (*opcode, error) {
code := newOpCode(ctx.withType(rtype_ptrTo(ctx.typ)), opMarshalText)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileInt(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opInt)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileInt8(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opInt8)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileInt16(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opInt16)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileInt32(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opInt32)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileInt64(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opInt64)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileUint(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opUint)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileUint8(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opUint8)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileUint16(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opUint16)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileUint32(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opUint32)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileUint64(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opUint64)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileFloat32(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opFloat32)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileFloat64(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opFloat64)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileString(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opString)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileBool(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opBool)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-04-29 18:31:50 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileBytes(ctx *encodeCompileContext) (*opcode, error) {
2020-08-30 11:32:26 +03:00
code := newOpCode(ctx, opBytes)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
2020-08-19 04:34:11 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileInterface(ctx *encodeCompileContext) (*opcode, error) {
2020-08-31 15:59:22 +03:00
code := newInterfaceCode(ctx)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
return code, nil
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileSlice(ctx *encodeCompileContext) (*opcode, error) {
ctx.root = false
elem := ctx.typ.Elem()
2020-04-29 18:31:50 +03:00
size := elem.Size()
2020-05-02 17:35:41 +03:00
2020-08-30 11:32:26 +03:00
header := newSliceHeaderCode(ctx)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
2020-08-29 09:35:03 +03:00
code, err := e.compile(ctx.withType(ctx.typ.Elem()).incIndent())
2020-04-29 18:31:50 +03:00
if err != nil {
return nil, err
}
2020-04-29 19:44:48 +03:00
// header => opcode => elem => end
// ^ |
// |________|
2020-04-29 18:31:50 +03:00
2020-09-01 16:26:26 +03:00
elemCode := newSliceElemCode(ctx, header, size)
ctx.incIndex()
2020-08-30 11:32:26 +03:00
2020-08-29 09:35:03 +03:00
end := newOpCode(ctx, opSliceEnd)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-29 09:11:31 +03:00
if ctx.withIndent {
if ctx.root {
2020-08-12 10:54:15 +03:00
header.op = opRootSliceHeadIndent
elemCode.op = opRootSliceElemIndent
} else {
header.op = opSliceHeadIndent
elemCode.op = opSliceElemIndent
}
2020-05-02 17:35:41 +03:00
end.op = opSliceEndIndent
}
header.elem = elemCode
header.end = end
header.next = code
code.beforeLastCode().next = (*opcode)(unsafe.Pointer(elemCode))
elemCode.next = code
elemCode.end = end
return (*opcode)(unsafe.Pointer(header)), nil
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileArray(ctx *encodeCompileContext) (*opcode, error) {
ctx.root = false
typ := ctx.typ
elem := typ.Elem()
alen := typ.Len()
size := elem.Size()
2020-05-02 17:35:41 +03:00
2020-08-30 11:32:26 +03:00
header := newArrayHeaderCode(ctx, alen)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
2020-08-29 09:35:03 +03:00
code, err := e.compile(ctx.withType(elem).incIndent())
if err != nil {
return nil, err
}
// header => opcode => elem => end
// ^ |
// |________|
2020-09-01 16:26:26 +03:00
elemCode := newArrayElemCode(ctx, header, alen, size)
ctx.incIndex()
2020-08-30 11:32:26 +03:00
2020-08-29 09:35:03 +03:00
end := newOpCode(ctx, opArrayEnd)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-05-02 17:35:41 +03:00
2020-08-29 09:11:31 +03:00
if ctx.withIndent {
2020-05-02 17:35:41 +03:00
header.op = opArrayHeadIndent
elemCode.op = opArrayElemIndent
end.op = opArrayEndIndent
}
2020-04-29 18:31:50 +03:00
2020-04-29 19:44:48 +03:00
header.elem = elemCode
header.end = end
header.next = code
2020-04-29 18:31:50 +03:00
code.beforeLastCode().next = (*opcode)(unsafe.Pointer(elemCode))
elemCode.next = code
elemCode.end = end
return (*opcode)(unsafe.Pointer(header)), nil
}
2020-04-30 07:52:24 +03:00
//go:linkname mapiterinit reflect.mapiterinit
//go:noescape
func mapiterinit(mapType *rtype, m unsafe.Pointer) unsafe.Pointer
//go:linkname mapiterkey reflect.mapiterkey
//go:noescape
func mapiterkey(it unsafe.Pointer) unsafe.Pointer
//go:linkname mapiternext reflect.mapiternext
//go:noescape
func mapiternext(it unsafe.Pointer)
//go:linkname maplen reflect.maplen
//go:noescape
func maplen(m unsafe.Pointer) int
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileMap(ctx *encodeCompileContext, withLoad bool) (*opcode, error) {
// header => code => value => code => key => code => value => code => end
// ^ |
// |_______________________|
2020-08-29 09:35:03 +03:00
ctx = ctx.incIndent()
2020-08-30 11:32:26 +03:00
header := newMapHeaderCode(ctx, withLoad)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
2020-08-29 09:11:31 +03:00
typ := ctx.typ
keyType := ctx.typ.Key()
keyCode, err := e.compileKey(ctx.withType(keyType))
if err != nil {
return nil, err
}
2020-08-30 11:32:26 +03:00
2020-09-01 16:26:26 +03:00
value := newMapValueCode(ctx, header)
ctx.incIndex()
2020-08-30 11:32:26 +03:00
valueType := typ.Elem()
2020-08-29 09:11:31 +03:00
valueCode, err := e.compile(ctx.withType(valueType))
if err != nil {
return nil, err
}
2020-05-02 17:35:41 +03:00
2020-09-01 16:26:26 +03:00
key := newMapKeyCode(ctx, header)
ctx.incIndex()
2020-05-02 17:35:41 +03:00
2020-08-29 09:35:03 +03:00
ctx = ctx.decIndent()
2020-05-02 17:35:41 +03:00
2020-08-31 15:59:22 +03:00
header.mapKey = key
header.mapValue = value
2020-09-16 08:51:37 +03:00
end := newMapEndCode(ctx, header)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-05-02 17:35:41 +03:00
2020-08-29 09:11:31 +03:00
if ctx.withIndent {
2020-09-16 08:51:37 +03:00
header.op = header.op.toIndent()
key.op = key.op.toIndent()
value.op = value.op.toIndent()
end.op = end.op.toIndent()
2020-05-02 17:35:41 +03:00
}
header.next = keyCode
keyCode.beforeLastCode().next = (*opcode)(unsafe.Pointer(value))
value.next = valueCode
valueCode.beforeLastCode().next = (*opcode)(unsafe.Pointer(key))
key.next = keyCode
header.end = end
key.end = end
2020-09-16 08:51:37 +03:00
value.end = end
2020-04-29 18:31:50 +03:00
return (*opcode)(unsafe.Pointer(header)), nil
}
2020-08-15 11:41:38 +03:00
func (e *Encoder) typeToHeaderType(op opType) opType {
2020-05-02 17:35:41 +03:00
switch op {
case opInt:
return opStructFieldHeadInt
case opInt8:
return opStructFieldHeadInt8
case opInt16:
return opStructFieldHeadInt16
case opInt32:
return opStructFieldHeadInt32
case opInt64:
return opStructFieldHeadInt64
case opUint:
return opStructFieldHeadUint
case opUint8:
return opStructFieldHeadUint8
case opUint16:
return opStructFieldHeadUint16
case opUint32:
return opStructFieldHeadUint32
case opUint64:
return opStructFieldHeadUint64
case opFloat32:
return opStructFieldHeadFloat32
case opFloat64:
return opStructFieldHeadFloat64
case opString:
return opStructFieldHeadString
case opBool:
return opStructFieldHeadBool
case opMapHead:
return opStructFieldHeadMap
case opMapHeadLoad:
return opStructFieldHeadMapLoad
case opMapHeadIndent:
return opStructFieldHeadMapIndent
case opMapHeadLoadIndent:
return opStructFieldHeadMapLoadIndent
case opArrayHead:
return opStructFieldHeadArray
case opArrayHeadIndent:
return opStructFieldHeadArrayIndent
case opSliceHead:
return opStructFieldHeadSlice
case opSliceHeadIndent:
return opStructFieldHeadSliceIndent
case opStructFieldHead:
return opStructFieldHeadStruct
case opStructFieldHeadIndent:
return opStructFieldHeadStructIndent
case opMarshalJSON:
return opStructFieldHeadMarshalJSON
case opMarshalText:
return opStructFieldHeadMarshalText
2020-05-02 17:35:41 +03:00
}
return opStructFieldHead
}
2020-09-17 15:50:27 +03:00
func (e *Encoder) typeToFieldType(code *opcode) opType {
switch code.op {
case opPtr:
switch code.next.op {
case opInt:
return opStructFieldPtrInt
case opInt8:
return opStructFieldPtrInt8
case opInt16:
return opStructFieldPtrInt16
case opInt32:
return opStructFieldPtrInt32
case opInt64:
return opStructFieldPtrInt64
case opUint:
return opStructFieldPtrUint
case opUint8:
return opStructFieldPtrUint8
case opUint16:
return opStructFieldPtrUint16
case opUint32:
return opStructFieldPtrUint32
case opUint64:
return opStructFieldPtrUint64
case opFloat32:
return opStructFieldPtrFloat32
case opFloat64:
return opStructFieldPtrFloat64
case opString:
return opStructFieldPtrString
case opBool:
return opStructFieldPtrBool
}
2020-05-02 17:35:41 +03:00
case opInt:
return opStructFieldInt
case opInt8:
return opStructFieldInt8
case opInt16:
return opStructFieldInt16
case opInt32:
return opStructFieldInt32
case opInt64:
return opStructFieldInt64
case opUint:
return opStructFieldUint
case opUint8:
return opStructFieldUint8
case opUint16:
return opStructFieldUint16
case opUint32:
return opStructFieldUint32
case opUint64:
return opStructFieldUint64
case opFloat32:
return opStructFieldFloat32
case opFloat64:
return opStructFieldFloat64
case opString:
return opStructFieldString
case opBool:
return opStructFieldBool
case opMapHead:
return opStructFieldMap
case opMapHeadLoad:
return opStructFieldMapLoad
case opMapHeadIndent:
return opStructFieldMapIndent
case opMapHeadLoadIndent:
return opStructFieldMapLoadIndent
case opArrayHead:
return opStructFieldArray
case opArrayHeadIndent:
return opStructFieldArrayIndent
case opSliceHead:
return opStructFieldSlice
case opSliceHeadIndent:
return opStructFieldSliceIndent
case opStructFieldHead:
return opStructFieldStruct
case opStructFieldHeadIndent:
return opStructFieldStructIndent
case opMarshalJSON:
return opStructFieldMarshalJSON
case opMarshalText:
return opStructFieldMarshalText
2020-05-02 17:35:41 +03:00
}
return opStructField
}
2020-08-19 13:56:02 +03:00
func (e *Encoder) optimizeStructHeader(op opType, tag *structTag, withIndent bool) opType {
2020-08-15 11:41:38 +03:00
headType := e.typeToHeaderType(op)
2020-08-19 13:56:02 +03:00
switch {
case tag.isOmitEmpty:
2020-08-15 11:41:38 +03:00
headType = headType.headToOmitEmptyHead()
2020-08-19 13:56:02 +03:00
case tag.isString:
headType = headType.headToStringTagHead()
2020-08-15 11:41:38 +03:00
}
if withIndent {
return headType.toIndent()
}
return headType
}
2020-09-17 15:50:27 +03:00
func (e *Encoder) optimizeStructField(code *opcode, tag *structTag, withIndent bool) opType {
fieldType := e.typeToFieldType(code)
2020-08-19 13:56:02 +03:00
switch {
case tag.isOmitEmpty:
2020-08-15 11:41:38 +03:00
fieldType = fieldType.fieldToOmitEmptyField()
2020-08-19 13:56:02 +03:00
case tag.isString:
fieldType = fieldType.fieldToStringTagField()
2020-08-15 11:41:38 +03:00
}
if withIndent {
return fieldType.toIndent()
}
return fieldType
}
2020-08-31 15:59:22 +03:00
func (e *Encoder) recursiveCode(ctx *encodeCompileContext, jmp *compiledCode) *opcode {
code := newRecursiveCode(ctx, jmp)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-31 15:59:22 +03:00
return code
2020-08-14 16:44:09 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compiledCode(ctx *encodeCompileContext) *opcode {
typ := ctx.typ
2020-08-12 12:42:29 +03:00
typeptr := uintptr(unsafe.Pointer(typ))
2020-08-29 09:11:31 +03:00
if ctx.withIndent {
2020-08-14 16:44:09 +03:00
if compiledCode, exists := e.structTypeToCompiledIndentCode[typeptr]; exists {
2020-08-29 09:11:31 +03:00
return e.recursiveCode(ctx, compiledCode)
2020-08-12 12:42:29 +03:00
}
} else {
2020-08-14 16:44:09 +03:00
if compiledCode, exists := e.structTypeToCompiledCode[typeptr]; exists {
2020-08-29 09:11:31 +03:00
return e.recursiveCode(ctx, compiledCode)
2020-08-14 16:44:09 +03:00
}
}
return nil
}
2020-08-31 15:59:22 +03:00
func (e *Encoder) structHeader(ctx *encodeCompileContext, fieldCode *opcode, valueCode *opcode, tag *structTag) *opcode {
2020-08-14 16:44:09 +03:00
fieldCode.indent--
2020-08-30 11:32:26 +03:00
op := e.optimizeStructHeader(valueCode.op, tag, ctx.withIndent)
2020-08-14 16:44:09 +03:00
fieldCode.op = op
switch op {
case opStructFieldHead,
opStructFieldHeadSlice,
opStructFieldHeadArray,
opStructFieldHeadMap,
opStructFieldHeadMapLoad,
opStructFieldHeadStruct,
2020-08-14 16:44:09 +03:00
opStructFieldHeadOmitEmpty,
opStructFieldHeadOmitEmptySlice,
opStructFieldHeadOmitEmptyArray,
opStructFieldHeadOmitEmptyMap,
opStructFieldHeadOmitEmptyMapLoad,
opStructFieldHeadOmitEmptyStruct,
opStructFieldHeadStringTag,
2020-08-14 16:44:09 +03:00
opStructFieldHeadIndent,
opStructFieldHeadSliceIndent,
opStructFieldHeadArrayIndent,
opStructFieldHeadMapIndent,
opStructFieldHeadMapLoadIndent,
opStructFieldHeadStructIndent,
opStructFieldHeadOmitEmptyIndent,
opStructFieldHeadOmitEmptySliceIndent,
opStructFieldHeadOmitEmptyArrayIndent,
opStructFieldHeadOmitEmptyMapIndent,
opStructFieldHeadOmitEmptyMapLoadIndent,
opStructFieldHeadOmitEmptyStructIndent,
opStructFieldHeadStringTagIndent:
2020-08-14 16:44:09 +03:00
return valueCode.beforeLastCode()
}
2020-08-30 11:32:26 +03:00
ctx.decOpcodeIndex()
2020-08-14 16:44:09 +03:00
return (*opcode)(unsafe.Pointer(fieldCode))
}
2020-08-31 15:59:22 +03:00
func (e *Encoder) structField(ctx *encodeCompileContext, fieldCode *opcode, valueCode *opcode, tag *structTag) *opcode {
2020-08-14 16:44:09 +03:00
code := (*opcode)(unsafe.Pointer(fieldCode))
2020-09-17 15:50:27 +03:00
op := e.optimizeStructField(valueCode, tag, ctx.withIndent)
2020-08-14 16:44:09 +03:00
fieldCode.op = op
switch op {
case opStructField,
opStructFieldSlice,
opStructFieldArray,
opStructFieldMap,
opStructFieldMapLoad,
opStructFieldStruct,
2020-08-14 16:44:09 +03:00
opStructFieldOmitEmpty,
opStructFieldOmitEmptySlice,
opStructFieldOmitEmptyArray,
opStructFieldOmitEmptyMap,
opStructFieldOmitEmptyMapLoad,
opStructFieldOmitEmptyStruct,
opStructFieldStringTag,
2020-08-14 16:44:09 +03:00
opStructFieldIndent,
opStructFieldSliceIndent,
opStructFieldArrayIndent,
opStructFieldMapIndent,
opStructFieldMapLoadIndent,
opStructFieldStructIndent,
opStructFieldOmitEmptyIndent,
opStructFieldOmitEmptySliceIndent,
opStructFieldOmitEmptyArrayIndent,
opStructFieldOmitEmptyMapIndent,
opStructFieldOmitEmptyMapLoadIndent,
opStructFieldOmitEmptyStructIndent,
opStructFieldStringTagIndent:
2020-08-14 16:44:09 +03:00
return valueCode.beforeLastCode()
}
2020-09-01 16:26:26 +03:00
ctx.decIndex()
2020-08-14 16:44:09 +03:00
return code
}
2020-08-22 06:58:34 +03:00
2020-08-31 15:59:22 +03:00
func (e *Encoder) isNotExistsField(head *opcode) bool {
2020-08-22 06:58:34 +03:00
if head == nil {
return false
}
if head.op != opStructFieldAnonymousHead {
return false
}
if head.next == nil {
return false
}
if head.nextField == nil {
return false
}
if head.nextField.op != opStructAnonymousEnd {
return false
}
if head.next.op == opStructAnonymousEnd {
return true
}
if head.next.op.codeType() != codeStructField {
return false
}
2020-08-31 15:59:22 +03:00
return e.isNotExistsField(head.next)
2020-08-22 06:58:34 +03:00
}
2020-08-31 15:59:22 +03:00
func (e *Encoder) optimizeAnonymousFields(head *opcode) {
2020-08-22 06:58:34 +03:00
code := head
2020-08-31 15:59:22 +03:00
var prev *opcode
2020-08-22 06:58:34 +03:00
for {
if code.op == opStructEnd || code.op == opStructEndIndent {
break
}
if code.op == opStructField || code.op == opStructFieldIndent {
codeType := code.next.op.codeType()
if codeType == codeStructField {
2020-08-31 15:59:22 +03:00
if e.isNotExistsField(code.next) {
2020-08-22 06:58:34 +03:00
code.next = code.nextField
2020-08-30 21:14:37 +03:00
diff := code.next.displayIdx - code.displayIdx
2020-08-30 11:32:26 +03:00
for i := 0; i < diff; i++ {
code.next.decOpcodeIndex()
}
2020-08-22 06:58:34 +03:00
linkPrevToNextField(prev, code)
code = prev
}
}
}
prev = code
2020-08-31 15:59:22 +03:00
code = code.nextField
2020-08-22 06:58:34 +03:00
}
}
type structFieldPair struct {
2020-08-31 15:59:22 +03:00
prevField *opcode
curField *opcode
2020-08-22 09:40:18 +03:00
isTaggedKey bool
linked bool
}
2020-08-31 15:59:22 +03:00
func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, valueCode *opcode) map[string][]structFieldPair {
2020-08-22 09:40:18 +03:00
anonymousFields := map[string][]structFieldPair{}
f := valueCode
2020-08-31 15:59:22 +03:00
var prevAnonymousField *opcode
2020-08-22 09:40:18 +03:00
for {
existsKey := tags.existsKey(f.displayKey)
op := f.op.headToAnonymousHead()
if op != f.op {
if existsKey {
f.op = opStructFieldAnonymousHead
} else {
f.op = op
}
} else if f.op == opStructEnd {
f.op = opStructAnonymousEnd
} else if existsKey {
2020-08-30 21:14:37 +03:00
diff := f.nextField.displayIdx - f.displayIdx
2020-08-30 11:32:26 +03:00
for i := 0; i < diff; i++ {
f.nextField.decOpcodeIndex()
}
2020-08-22 09:40:18 +03:00
linkPrevToNextField(prevAnonymousField, f)
}
if f.displayKey == "" {
if f.nextField == nil {
break
}
prevAnonymousField = f
2020-08-31 15:59:22 +03:00
f = f.nextField
2020-08-22 09:40:18 +03:00
continue
}
anonymousFields[f.displayKey] = append(anonymousFields[f.displayKey], structFieldPair{
prevField: prevAnonymousField,
curField: f,
isTaggedKey: f.isTaggedKey,
})
if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField {
2020-08-31 15:59:22 +03:00
for k, v := range e.anonymousStructFieldPairMap(typ, tags, f.next) {
2020-08-22 09:40:18 +03:00
anonymousFields[k] = append(anonymousFields[k], v...)
}
}
if f.nextField == nil {
break
}
prevAnonymousField = f
2020-08-31 15:59:22 +03:00
f = f.nextField
2020-08-22 09:40:18 +03:00
}
return anonymousFields
}
func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPair) {
for _, fieldPairs := range anonymousFields {
if len(fieldPairs) == 1 {
continue
}
// conflict anonymous fields
taggedPairs := []structFieldPair{}
for _, fieldPair := range fieldPairs {
if fieldPair.isTaggedKey {
taggedPairs = append(taggedPairs, fieldPair)
} else {
if !fieldPair.linked {
if fieldPair.prevField == nil {
// head operation
fieldPair.curField.op = opStructFieldAnonymousHead
} else {
2020-08-30 21:14:37 +03:00
diff := fieldPair.curField.nextField.displayIdx - fieldPair.curField.displayIdx
2020-08-30 11:32:26 +03:00
for i := 0; i < diff; i++ {
fieldPair.curField.nextField.decOpcodeIndex()
}
2020-08-22 09:40:18 +03:00
linkPrevToNextField(fieldPair.prevField, fieldPair.curField)
}
fieldPair.linked = true
}
}
}
if len(taggedPairs) > 1 {
for _, fieldPair := range taggedPairs {
if !fieldPair.linked {
if fieldPair.prevField == nil {
// head operation
fieldPair.curField.op = opStructFieldAnonymousHead
} else {
2020-08-30 21:14:37 +03:00
diff := fieldPair.curField.nextField.displayIdx - fieldPair.curField.displayIdx
2020-08-30 11:32:26 +03:00
for i := 0; i < diff; i++ {
fieldPair.curField.nextField.decOpcodeIndex()
}
2020-08-22 09:40:18 +03:00
linkPrevToNextField(fieldPair.prevField, fieldPair.curField)
}
fieldPair.linked = true
}
}
} else {
for _, fieldPair := range taggedPairs {
fieldPair.curField.isTaggedKey = false
}
}
}
2020-08-22 06:58:34 +03:00
}
2020-08-29 09:11:31 +03:00
func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode, error) {
ctx.root = false
if code := e.compiledCode(ctx); code != nil {
2020-08-14 16:44:09 +03:00
return code, nil
}
2020-08-29 09:11:31 +03:00
typ := ctx.typ
2020-08-14 16:44:09 +03:00
typeptr := uintptr(unsafe.Pointer(typ))
2020-08-12 12:42:29 +03:00
compiled := &compiledCode{}
2020-08-29 09:11:31 +03:00
if ctx.withIndent {
2020-08-12 12:42:29 +03:00
e.structTypeToCompiledIndentCode[typeptr] = compiled
2020-08-13 09:26:35 +03:00
} else {
e.structTypeToCompiledCode[typeptr] = compiled
2020-08-12 12:42:29 +03:00
}
2020-04-30 05:56:56 +03:00
// header => code => structField => code => end
// ^ |
// |__________|
2020-04-29 18:31:50 +03:00
fieldNum := typ.NumField()
fieldIdx := 0
2020-04-29 19:44:48 +03:00
var (
2020-08-31 15:59:22 +03:00
head *opcode
2020-04-29 19:44:48 +03:00
code *opcode
2020-08-31 15:59:22 +03:00
prevField *opcode
2020-04-29 19:44:48 +03:00
)
2020-08-29 09:35:03 +03:00
ctx = ctx.incIndent()
2020-08-22 06:58:34 +03:00
tags := structTags{}
2020-08-22 09:40:18 +03:00
anonymousFields := map[string][]structFieldPair{}
2020-04-29 18:31:50 +03:00
for i := 0; i < fieldNum; i++ {
field := typ.Field(i)
2020-08-20 06:38:50 +03:00
if isIgnoredStructField(field) {
2020-04-29 18:31:50 +03:00
continue
}
2020-08-22 06:58:34 +03:00
tags = append(tags, structTagFromField(field))
}
for i, tag := range tags {
field := tag.field
2020-04-29 18:31:50 +03:00
fieldType := type2rtype(field.Type)
if isPtr && i == 0 {
// head field of pointer structure at top level
// if field type is pointer and implements MarshalJSON or MarshalText,
// it need to operation of dereference of pointer.
if field.Type.Kind() == reflect.Ptr &&
(field.Type.Implements(marshalJSONType) || field.Type.Implements(marshalTextType)) {
fieldType = rtype_ptrTo(fieldType)
}
}
2020-08-30 11:32:26 +03:00
fieldOpcodeIndex := ctx.opcodeIndex
2020-09-01 16:26:26 +03:00
fieldPtrIndex := ctx.ptrIndex
ctx.incIndex()
2020-08-29 09:11:31 +03:00
valueCode, err := e.compile(ctx.withType(fieldType))
2020-04-29 18:31:50 +03:00
if err != nil {
return nil, err
}
2020-08-30 11:32:26 +03:00
2020-08-15 11:41:38 +03:00
if field.Anonymous {
2020-08-31 15:59:22 +03:00
for k, v := range e.anonymousStructFieldPairMap(typ, tags, valueCode) {
2020-08-22 09:40:18 +03:00
anonymousFields[k] = append(anonymousFields[k], v...)
2020-08-15 11:41:38 +03:00
}
}
if fieldNum == 1 && valueCode.op == opPtr {
// if field number is one and primitive pointer type,
// it should encode as **not** pointer .
switch valueCode.next.op {
case opInt, opInt8, opInt16, opInt32, opInt64,
opUint, opUint8, opUint16, opUint32, opUint64,
opFloat32, opFloat64, opBool, opString, opBytes:
valueCode = valueCode.next
2020-08-30 11:32:26 +03:00
ctx.decOpcodeIndex()
}
}
2020-08-19 13:56:02 +03:00
key := fmt.Sprintf(`"%s":`, tag.key)
2020-09-16 19:26:39 +03:00
var buf bytes.Buffer
enc := NewEncoder(&buf)
enc.encodeEscapedString(tag.key)
escapedKey := fmt.Sprintf(`%s:`, string(enc.buf))
enc.release()
2020-08-31 15:59:22 +03:00
fieldCode := &opcode{
typ: valueCode.typ,
displayIdx: fieldOpcodeIndex,
2020-09-01 16:26:26 +03:00
idx: opcodeOffset(fieldPtrIndex),
2020-08-31 15:59:22 +03:00
next: valueCode,
indent: ctx.indent,
2020-08-15 11:41:38 +03:00
anonymousKey: field.Anonymous,
key: []byte(key),
2020-09-16 19:26:39 +03:00
escapedKey: []byte(escapedKey),
2020-08-22 09:40:18 +03:00
isTaggedKey: tag.isTaggedKey,
2020-08-22 06:58:34 +03:00
displayKey: tag.key,
2020-08-15 11:41:38 +03:00
offset: field.Offset,
2020-04-30 05:56:56 +03:00
}
2020-04-29 18:31:50 +03:00
if fieldIdx == 0 {
2020-09-01 16:26:26 +03:00
fieldCode.headIdx = fieldCode.idx
2020-08-30 11:32:26 +03:00
code = e.structHeader(ctx, fieldCode, valueCode, tag)
2020-04-29 19:44:48 +03:00
head = fieldCode
2020-04-29 18:31:50 +03:00
prevField = fieldCode
} else {
2020-09-01 16:26:26 +03:00
fieldCode.headIdx = head.headIdx
code.next = fieldCode
2020-08-30 11:32:26 +03:00
code = e.structField(ctx, fieldCode, valueCode, tag)
2020-09-01 16:26:26 +03:00
prevField.nextField = fieldCode
2020-04-29 18:31:50 +03:00
prevField = fieldCode
}
fieldIdx++
}
2020-08-29 09:35:03 +03:00
ctx = ctx.decIndent()
2020-05-01 07:12:01 +03:00
2020-08-31 15:59:22 +03:00
structEndCode := &opcode{
op: opStructEnd,
typ: nil,
indent: ctx.indent,
next: newEndOp(ctx),
}
2020-05-01 07:12:01 +03:00
// no struct field
if head == nil {
2020-08-31 15:59:22 +03:00
head = &opcode{
op: opStructFieldHead,
typ: typ,
displayIdx: ctx.opcodeIndex,
2020-09-01 16:26:26 +03:00
idx: opcodeOffset(ctx.ptrIndex),
headIdx: opcodeOffset(ctx.ptrIndex),
2020-08-31 15:59:22 +03:00
indent: ctx.indent,
nextField: structEndCode,
2020-05-01 07:12:01 +03:00
}
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-29 09:11:31 +03:00
if ctx.withIndent {
2020-05-02 17:35:41 +03:00
head.op = opStructFieldHeadIndent
}
2020-08-31 15:59:22 +03:00
code = head
2020-05-01 07:12:01 +03:00
}
2020-08-30 11:32:26 +03:00
2020-08-30 21:14:37 +03:00
structEndCode.displayIdx = ctx.opcodeIndex
2020-09-01 17:23:07 +03:00
structEndCode.idx = opcodeOffset(ctx.ptrIndex)
2020-09-01 16:26:26 +03:00
ctx.incIndex()
2020-08-30 11:32:26 +03:00
if ctx.withIndent {
structEndCode.op = opStructEndIndent
}
if prevField != nil && prevField.nextField == nil {
prevField.nextField = structEndCode
}
2020-04-29 19:44:48 +03:00
head.end = structEndCode
2020-04-29 18:31:50 +03:00
code.next = structEndCode
2020-08-22 06:58:34 +03:00
2020-08-22 09:40:18 +03:00
e.optimizeConflictAnonymousFields(anonymousFields)
2020-08-22 06:58:34 +03:00
e.optimizeAnonymousFields(head)
2020-08-12 12:42:29 +03:00
ret := (*opcode)(unsafe.Pointer(head))
compiled.code = ret
2020-08-15 15:13:08 +03:00
2020-08-29 09:11:31 +03:00
if ctx.withIndent {
2020-08-15 15:13:08 +03:00
delete(e.structTypeToCompiledIndentCode, typeptr)
} else {
delete(e.structTypeToCompiledCode, typeptr)
}
2020-08-12 12:42:29 +03:00
return ret, nil
2020-04-29 18:31:50 +03:00
}