go-json/encode_compile.go

907 lines
25 KiB
Go
Raw Normal View History

2020-04-29 18:31:50 +03:00
package json
import (
"fmt"
"reflect"
"strings"
"unsafe"
)
2020-05-04 12:39:17 +03:00
func (e *Encoder) compileHead(typ *rtype, withIndent bool) (*opcode, error) {
if typ.Implements(marshalJSONType) {
return newOpCode(opMarshalJSON, typ, e.indent, newEndOp(e.indent)), nil
} else if typ.Implements(marshalTextType) {
return newOpCode(opMarshalText, typ, e.indent, newEndOp(e.indent)), nil
}
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
2020-08-12 10:54:15 +03:00
root := true
if typ.Kind() == reflect.Map {
2020-08-12 10:54:15 +03:00
return e.compileMap(typ, false, root, withIndent)
}
2020-08-12 10:54:15 +03:00
return e.compile(typ, root, withIndent)
2020-05-04 12:39:17 +03:00
}
2020-08-12 10:54:15 +03:00
func (e *Encoder) compile(typ *rtype, root, withIndent bool) (*opcode, error) {
2020-05-04 12:39:17 +03:00
if typ.Implements(marshalJSONType) {
return newOpCode(opMarshalJSON, typ, e.indent, newEndOp(e.indent)), nil
} else if typ.Implements(marshalTextType) {
return newOpCode(opMarshalText, typ, e.indent, newEndOp(e.indent)), nil
}
2020-04-29 18:31:50 +03:00
switch typ.Kind() {
case reflect.Ptr:
2020-08-12 10:54:15 +03:00
return e.compilePtr(typ, root, withIndent)
2020-04-29 18:31:50 +03:00
case reflect.Slice:
2020-08-12 10:54:15 +03:00
return e.compileSlice(typ, root, withIndent)
case reflect.Array:
2020-08-12 10:54:15 +03:00
return e.compileArray(typ, root, withIndent)
case reflect.Map:
2020-08-12 10:54:15 +03:00
return e.compileMap(typ, true, root, withIndent)
2020-04-29 18:31:50 +03:00
case reflect.Struct:
2020-08-12 10:54:15 +03:00
return e.compileStruct(typ, root, withIndent)
case reflect.Interface:
return e.compileInterface(typ, root)
2020-04-29 18:31:50 +03:00
case reflect.Int:
2020-04-30 07:52:24 +03:00
return e.compileInt(typ)
2020-04-29 18:31:50 +03:00
case reflect.Int8:
2020-04-30 07:52:24 +03:00
return e.compileInt8(typ)
2020-04-29 18:31:50 +03:00
case reflect.Int16:
2020-04-30 07:52:24 +03:00
return e.compileInt16(typ)
2020-04-29 18:31:50 +03:00
case reflect.Int32:
2020-04-30 07:52:24 +03:00
return e.compileInt32(typ)
2020-04-29 18:31:50 +03:00
case reflect.Int64:
2020-04-30 07:52:24 +03:00
return e.compileInt64(typ)
2020-04-29 18:31:50 +03:00
case reflect.Uint:
2020-04-30 07:52:24 +03:00
return e.compileUint(typ)
2020-04-29 18:31:50 +03:00
case reflect.Uint8:
2020-04-30 07:52:24 +03:00
return e.compileUint8(typ)
2020-04-29 18:31:50 +03:00
case reflect.Uint16:
2020-04-30 07:52:24 +03:00
return e.compileUint16(typ)
2020-04-29 18:31:50 +03:00
case reflect.Uint32:
2020-04-30 07:52:24 +03:00
return e.compileUint32(typ)
2020-04-29 18:31:50 +03:00
case reflect.Uint64:
2020-04-30 07:52:24 +03:00
return e.compileUint64(typ)
2020-04-29 18:31:50 +03:00
case reflect.Uintptr:
2020-04-30 07:52:24 +03:00
return e.compileUint(typ)
2020-04-29 18:31:50 +03:00
case reflect.Float32:
2020-04-30 07:52:24 +03:00
return e.compileFloat32(typ)
2020-04-29 18:31:50 +03:00
case reflect.Float64:
2020-04-30 07:52:24 +03:00
return e.compileFloat64(typ)
2020-04-29 18:31:50 +03:00
case reflect.String:
2020-04-30 07:52:24 +03:00
return e.compileString(typ)
2020-04-29 18:31:50 +03:00
case reflect.Bool:
2020-04-30 07:52:24 +03:00
return e.compileBool(typ)
2020-04-29 18:31:50 +03:00
}
return nil, &UnsupportedTypeError{Type: rtype2type(typ)}
2020-04-29 18:31:50 +03:00
}
2020-04-30 05:56:56 +03:00
func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode {
2020-04-29 19:44:48 +03:00
switch code.op {
case opStructFieldHead:
code.op = opStructFieldPtrHead
case opStructFieldHeadInt:
code.op = opStructFieldPtrHeadInt
2020-04-30 05:56:56 +03:00
case opStructFieldHeadInt8:
code.op = opStructFieldPtrHeadInt8
case opStructFieldHeadInt16:
code.op = opStructFieldPtrHeadInt16
case opStructFieldHeadInt32:
code.op = opStructFieldPtrHeadInt32
case opStructFieldHeadInt64:
code.op = opStructFieldPtrHeadInt64
case opStructFieldHeadUint:
code.op = opStructFieldPtrHeadUint
case opStructFieldHeadUint8:
code.op = opStructFieldPtrHeadUint8
case opStructFieldHeadUint16:
code.op = opStructFieldPtrHeadUint16
case opStructFieldHeadUint32:
code.op = opStructFieldPtrHeadUint32
case opStructFieldHeadUint64:
code.op = opStructFieldPtrHeadUint64
case opStructFieldHeadFloat32:
code.op = opStructFieldPtrHeadFloat32
case opStructFieldHeadFloat64:
code.op = opStructFieldPtrHeadFloat64
2020-04-29 19:44:48 +03:00
case opStructFieldHeadString:
code.op = opStructFieldPtrHeadString
2020-04-30 05:56:56 +03:00
case opStructFieldHeadBool:
code.op = opStructFieldPtrHeadBool
2020-05-01 07:12:01 +03:00
case opStructFieldHeadOmitEmpty:
code.op = opStructFieldPtrHeadOmitEmpty
case opStructFieldHeadIntOmitEmpty:
code.op = opStructFieldPtrHeadIntOmitEmpty
case opStructFieldHeadInt8OmitEmpty:
code.op = opStructFieldPtrHeadInt8OmitEmpty
case opStructFieldHeadInt16OmitEmpty:
code.op = opStructFieldPtrHeadInt16OmitEmpty
case opStructFieldHeadInt32OmitEmpty:
code.op = opStructFieldPtrHeadInt32OmitEmpty
case opStructFieldHeadInt64OmitEmpty:
code.op = opStructFieldPtrHeadInt64OmitEmpty
case opStructFieldHeadUintOmitEmpty:
code.op = opStructFieldPtrHeadUintOmitEmpty
case opStructFieldHeadUint8OmitEmpty:
code.op = opStructFieldPtrHeadUint8OmitEmpty
case opStructFieldHeadUint16OmitEmpty:
code.op = opStructFieldPtrHeadUint16OmitEmpty
case opStructFieldHeadUint32OmitEmpty:
code.op = opStructFieldPtrHeadUint32OmitEmpty
case opStructFieldHeadUint64OmitEmpty:
code.op = opStructFieldPtrHeadUint64OmitEmpty
case opStructFieldHeadFloat32OmitEmpty:
code.op = opStructFieldPtrHeadFloat32OmitEmpty
case opStructFieldHeadFloat64OmitEmpty:
code.op = opStructFieldPtrHeadFloat64OmitEmpty
case opStructFieldHeadStringOmitEmpty:
code.op = opStructFieldPtrHeadStringOmitEmpty
case opStructFieldHeadBoolOmitEmpty:
code.op = opStructFieldPtrHeadBoolOmitEmpty
2020-05-02 17:35:41 +03:00
case opStructFieldHeadIndent:
code.op = opStructFieldPtrHeadIndent
case opStructFieldHeadIntIndent:
code.op = opStructFieldPtrHeadIntIndent
case opStructFieldHeadInt8Indent:
code.op = opStructFieldPtrHeadInt8Indent
case opStructFieldHeadInt16Indent:
code.op = opStructFieldPtrHeadInt16Indent
case opStructFieldHeadInt32Indent:
code.op = opStructFieldPtrHeadInt32Indent
case opStructFieldHeadInt64Indent:
code.op = opStructFieldPtrHeadInt64Indent
case opStructFieldHeadUintIndent:
code.op = opStructFieldPtrHeadUintIndent
case opStructFieldHeadUint8Indent:
code.op = opStructFieldPtrHeadUint8Indent
case opStructFieldHeadUint16Indent:
code.op = opStructFieldPtrHeadUint16Indent
case opStructFieldHeadUint32Indent:
code.op = opStructFieldPtrHeadUint32Indent
case opStructFieldHeadUint64Indent:
code.op = opStructFieldPtrHeadUint64Indent
case opStructFieldHeadFloat32Indent:
code.op = opStructFieldPtrHeadFloat32Indent
case opStructFieldHeadFloat64Indent:
code.op = opStructFieldPtrHeadFloat64Indent
case opStructFieldHeadStringIndent:
code.op = opStructFieldPtrHeadStringIndent
case opStructFieldHeadBoolIndent:
code.op = opStructFieldPtrHeadBoolIndent
case opStructFieldHeadOmitEmptyIndent:
code.op = opStructFieldPtrHeadOmitEmptyIndent
case opStructFieldHeadIntOmitEmptyIndent:
code.op = opStructFieldPtrHeadIntOmitEmptyIndent
case opStructFieldHeadInt8OmitEmptyIndent:
code.op = opStructFieldPtrHeadInt8OmitEmptyIndent
case opStructFieldHeadInt16OmitEmptyIndent:
code.op = opStructFieldPtrHeadInt16OmitEmptyIndent
case opStructFieldHeadInt32OmitEmptyIndent:
code.op = opStructFieldPtrHeadInt32OmitEmptyIndent
case opStructFieldHeadInt64OmitEmptyIndent:
code.op = opStructFieldPtrHeadInt64OmitEmptyIndent
case opStructFieldHeadUintOmitEmptyIndent:
code.op = opStructFieldPtrHeadUintOmitEmptyIndent
case opStructFieldHeadUint8OmitEmptyIndent:
code.op = opStructFieldPtrHeadUint8OmitEmptyIndent
case opStructFieldHeadUint16OmitEmptyIndent:
code.op = opStructFieldPtrHeadUint16OmitEmptyIndent
case opStructFieldHeadUint32OmitEmptyIndent:
code.op = opStructFieldPtrHeadUint32OmitEmptyIndent
case opStructFieldHeadUint64OmitEmptyIndent:
code.op = opStructFieldPtrHeadUint64OmitEmptyIndent
case opStructFieldHeadFloat32OmitEmptyIndent:
code.op = opStructFieldPtrHeadFloat32OmitEmptyIndent
case opStructFieldHeadFloat64OmitEmptyIndent:
code.op = opStructFieldPtrHeadFloat64OmitEmptyIndent
case opStructFieldHeadStringOmitEmptyIndent:
code.op = opStructFieldPtrHeadStringOmitEmptyIndent
case opStructFieldHeadBoolOmitEmptyIndent:
code.op = opStructFieldPtrHeadBoolOmitEmptyIndent
2020-04-29 19:44:48 +03:00
default:
2020-05-02 17:35:41 +03:00
return newOpCode(opPtr, typ, e.indent, code)
2020-04-30 05:56:56 +03:00
}
return code
}
2020-08-12 10:54:15 +03:00
func (e *Encoder) compilePtr(typ *rtype, root, withIndent bool) (*opcode, error) {
code, err := e.compile(typ.Elem(), root, withIndent)
2020-04-30 05:56:56 +03:00
if err != nil {
return nil, err
2020-04-29 19:44:48 +03:00
}
2020-04-30 05:56:56 +03:00
return e.optimizeStructFieldPtrHead(typ, code), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileInt(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opInt, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileInt8(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opInt8, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileInt16(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opInt16, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileInt32(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opInt32, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileInt64(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opInt64, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileUint(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opUint, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileUint8(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opUint8, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileUint16(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opUint16, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileUint32(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opUint32, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileUint64(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opUint64, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileFloat32(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opFloat32, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileFloat64(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opFloat64, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileString(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opString, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) compileBool(typ *rtype) (*opcode, error) {
2020-05-02 17:35:41 +03:00
return newOpCode(opBool, typ, e.indent, newEndOp(e.indent)), nil
2020-04-29 18:31:50 +03:00
}
2020-08-12 10:54:15 +03:00
func (e *Encoder) compileInterface(typ *rtype, root bool) (*opcode, error) {
return (*opcode)(unsafe.Pointer(&interfaceCode{
opcodeHeader: &opcodeHeader{
op: opInterface,
typ: typ,
indent: e.indent,
next: newEndOp(e.indent),
},
root: root,
})), nil
}
2020-08-12 10:54:15 +03:00
func (e *Encoder) compileSlice(typ *rtype, root, withIndent bool) (*opcode, error) {
2020-04-29 18:31:50 +03:00
elem := typ.Elem()
size := elem.Size()
2020-05-02 17:35:41 +03:00
e.indent++
2020-08-12 10:54:15 +03:00
code, err := e.compile(elem, false, withIndent)
2020-05-02 17:35:41 +03:00
e.indent--
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-05-02 17:35:41 +03:00
header := newSliceHeaderCode(e.indent)
elemCode := &sliceElemCode{
opcodeHeader: &opcodeHeader{
op: opSliceElem,
indent: e.indent,
},
size: size,
}
end := newOpCode(opSliceEnd, nil, e.indent, newEndOp(e.indent))
if withIndent {
2020-08-12 10:54:15 +03:00
if root {
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-12 10:54:15 +03:00
func (e *Encoder) compileArray(typ *rtype, root, withIndent bool) (*opcode, error) {
elem := typ.Elem()
alen := typ.Len()
size := elem.Size()
2020-05-02 17:35:41 +03:00
e.indent++
2020-08-12 10:54:15 +03:00
code, err := e.compile(elem, false, withIndent)
2020-05-02 17:35:41 +03:00
e.indent--
if err != nil {
return nil, err
}
// header => opcode => elem => end
// ^ |
// |________|
2020-05-02 17:35:41 +03:00
header := newArrayHeaderCode(e.indent, alen)
elemCode := &arrayElemCode{
opcodeHeader: &opcodeHeader{
op: opArrayElem,
},
len: uintptr(alen),
size: size,
}
2020-05-02 17:35:41 +03:00
end := newOpCode(opArrayEnd, nil, e.indent, newEndOp(e.indent))
if withIndent {
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-12 10:54:15 +03:00
func (e *Encoder) compileMap(typ *rtype, withLoad, root, withIndent bool) (*opcode, error) {
// header => code => value => code => key => code => value => code => end
// ^ |
// |_______________________|
2020-05-02 17:35:41 +03:00
e.indent++
keyType := typ.Key()
2020-08-12 10:54:15 +03:00
keyCode, err := e.compile(keyType, false, withIndent)
if err != nil {
return nil, err
}
valueType := typ.Elem()
2020-08-12 10:54:15 +03:00
valueCode, err := e.compile(valueType, false, withIndent)
if err != nil {
return nil, err
}
2020-05-02 17:35:41 +03:00
key := newMapKeyCode(e.indent)
value := newMapValueCode(e.indent)
e.indent--
header := newMapHeaderCode(typ, withLoad, e.indent)
header.key = key
header.value = value
2020-05-02 17:35:41 +03:00
end := newOpCode(opMapEnd, nil, e.indent, newEndOp(e.indent))
if withIndent {
if header.op == opMapHead {
2020-08-12 10:54:15 +03:00
if root {
header.op = opRootMapHeadIndent
} else {
header.op = opMapHeadIndent
}
} else {
header.op = opMapHeadLoadIndent
}
2020-08-12 10:54:15 +03:00
if root {
key.op = opRootMapKeyIndent
} else {
key.op = opMapKeyIndent
}
2020-05-02 17:35:41 +03:00
value.op = opMapValueIndent
end.op = opMapEndIndent
}
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-04-29 18:31:50 +03:00
return (*opcode)(unsafe.Pointer(header)), nil
}
2020-04-30 07:52:24 +03:00
func (e *Encoder) getTag(field reflect.StructField) string {
return field.Tag.Get("json")
}
func (e *Encoder) isIgnoredStructField(field reflect.StructField) bool {
if field.PkgPath != "" && !field.Anonymous {
// private field
return true
}
tag := e.getTag(field)
if tag == "-" {
return true
}
return false
}
2020-05-02 17:35:41 +03:00
func (e *Encoder) optimizeStructHeaderOmitEmptyIndent(op opType) opType {
switch op {
case opInt:
return opStructFieldHeadIntOmitEmptyIndent
case opInt8:
return opStructFieldHeadInt8OmitEmptyIndent
case opInt16:
return opStructFieldHeadInt16OmitEmptyIndent
case opInt32:
return opStructFieldHeadInt32OmitEmptyIndent
case opInt64:
return opStructFieldHeadInt64OmitEmptyIndent
case opUint:
return opStructFieldHeadUintOmitEmptyIndent
case opUint8:
return opStructFieldHeadUint8OmitEmptyIndent
case opUint16:
return opStructFieldHeadUint16OmitEmptyIndent
case opUint32:
return opStructFieldHeadUint32OmitEmptyIndent
case opUint64:
return opStructFieldHeadUint64OmitEmptyIndent
case opFloat32:
return opStructFieldHeadFloat32OmitEmptyIndent
case opFloat64:
return opStructFieldHeadFloat64OmitEmptyIndent
case opString:
return opStructFieldHeadStringOmitEmptyIndent
case opBool:
return opStructFieldHeadBoolOmitEmptyIndent
}
return opStructFieldHeadOmitEmptyIndent
}
func (e *Encoder) optimizeStructHeaderIndent(op opType, isOmitEmpty bool) opType {
if isOmitEmpty {
return e.optimizeStructHeaderOmitEmptyIndent(op)
}
switch op {
case opInt:
return opStructFieldHeadIntIndent
case opInt8:
return opStructFieldHeadInt8Indent
case opInt16:
return opStructFieldHeadInt16Indent
case opInt32:
return opStructFieldHeadInt32Indent
case opInt64:
return opStructFieldHeadInt64Indent
case opUint:
return opStructFieldHeadUintIndent
case opUint8:
return opStructFieldHeadUint8Indent
case opUint16:
return opStructFieldHeadUint16Indent
case opUint32:
return opStructFieldHeadUint32Indent
case opUint64:
return opStructFieldHeadUint64Indent
case opFloat32:
return opStructFieldHeadFloat32Indent
case opFloat64:
return opStructFieldHeadFloat64Indent
case opString:
return opStructFieldHeadStringIndent
case opBool:
return opStructFieldHeadBoolIndent
}
return opStructFieldHeadIndent
}
func (e *Encoder) optimizeStructHeaderOmitEmpty(op opType) opType {
switch op {
case opInt:
return opStructFieldHeadIntOmitEmpty
case opInt8:
return opStructFieldHeadInt8OmitEmpty
case opInt16:
return opStructFieldHeadInt16OmitEmpty
case opInt32:
return opStructFieldHeadInt32OmitEmpty
case opInt64:
return opStructFieldHeadInt64OmitEmpty
case opUint:
return opStructFieldHeadUintOmitEmpty
case opUint8:
return opStructFieldHeadUint8OmitEmpty
case opUint16:
return opStructFieldHeadUint16OmitEmpty
case opUint32:
return opStructFieldHeadUint32OmitEmpty
case opUint64:
return opStructFieldHeadUint64OmitEmpty
case opFloat32:
return opStructFieldHeadFloat32OmitEmpty
case opFloat64:
return opStructFieldHeadFloat64OmitEmpty
case opString:
return opStructFieldHeadStringOmitEmpty
case opBool:
return opStructFieldHeadBoolOmitEmpty
}
return opStructFieldHeadOmitEmpty
}
func (e *Encoder) optimizeStructHeader(op opType, isOmitEmpty, withIndent bool) opType {
if withIndent {
return e.optimizeStructHeaderIndent(op, isOmitEmpty)
}
if isOmitEmpty {
return e.optimizeStructHeaderOmitEmpty(op)
}
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
}
return opStructFieldHead
}
func (e *Encoder) optimizeStructFieldOmitEmptyIndent(op opType) opType {
switch op {
case opInt:
return opStructFieldIntOmitEmptyIndent
case opInt8:
return opStructFieldInt8OmitEmptyIndent
case opInt16:
return opStructFieldInt16OmitEmptyIndent
case opInt32:
return opStructFieldInt32OmitEmptyIndent
case opInt64:
return opStructFieldInt64OmitEmptyIndent
case opUint:
return opStructFieldUintOmitEmptyIndent
case opUint8:
return opStructFieldUint8OmitEmptyIndent
case opUint16:
return opStructFieldUint16OmitEmptyIndent
case opUint32:
return opStructFieldUint32OmitEmptyIndent
case opUint64:
return opStructFieldUint64OmitEmptyIndent
case opFloat32:
return opStructFieldFloat32OmitEmptyIndent
case opFloat64:
return opStructFieldFloat64OmitEmptyIndent
case opString:
return opStructFieldStringOmitEmptyIndent
case opBool:
return opStructFieldBoolOmitEmptyIndent
}
return opStructFieldOmitEmptyIndent
}
func (e *Encoder) optimizeStructFieldIndent(op opType, isOmitEmpty bool) opType {
if isOmitEmpty {
return e.optimizeStructFieldOmitEmptyIndent(op)
}
switch op {
case opInt:
return opStructFieldIntIndent
case opInt8:
return opStructFieldInt8Indent
case opInt16:
return opStructFieldInt16Indent
case opInt32:
return opStructFieldInt32Indent
case opInt64:
return opStructFieldInt64Indent
case opUint:
return opStructFieldUintIndent
case opUint8:
return opStructFieldUint8Indent
case opUint16:
return opStructFieldUint16Indent
case opUint32:
return opStructFieldUint32Indent
case opUint64:
return opStructFieldUint64Indent
case opFloat32:
return opStructFieldFloat32Indent
case opFloat64:
return opStructFieldFloat64Indent
case opString:
return opStructFieldStringIndent
case opBool:
return opStructFieldBoolIndent
}
return opStructFieldIndent
}
func (e *Encoder) optimizeStructFieldOmitEmpty(op opType) opType {
switch op {
case opInt:
return opStructFieldIntOmitEmpty
case opInt8:
return opStructFieldInt8OmitEmpty
case opInt16:
return opStructFieldInt16OmitEmpty
case opInt32:
return opStructFieldInt32OmitEmpty
case opInt64:
return opStructFieldInt64OmitEmpty
case opUint:
return opStructFieldUintOmitEmpty
case opUint8:
return opStructFieldUint8OmitEmpty
case opUint16:
return opStructFieldUint16OmitEmpty
case opUint32:
return opStructFieldUint32OmitEmpty
case opUint64:
return opStructFieldUint64OmitEmpty
case opFloat32:
return opStructFieldFloat32OmitEmpty
case opFloat64:
return opStructFieldFloat64OmitEmpty
case opString:
return opStructFieldStringOmitEmpty
case opBool:
return opStructFieldBoolOmitEmpty
}
return opStructFieldOmitEmpty
}
func (e *Encoder) optimizeStructField(op opType, isOmitEmpty, withIndent bool) opType {
if withIndent {
return e.optimizeStructFieldIndent(op, isOmitEmpty)
}
if isOmitEmpty {
return e.optimizeStructFieldOmitEmpty(op)
}
switch op {
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
}
return opStructField
}
2020-08-14 16:44:09 +03:00
func (e *Encoder) recursiveCode(typ *rtype, code *compiledCode) *opcode {
return (*opcode)(unsafe.Pointer(&recursiveCode{
opcodeHeader: &opcodeHeader{
op: opStructFieldRecursive,
typ: typ,
indent: e.indent,
next: newEndOp(e.indent),
},
jmp: code,
}))
}
func (e *Encoder) compiledCode(typ *rtype, withIndent bool) *opcode {
2020-08-12 12:42:29 +03:00
typeptr := uintptr(unsafe.Pointer(typ))
if withIndent {
2020-08-14 16:44:09 +03:00
if compiledCode, exists := e.structTypeToCompiledIndentCode[typeptr]; exists {
return e.recursiveCode(typ, 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 {
return e.recursiveCode(typ, compiledCode)
}
}
return nil
}
func (e *Encoder) keyNameAndOmitEmptyFromField(field reflect.StructField) (string, bool) {
keyName := field.Name
tag := e.getTag(field)
opts := strings.Split(tag, ",")
if len(opts) > 0 {
if opts[0] != "" {
keyName = opts[0]
2020-08-12 12:42:29 +03:00
}
}
2020-08-14 16:44:09 +03:00
isOmitEmpty := false
if len(opts) > 1 {
isOmitEmpty = opts[1] == "omitempty"
}
return keyName, isOmitEmpty
}
func (e *Encoder) structHeader(fieldCode *structFieldCode, valueCode *opcode, isOmitEmpty, withIndent bool) *opcode {
fieldCode.indent--
op := e.optimizeStructHeader(valueCode.op, isOmitEmpty, withIndent)
fieldCode.op = op
switch op {
case opStructFieldHead,
opStructFieldHeadOmitEmpty,
opStructFieldHeadIndent,
opStructFieldHeadOmitEmptyIndent:
return valueCode.beforeLastCode()
}
return (*opcode)(unsafe.Pointer(fieldCode))
}
func (e *Encoder) structField(fieldCode *structFieldCode, valueCode *opcode, isOmitEmpty, withIndent bool) *opcode {
code := (*opcode)(unsafe.Pointer(fieldCode))
op := e.optimizeStructField(valueCode.op, isOmitEmpty, withIndent)
fieldCode.op = op
switch op {
case opStructField,
opStructFieldOmitEmpty,
opStructFieldIndent,
opStructFieldOmitEmptyIndent:
return valueCode.beforeLastCode()
}
return code
}
func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, error) {
if code := e.compiledCode(typ, withIndent); code != nil {
return code, nil
}
typeptr := uintptr(unsafe.Pointer(typ))
2020-08-12 12:42:29 +03:00
compiled := &compiledCode{}
if withIndent {
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 (
head *structFieldCode
code *opcode
prevField *structFieldCode
)
2020-05-02 17:35:41 +03:00
e.indent++
2020-04-29 18:31:50 +03:00
for i := 0; i < fieldNum; i++ {
field := typ.Field(i)
if e.isIgnoredStructField(field) {
continue
}
2020-08-14 16:44:09 +03:00
keyName, isOmitEmpty := e.keyNameAndOmitEmptyFromField(field)
2020-04-29 18:31:50 +03:00
fieldType := type2rtype(field.Type)
2020-08-12 10:54:15 +03:00
valueCode, err := e.compile(fieldType, false, withIndent)
2020-04-29 18:31:50 +03:00
if err != nil {
return nil, err
}
key := fmt.Sprintf(`"%s":`, keyName)
2020-04-30 05:56:56 +03:00
fieldCode := &structFieldCode{
opcodeHeader: &opcodeHeader{
2020-05-02 17:35:41 +03:00
typ: fieldType,
next: valueCode,
indent: e.indent,
2020-04-30 05:56:56 +03:00
},
key: []byte(key),
offset: field.Offset,
}
2020-04-29 18:31:50 +03:00
if fieldIdx == 0 {
2020-08-14 16:44:09 +03:00
code = e.structHeader(fieldCode, valueCode, isOmitEmpty, withIndent)
2020-04-29 19:44:48 +03:00
head = fieldCode
2020-04-29 18:31:50 +03:00
prevField = fieldCode
} else {
2020-08-14 16:44:09 +03:00
fcode := (*opcode)(unsafe.Pointer(fieldCode))
code.next = fcode
code = e.structField(fieldCode, valueCode, isOmitEmpty, withIndent)
prevField.nextField = fcode
2020-04-29 18:31:50 +03:00
prevField = fieldCode
}
fieldIdx++
}
2020-05-02 17:35:41 +03:00
e.indent--
2020-05-01 07:12:01 +03:00
2020-05-02 17:35:41 +03:00
structEndCode := newOpCode(opStructEnd, nil, e.indent, nil)
2020-08-14 16:44:09 +03:00
structEndCode.next = newEndOp(e.indent)
2020-05-02 17:35:41 +03:00
if withIndent {
structEndCode.op = opStructEndIndent
}
2020-05-01 07:12:01 +03:00
if prevField != nil && prevField.nextField == nil {
prevField.nextField = structEndCode
}
// no struct field
if head == nil {
head = &structFieldCode{
opcodeHeader: &opcodeHeader{
2020-05-02 17:35:41 +03:00
op: opStructFieldHead,
typ: typ,
indent: e.indent,
2020-05-01 07:12:01 +03:00
},
nextField: structEndCode,
}
2020-05-02 17:35:41 +03:00
if withIndent {
head.op = opStructFieldHeadIndent
}
2020-05-01 07:12:01 +03:00
code = (*opcode)(unsafe.Pointer(head))
}
2020-04-29 19:44:48 +03:00
head.end = structEndCode
2020-04-29 18:31:50 +03:00
code.next = structEndCode
2020-08-12 12:42:29 +03:00
ret := (*opcode)(unsafe.Pointer(head))
compiled.code = ret
return ret, nil
2020-04-29 18:31:50 +03:00
}