forked from mirror/go-json
Merge pull request #25 from goccy/feature/support-anonymous-struct
Support embedded field
This commit is contained in:
commit
92ed6c6d7e
|
@ -0,0 +1,331 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type opType struct {
|
||||
Op string
|
||||
IndentOp string
|
||||
Code string
|
||||
}
|
||||
|
||||
type headType struct {
|
||||
Head string
|
||||
PtrHead string
|
||||
AnonymousHead string
|
||||
AnonymousPtrHead string
|
||||
OmitEmptyHead string
|
||||
OmitEmptyPtrHead string
|
||||
AnonymousOmitEmptyHead string
|
||||
AnonymousOmitEmptyPtrHead string
|
||||
}
|
||||
|
||||
type fieldType struct {
|
||||
Field string
|
||||
OmitEmptyField string
|
||||
}
|
||||
|
||||
func _main() error {
|
||||
tmpl, err := template.New("").Parse(`// Code generated by cmd/generator. DO NOT EDIT!
|
||||
package json
|
||||
|
||||
type codeType int
|
||||
|
||||
const (
|
||||
{{- range $index, $type := .CodeTypes }}
|
||||
code{{ $type }} codeType = {{ $index }}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
type opType int
|
||||
|
||||
const (
|
||||
{{- range $index, $type := .OpTypes }}
|
||||
op{{ $type.Op }} opType = {{ $index }}
|
||||
{{- end }}
|
||||
)
|
||||
|
||||
func (t opType) String() string {
|
||||
switch t {
|
||||
{{- range $type := .OpTypes }}
|
||||
case op{{ $type.Op }}:
|
||||
return "{{ $type.Op }}"
|
||||
{{- end }}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (t opType) codeType() codeType {
|
||||
switch t {
|
||||
{{- range $type := .OpTypes }}
|
||||
case op{{ $type.Op }}:
|
||||
return code{{ $type.Code }}
|
||||
{{- end }}
|
||||
}
|
||||
return codeOp
|
||||
}
|
||||
|
||||
func (t opType) toIndent() opType {
|
||||
switch t {
|
||||
{{- range $type := .OpTypes }}
|
||||
case op{{ $type.Op }}:
|
||||
return op{{ $type.IndentOp }}
|
||||
{{- end }}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) headToPtrHead() opType {
|
||||
switch t {
|
||||
{{- range $type := .HeadTypes }}
|
||||
case op{{ $type.Head }}:
|
||||
return op{{ $type.PtrHead }}
|
||||
case op{{ $type.AnonymousHead }}:
|
||||
return op{{ $type.AnonymousPtrHead }}
|
||||
case op{{ $type.OmitEmptyHead }}:
|
||||
return op{{ $type.OmitEmptyPtrHead }}
|
||||
case op{{ $type.AnonymousOmitEmptyHead }}:
|
||||
return op{{ $type.AnonymousOmitEmptyPtrHead }}
|
||||
{{- end }}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) headToAnonymousHead() opType {
|
||||
switch t {
|
||||
{{- range $type := .HeadTypes }}
|
||||
case op{{ $type.Head }}:
|
||||
return op{{ $type.AnonymousHead }}
|
||||
case op{{ $type.PtrHead }}:
|
||||
return op{{ $type.AnonymousPtrHead }}
|
||||
case op{{ $type.OmitEmptyHead }}:
|
||||
return op{{ $type.AnonymousOmitEmptyHead }}
|
||||
case op{{ $type.OmitEmptyPtrHead }}:
|
||||
return op{{ $type.AnonymousOmitEmptyPtrHead }}
|
||||
{{- end }}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) headToOmitEmptyHead() opType {
|
||||
switch t {
|
||||
{{- range $type := .HeadTypes }}
|
||||
case op{{ $type.Head }}:
|
||||
return op{{ $type.OmitEmptyHead }}
|
||||
case op{{ $type.PtrHead }}:
|
||||
return op{{ $type.OmitEmptyPtrHead }}
|
||||
{{- end }}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) ptrHeadToHead() opType {
|
||||
switch t {
|
||||
{{- range $type := .HeadTypes }}
|
||||
case op{{ $type.PtrHead }}:
|
||||
return op{{ $type.Head }}
|
||||
case op{{ $type.AnonymousPtrHead }}:
|
||||
return op{{ $type.AnonymousHead }}
|
||||
case op{{ $type.OmitEmptyPtrHead }}:
|
||||
return op{{ $type.OmitEmptyHead }}
|
||||
case op{{ $type.AnonymousOmitEmptyPtrHead }}:
|
||||
return op{{ $type.AnonymousOmitEmptyHead }}
|
||||
{{- end }}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t opType) fieldToOmitEmptyField() opType {
|
||||
switch t {
|
||||
{{- range $type := .FieldTypes }}
|
||||
case op{{ $type.Field }}:
|
||||
return op{{ $type.OmitEmptyField }}
|
||||
{{- end }}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
codeTypes := []string{
|
||||
"Op",
|
||||
"ArrayHead",
|
||||
"ArrayElem",
|
||||
"SliceHead",
|
||||
"SliceElem",
|
||||
"MapHead",
|
||||
"MapKey",
|
||||
"MapValue",
|
||||
"StructFieldRecursive",
|
||||
"StructField",
|
||||
}
|
||||
primitiveTypes := []string{
|
||||
"int", "int8", "int16", "int32", "int64",
|
||||
"uint", "uint8", "uint16", "uint32", "uint64",
|
||||
"float32", "float64", "bool", "string",
|
||||
}
|
||||
primitiveTypesUpper := []string{}
|
||||
for _, typ := range primitiveTypes {
|
||||
primitiveTypesUpper = append(primitiveTypesUpper, strings.ToUpper(string(typ[0]))+typ[1:])
|
||||
}
|
||||
opTypes := []opType{
|
||||
{"End", "EndIndent", "Op"},
|
||||
{"Interface", "InterfaceIndent", "Op"},
|
||||
{"Ptr", "PtrIndent", "Op"},
|
||||
{"MarshalJSON", "MarshalJSONIndent", "Op"},
|
||||
{"MarshalText", "MarshalTextIndent", "Op"},
|
||||
{"SliceHead", "SliceHeadIndent", "SliceHead"},
|
||||
{"RootSliceHead", "RootSliceHeadIndent", "SliceHead"},
|
||||
{"SliceElem", "SliceElemIndent", "SliceElem"},
|
||||
{"RootSliceElem", "RootSliceElemIndent", "SliceElem"},
|
||||
{"SliceEnd", "SliceEndIndent", "Op"},
|
||||
{"ArrayHead", "ArrayHeadIndent", "ArrayHead"},
|
||||
{"ArrayElem", "ArrayElemIndent", "ArrayElem"},
|
||||
{"ArrayEnd", "ArrayEndIndent", "Op"},
|
||||
{"MapHead", "MapHeadIndent", "MapHead"},
|
||||
{"MapHeadLoad", "MapHeadLoadIndent", "MapHead"},
|
||||
{"RootMapHead", "RootMapHeadIndent", "MapHead"},
|
||||
{"MapKey", "MapKeyIndent", "MapKey"},
|
||||
{"RootMapKey", "RootMapKeyIndent", "MapKey"},
|
||||
{"MapValue", "MapValueIndent", "MapValue"},
|
||||
{"MapEnd", "MapEndIndent", "Op"},
|
||||
{"StructFieldHead", "StructFieldHeadIndent", "StructField"},
|
||||
{"StructFieldHeadOmitEmpty", "StructFieldHeadOmitEmptyIndent", "StructField"},
|
||||
{"StructFieldAnonymousHead", "StructFieldAnonymousHeadIndent", "StructField"},
|
||||
{"StructFieldAnonymousHeadOmitEmpty", "StructFieldAnonymousHeadOmitEmptyIndent", "StructField"},
|
||||
{"StructFieldPtrAnonymousHeadOmitEmpty", "StructFieldPtrAnonymousHeadOmitEmptyIndent", "StructField"},
|
||||
{"StructFieldPtrHead", "StructFieldPtrHeadIndent", "StructField"},
|
||||
{"StructFieldPtrHeadOmitEmpty", "StructFieldPtrHeadOmitEmptyIndent", "StructField"},
|
||||
{"StructFieldPtrAnonymousHead", "StructFieldPtrAnonymousHeadIndent", "StructField"},
|
||||
{"StructField", "StructFieldIndent", "StructField"},
|
||||
{"StructFieldOmitEmpty", "StructFieldOmitEmptyIndent", "StructField"},
|
||||
{"StructFieldRecursive", "StructFieldRecursiveIndent", "StructFieldRecursive"},
|
||||
{"StructEnd", "StructEndIndent", "StructField"},
|
||||
{"StructAnonymousEnd", "StructAnonymousEndIndent", "StructField"},
|
||||
}
|
||||
for _, typ := range primitiveTypesUpper {
|
||||
opTypes = append(opTypes, opType{
|
||||
Op: typ,
|
||||
IndentOp: fmt.Sprintf("%sIndent", typ),
|
||||
Code: "Op",
|
||||
})
|
||||
}
|
||||
for _, prefix := range []string{
|
||||
"StructFieldHead",
|
||||
"StructFieldHeadOmitEmpty",
|
||||
"StructFieldAnonymousHead",
|
||||
"StructFieldAnonymousHeadOmitEmpty",
|
||||
"StructFieldPtrHead",
|
||||
"StructFieldPtrHeadOmitEmpty",
|
||||
"StructFieldPtrAnonymousHead",
|
||||
"StructFieldPtrAnonymousHeadOmitEmpty",
|
||||
"StructField",
|
||||
"StructFieldOmitEmpty",
|
||||
} {
|
||||
for _, typ := range primitiveTypesUpper {
|
||||
opTypes = append(opTypes, opType{
|
||||
Op: fmt.Sprintf("%s%s", prefix, typ),
|
||||
IndentOp: fmt.Sprintf("%s%sIndent", prefix, typ),
|
||||
Code: "StructField",
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, typ := range opTypes {
|
||||
opTypes = append(opTypes, opType{
|
||||
Op: typ.IndentOp,
|
||||
IndentOp: typ.IndentOp,
|
||||
Code: typ.Code,
|
||||
})
|
||||
}
|
||||
base := headType{
|
||||
Head: "StructFieldHead",
|
||||
PtrHead: "StructFieldPtrHead",
|
||||
AnonymousHead: "StructFieldAnonymousHead",
|
||||
AnonymousPtrHead: "StructFieldPtrAnonymousHead",
|
||||
OmitEmptyHead: "StructFieldHeadOmitEmpty",
|
||||
OmitEmptyPtrHead: "StructFieldPtrHeadOmitEmpty",
|
||||
AnonymousOmitEmptyHead: "StructFieldAnonymousHeadOmitEmpty",
|
||||
AnonymousOmitEmptyPtrHead: "StructFieldPtrAnonymousHeadOmitEmpty",
|
||||
}
|
||||
headTypes := []headType{base}
|
||||
for _, prim := range primitiveTypesUpper {
|
||||
headTypes = append(headTypes, headType{
|
||||
Head: fmt.Sprintf("%s%s", base.Head, prim),
|
||||
PtrHead: fmt.Sprintf("%s%s", base.PtrHead, prim),
|
||||
AnonymousHead: fmt.Sprintf("%s%s", base.AnonymousHead, prim),
|
||||
AnonymousPtrHead: fmt.Sprintf("%s%s", base.AnonymousPtrHead, prim),
|
||||
OmitEmptyHead: fmt.Sprintf("%s%s", base.OmitEmptyHead, prim),
|
||||
OmitEmptyPtrHead: fmt.Sprintf("%s%s", base.OmitEmptyPtrHead, prim),
|
||||
AnonymousOmitEmptyHead: fmt.Sprintf("%s%s", base.AnonymousOmitEmptyHead, prim),
|
||||
AnonymousOmitEmptyPtrHead: fmt.Sprintf("%s%s", base.AnonymousOmitEmptyPtrHead, prim),
|
||||
})
|
||||
}
|
||||
for _, typ := range headTypes {
|
||||
headTypes = append(headTypes, headType{
|
||||
Head: fmt.Sprintf("%sIndent", typ.Head),
|
||||
PtrHead: fmt.Sprintf("%sIndent", typ.PtrHead),
|
||||
AnonymousHead: fmt.Sprintf("%sIndent", typ.AnonymousHead),
|
||||
AnonymousPtrHead: fmt.Sprintf("%sIndent", typ.AnonymousPtrHead),
|
||||
OmitEmptyHead: fmt.Sprintf("%sIndent", typ.OmitEmptyHead),
|
||||
OmitEmptyPtrHead: fmt.Sprintf("%sIndent", typ.OmitEmptyPtrHead),
|
||||
AnonymousOmitEmptyHead: fmt.Sprintf("%sIndent", typ.AnonymousOmitEmptyHead),
|
||||
AnonymousOmitEmptyPtrHead: fmt.Sprintf("%sIndent", typ.AnonymousOmitEmptyPtrHead),
|
||||
})
|
||||
}
|
||||
|
||||
baseField := fieldType{
|
||||
Field: "StructField",
|
||||
OmitEmptyField: "StructFieldOmitEmpty",
|
||||
}
|
||||
fieldTypes := []fieldType{baseField}
|
||||
for _, prim := range primitiveTypesUpper {
|
||||
fieldTypes = append(fieldTypes, fieldType{
|
||||
Field: fmt.Sprintf("%s%s", baseField.Field, prim),
|
||||
OmitEmptyField: fmt.Sprintf("%s%s", baseField.OmitEmptyField, prim),
|
||||
})
|
||||
}
|
||||
for _, typ := range fieldTypes {
|
||||
fieldTypes = append(fieldTypes, fieldType{
|
||||
Field: fmt.Sprintf("%sIndent", typ.Field),
|
||||
OmitEmptyField: fmt.Sprintf("%sIndent", typ.OmitEmptyField),
|
||||
})
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
if err := tmpl.Execute(&b, struct {
|
||||
CodeTypes []string
|
||||
OpTypes []opType
|
||||
HeadTypes []headType
|
||||
FieldTypes []fieldType
|
||||
}{
|
||||
CodeTypes: codeTypes,
|
||||
OpTypes: opTypes,
|
||||
HeadTypes: headTypes,
|
||||
FieldTypes: fieldTypes,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
path := filepath.Join(repoRoot(), "encode_optype.go")
|
||||
return ioutil.WriteFile(path, b.Bytes(), 0644)
|
||||
}
|
||||
|
||||
func repoRoot() string {
|
||||
_, file, _, _ := runtime.Caller(0)
|
||||
relativePathFromRepoRoot := filepath.Join("cmd", "generator")
|
||||
return strings.TrimSuffix(filepath.Dir(file), relativePathFromRepoRoot)
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := _main(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -77,133 +77,13 @@ func (e *Encoder) compile(typ *rtype, root, withIndent bool) (*opcode, error) {
|
|||
}
|
||||
|
||||
func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode {
|
||||
switch code.op {
|
||||
case opStructFieldHead:
|
||||
code.op = opStructFieldPtrHead
|
||||
case opStructFieldHeadInt:
|
||||
code.op = opStructFieldPtrHeadInt
|
||||
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
|
||||
case opStructFieldHeadString:
|
||||
code.op = opStructFieldPtrHeadString
|
||||
case opStructFieldHeadBool:
|
||||
code.op = opStructFieldPtrHeadBool
|
||||
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
|
||||
|
||||
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
|
||||
default:
|
||||
return newOpCode(opPtr, typ, e.indent, code)
|
||||
}
|
||||
ptrHeadOp := code.op.headToPtrHead()
|
||||
if code.op != ptrHeadOp {
|
||||
code.op = ptrHeadOp
|
||||
return code
|
||||
}
|
||||
return newOpCode(opPtr, typ, e.indent, code)
|
||||
}
|
||||
|
||||
func (e *Encoder) compilePtr(typ *rtype, root, withIndent bool) (*opcode, error) {
|
||||
code, err := e.compile(typ.Elem(), root, withIndent)
|
||||
|
@ -456,118 +336,7 @@ func (e *Encoder) isIgnoredStructField(field reflect.StructField) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
func (e *Encoder) typeToHeaderType(op opType) opType {
|
||||
switch op {
|
||||
case opInt:
|
||||
return opStructFieldHeadInt
|
||||
|
@ -601,118 +370,7 @@ func (e *Encoder) optimizeStructHeader(op opType, isOmitEmpty, withIndent bool)
|
|||
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)
|
||||
}
|
||||
func (e *Encoder) typeToFieldType(op opType) opType {
|
||||
switch op {
|
||||
case opInt:
|
||||
return opStructFieldInt
|
||||
|
@ -746,33 +404,102 @@ func (e *Encoder) optimizeStructField(op opType, isOmitEmpty, withIndent bool) o
|
|||
return opStructField
|
||||
}
|
||||
|
||||
func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, error) {
|
||||
func (e *Encoder) optimizeStructHeader(op opType, isOmitEmpty, withIndent bool) opType {
|
||||
headType := e.typeToHeaderType(op)
|
||||
if isOmitEmpty {
|
||||
headType = headType.headToOmitEmptyHead()
|
||||
}
|
||||
if withIndent {
|
||||
return headType.toIndent()
|
||||
}
|
||||
return headType
|
||||
}
|
||||
|
||||
func (e *Encoder) optimizeStructField(op opType, isOmitEmpty, withIndent bool) opType {
|
||||
fieldType := e.typeToFieldType(op)
|
||||
if isOmitEmpty {
|
||||
fieldType = fieldType.fieldToOmitEmptyField()
|
||||
}
|
||||
if withIndent {
|
||||
return fieldType.toIndent()
|
||||
}
|
||||
return fieldType
|
||||
}
|
||||
|
||||
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 {
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
if withIndent {
|
||||
if compiled, exists := e.structTypeToCompiledIndentCode[typeptr]; exists {
|
||||
return (*opcode)(unsafe.Pointer(&recursiveCode{
|
||||
opcodeHeader: &opcodeHeader{
|
||||
op: opStructFieldRecursive,
|
||||
typ: typ,
|
||||
indent: e.indent,
|
||||
next: newEndOp(e.indent),
|
||||
},
|
||||
jmp: compiled,
|
||||
})), nil
|
||||
if compiledCode, exists := e.structTypeToCompiledIndentCode[typeptr]; exists {
|
||||
return e.recursiveCode(typ, compiledCode)
|
||||
}
|
||||
} else {
|
||||
if compiled, exists := e.structTypeToCompiledCode[typeptr]; exists {
|
||||
return (*opcode)(unsafe.Pointer(&recursiveCode{
|
||||
opcodeHeader: &opcodeHeader{
|
||||
op: opStructFieldRecursive,
|
||||
typ: typ,
|
||||
indent: e.indent,
|
||||
next: newEndOp(e.indent),
|
||||
},
|
||||
jmp: compiled,
|
||||
})), nil
|
||||
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]
|
||||
}
|
||||
}
|
||||
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))
|
||||
compiled := &compiledCode{}
|
||||
if withIndent {
|
||||
e.structTypeToCompiledIndentCode[typeptr] = compiled
|
||||
|
@ -795,23 +522,25 @@ func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, err
|
|||
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]
|
||||
}
|
||||
}
|
||||
isOmitEmpty := false
|
||||
if len(opts) > 1 {
|
||||
isOmitEmpty = opts[1] == "omitempty"
|
||||
}
|
||||
keyName, isOmitEmpty := e.keyNameAndOmitEmptyFromField(field)
|
||||
fieldType := type2rtype(field.Type)
|
||||
valueCode, err := e.compile(fieldType, false, withIndent)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if field.Anonymous {
|
||||
f := valueCode.toStructFieldCode()
|
||||
for {
|
||||
f.op = f.op.headToAnonymousHead()
|
||||
if f.op == opStructEnd {
|
||||
f.op = opStructAnonymousEnd
|
||||
}
|
||||
if f.nextField == nil {
|
||||
break
|
||||
}
|
||||
f = f.nextField.toStructFieldCode()
|
||||
}
|
||||
}
|
||||
key := fmt.Sprintf(`"%s":`, keyName)
|
||||
fieldCode := &structFieldCode{
|
||||
opcodeHeader: &opcodeHeader{
|
||||
|
@ -819,44 +548,33 @@ func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, err
|
|||
next: valueCode,
|
||||
indent: e.indent,
|
||||
},
|
||||
anonymousKey: field.Anonymous,
|
||||
key: []byte(key),
|
||||
offset: field.Offset,
|
||||
}
|
||||
if fieldIdx == 0 {
|
||||
fieldCode.indent--
|
||||
code = e.structHeader(fieldCode, valueCode, isOmitEmpty, withIndent)
|
||||
head = fieldCode
|
||||
code = (*opcode)(unsafe.Pointer(fieldCode))
|
||||
prevField = fieldCode
|
||||
op := e.optimizeStructHeader(valueCode.op, isOmitEmpty, withIndent)
|
||||
fieldCode.op = op
|
||||
switch op {
|
||||
case opStructFieldHead,
|
||||
opStructFieldHeadOmitEmpty,
|
||||
opStructFieldHeadIndent,
|
||||
opStructFieldHeadOmitEmptyIndent:
|
||||
code = valueCode.beforeLastCode()
|
||||
}
|
||||
} else {
|
||||
code.next = (*opcode)(unsafe.Pointer(fieldCode))
|
||||
prevField.nextField = (*opcode)(unsafe.Pointer(fieldCode))
|
||||
fcode := (*opcode)(unsafe.Pointer(fieldCode))
|
||||
code.next = fcode
|
||||
code = e.structField(fieldCode, valueCode, isOmitEmpty, withIndent)
|
||||
prevField.nextField = fcode
|
||||
prevField = fieldCode
|
||||
code = (*opcode)(unsafe.Pointer(fieldCode))
|
||||
op := e.optimizeStructField(valueCode.op, isOmitEmpty, withIndent)
|
||||
fieldCode.op = op
|
||||
switch op {
|
||||
case opStructField,
|
||||
opStructFieldOmitEmpty,
|
||||
opStructFieldIndent,
|
||||
opStructFieldOmitEmptyIndent:
|
||||
code = valueCode.beforeLastCode()
|
||||
}
|
||||
}
|
||||
fieldIdx++
|
||||
}
|
||||
e.indent--
|
||||
|
||||
structEndCode := newOpCode(opStructEnd, nil, e.indent, nil)
|
||||
|
||||
structEndCode := (*opcode)(unsafe.Pointer(&structFieldCode{
|
||||
opcodeHeader: &opcodeHeader{
|
||||
op: opStructEnd,
|
||||
typ: nil,
|
||||
indent: e.indent,
|
||||
},
|
||||
}))
|
||||
structEndCode.next = newEndOp(e.indent)
|
||||
if withIndent {
|
||||
structEndCode.op = opStructEndIndent
|
||||
}
|
||||
|
@ -882,7 +600,6 @@ func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, err
|
|||
}
|
||||
head.end = structEndCode
|
||||
code.next = structEndCode
|
||||
structEndCode.next = newEndOp(e.indent)
|
||||
ret := (*opcode)(unsafe.Pointer(head))
|
||||
compiled.code = ret
|
||||
return ret, nil
|
||||
|
|
1064
encode_opcode.go
1064
encode_opcode.go
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -132,6 +132,56 @@ func Test_Marshal(t *testing.T) {
|
|||
assertErr(t, err)
|
||||
assertEq(t, "recursive", `{"a":{"b":{"t":{"d":"hello"}},"c":{"t":{"d":"world"}}}}`, string(bytes))
|
||||
})
|
||||
t.Run("embedded", func(t *testing.T) {
|
||||
type T struct {
|
||||
A string `json:"a"`
|
||||
}
|
||||
type U struct {
|
||||
*T
|
||||
B string `json:"b"`
|
||||
}
|
||||
type T2 struct {
|
||||
A string `json:"a,omitempty"`
|
||||
}
|
||||
type U2 struct {
|
||||
*T2
|
||||
B string `json:"b,omitempty"`
|
||||
}
|
||||
t.Run("exists field", func(t *testing.T) {
|
||||
bytes, err := json.Marshal(&U{
|
||||
T: &T{
|
||||
A: "aaa",
|
||||
},
|
||||
B: "bbb",
|
||||
})
|
||||
assertErr(t, err)
|
||||
assertEq(t, "embedded", `{"a":"aaa","b":"bbb"}`, string(bytes))
|
||||
t.Run("omitempty", func(t *testing.T) {
|
||||
bytes, err := json.Marshal(&U2{
|
||||
T2: &T2{
|
||||
A: "aaa",
|
||||
},
|
||||
B: "bbb",
|
||||
})
|
||||
assertErr(t, err)
|
||||
assertEq(t, "embedded", `{"a":"aaa","b":"bbb"}`, string(bytes))
|
||||
})
|
||||
})
|
||||
t.Run("none field", func(t *testing.T) {
|
||||
bytes, err := json.Marshal(&U{
|
||||
B: "bbb",
|
||||
})
|
||||
assertErr(t, err)
|
||||
assertEq(t, "embedded", `{"b":"bbb"}`, string(bytes))
|
||||
t.Run("omitempty", func(t *testing.T) {
|
||||
bytes, err := json.Marshal(&U2{
|
||||
B: "bbb",
|
||||
})
|
||||
assertErr(t, err)
|
||||
assertEq(t, "embedded", `{"b":"bbb"}`, string(bytes))
|
||||
})
|
||||
})
|
||||
})
|
||||
t.Run("omitempty", func(t *testing.T) {
|
||||
type T struct {
|
||||
A int `json:",omitempty"`
|
||||
|
|
866
encode_vm.go
866
encode_vm.go
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue