go-json/encode_compile.go

240 lines
6.0 KiB
Go
Raw Normal View History

2020-04-29 18:31:50 +03:00
package json
import (
"fmt"
"reflect"
"strings"
"unsafe"
"golang.org/x/xerrors"
)
func (e *Encoder) compileOp(typ *rtype) (*opcode, error) {
switch typ.Kind() {
case reflect.Ptr:
return e.compilePtrOp(typ)
case reflect.Slice:
return e.compileSliceOp(typ)
case reflect.Struct:
return e.compileStructOp(typ)
case reflect.Int:
return e.compileIntOp(typ)
case reflect.Int8:
return e.compileInt8Op(typ)
case reflect.Int16:
return e.compileInt16Op(typ)
case reflect.Int32:
return e.compileInt32Op(typ)
case reflect.Int64:
return e.compileInt64Op(typ)
case reflect.Uint:
return e.compileUintOp(typ)
case reflect.Uint8:
return e.compileUint8Op(typ)
case reflect.Uint16:
return e.compileUint16Op(typ)
case reflect.Uint32:
return e.compileUint32Op(typ)
case reflect.Uint64:
return e.compileUint64Op(typ)
case reflect.Uintptr:
return e.compileUintOp(typ)
case reflect.Float32:
return e.compileFloat32Op(typ)
case reflect.Float64:
return e.compileFloat64Op(typ)
case reflect.String:
return e.compileStringOp(typ)
case reflect.Bool:
return e.compileBoolOp(typ)
case reflect.Interface:
return nil, errCompileSlowPath
}
return nil, xerrors.Errorf("failed to encode type %s: %w", typ.String(), ErrUnsupportedType)
}
func (e *Encoder) compilePtrOp(typ *rtype) (*opcode, error) {
2020-04-29 19:44:48 +03:00
code, err := e.compileOp(typ.Elem())
2020-04-29 18:31:50 +03:00
if err != nil {
return nil, err
}
2020-04-29 19:44:48 +03:00
switch code.op {
case opStructFieldHead:
code.op = opStructFieldPtrHead
case opStructFieldHeadInt:
code.op = opStructFieldPtrHeadInt
case opStructFieldHeadString:
code.op = opStructFieldPtrHeadString
default:
return newOpCode(opPtr, typ, code), nil
}
return code, nil
2020-04-29 18:31:50 +03:00
}
func (e *Encoder) compileIntOp(typ *rtype) (*opcode, error) {
return newOpCode(opInt, typ, newEndOp()), nil
}
func (e *Encoder) compileInt8Op(typ *rtype) (*opcode, error) {
return newOpCode(opInt8, typ, newEndOp()), nil
}
func (e *Encoder) compileInt16Op(typ *rtype) (*opcode, error) {
return newOpCode(opInt16, typ, newEndOp()), nil
}
func (e *Encoder) compileInt32Op(typ *rtype) (*opcode, error) {
return newOpCode(opInt32, typ, newEndOp()), nil
}
func (e *Encoder) compileInt64Op(typ *rtype) (*opcode, error) {
return newOpCode(opInt64, typ, newEndOp()), nil
}
func (e *Encoder) compileUintOp(typ *rtype) (*opcode, error) {
return newOpCode(opUint, typ, newEndOp()), nil
}
func (e *Encoder) compileUint8Op(typ *rtype) (*opcode, error) {
return newOpCode(opUint8, typ, newEndOp()), nil
}
func (e *Encoder) compileUint16Op(typ *rtype) (*opcode, error) {
return newOpCode(opUint16, typ, newEndOp()), nil
}
func (e *Encoder) compileUint32Op(typ *rtype) (*opcode, error) {
return newOpCode(opUint32, typ, newEndOp()), nil
}
func (e *Encoder) compileUint64Op(typ *rtype) (*opcode, error) {
return newOpCode(opUint64, typ, newEndOp()), nil
}
func (e *Encoder) compileFloat32Op(typ *rtype) (*opcode, error) {
return newOpCode(opFloat32, typ, newEndOp()), nil
}
func (e *Encoder) compileFloat64Op(typ *rtype) (*opcode, error) {
return newOpCode(opFloat64, typ, newEndOp()), nil
}
func (e *Encoder) compileStringOp(typ *rtype) (*opcode, error) {
return newOpCode(opString, typ, newEndOp()), nil
}
func (e *Encoder) compileBoolOp(typ *rtype) (*opcode, error) {
return newOpCode(opBool, typ, newEndOp()), nil
}
func (e *Encoder) compileSliceOp(typ *rtype) (*opcode, error) {
elem := typ.Elem()
size := elem.Size()
code, err := e.compileOp(elem)
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-04-29 19:44:48 +03:00
header := newSliceHeaderCode()
2020-04-29 18:31:50 +03:00
elemCode := &sliceElemCode{opcodeHeader: &opcodeHeader{op: opSliceElem}, size: size}
2020-04-29 19:44:48 +03:00
end := newOpCode(opSliceEnd, nil, nil)
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
2020-04-29 19:44:48 +03:00
end.next = newEndOp()
2020-04-29 18:31:50 +03:00
return (*opcode)(unsafe.Pointer(header)), nil
}
func (e *Encoder) compileStructOp(typ *rtype) (*opcode, error) {
// header => firstField => structField => end
// ^ |
// |________|
fieldNum := typ.NumField()
fieldIdx := 0
2020-04-29 19:44:48 +03:00
var (
head *structFieldCode
code *opcode
prevField *structFieldCode
)
2020-04-29 18:31:50 +03:00
for i := 0; i < fieldNum; i++ {
field := typ.Field(i)
if e.isIgnoredStructField(field) {
continue
}
keyName := field.Name
tag := e.getTag(field)
opts := strings.Split(tag, ",")
if len(opts) > 0 {
if opts[0] != "" {
keyName = opts[0]
}
}
fieldType := type2rtype(field.Type)
valueCode, err := e.compileOp(fieldType)
if err != nil {
return nil, err
}
key := fmt.Sprintf(`"%s":`, keyName)
if fieldIdx == 0 {
fieldCode := &structFieldCode{
opcodeHeader: &opcodeHeader{
2020-04-29 19:44:48 +03:00
op: opStructFieldHead,
2020-04-29 18:31:50 +03:00
typ: fieldType,
next: valueCode,
},
key: key,
offset: field.Offset,
}
2020-04-29 19:44:48 +03:00
head = fieldCode
code = (*opcode)(unsafe.Pointer(fieldCode))
2020-04-29 18:31:50 +03:00
prevField = fieldCode
2020-04-29 19:44:48 +03:00
switch valueCode.op {
case opInt:
fieldCode.op = opStructFieldHeadInt
case opString:
fieldCode.op = opStructFieldHeadString
default:
2020-04-29 18:31:50 +03:00
code = valueCode.beforeLastCode()
}
} else {
fieldCode := &structFieldCode{
opcodeHeader: &opcodeHeader{
op: opStructField,
typ: fieldType,
next: valueCode,
},
key: key,
offset: field.Offset,
}
code.next = (*opcode)(unsafe.Pointer(fieldCode))
prevField.nextField = (*opcode)(unsafe.Pointer(fieldCode))
prevField = fieldCode
2020-04-29 19:44:48 +03:00
switch valueCode.op {
case opInt:
2020-04-29 18:31:50 +03:00
fieldCode.op = opStructFieldInt
code = (*opcode)(unsafe.Pointer(fieldCode))
2020-04-29 19:44:48 +03:00
case opString:
2020-04-29 18:31:50 +03:00
fieldCode.op = opStructFieldString
code = (*opcode)(unsafe.Pointer(fieldCode))
2020-04-29 19:44:48 +03:00
default:
2020-04-29 18:31:50 +03:00
code = valueCode.beforeLastCode()
}
}
2020-04-29 19:44:48 +03:00
prevField.nextField = newEndOp()
2020-04-29 18:31:50 +03:00
fieldIdx++
}
2020-04-29 19:44:48 +03:00
structEndCode := newOpCode(opStructEnd, nil, nil)
head.end = structEndCode
2020-04-29 18:31:50 +03:00
code.next = structEndCode
2020-04-29 19:44:48 +03:00
structEndCode.next = newEndOp()
return (*opcode)(unsafe.Pointer(head)), nil
2020-04-29 18:31:50 +03:00
}