Merge pull request #25 from goccy/feature/support-anonymous-struct

Support embedded field
This commit is contained in:
Masaaki Goshima 2020-08-15 18:40:52 +09:00 committed by GitHub
commit 92ed6c6d7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 4798 additions and 1653 deletions

331
cmd/generator/main.go Normal file
View File

@ -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)
}
}

View File

@ -77,132 +77,12 @@ func (e *Encoder) compile(typ *rtype, root, withIndent bool) (*opcode, error) {
} }
func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode { func (e *Encoder) optimizeStructFieldPtrHead(typ *rtype, code *opcode) *opcode {
switch code.op { ptrHeadOp := code.op.headToPtrHead()
case opStructFieldHead: if code.op != ptrHeadOp {
code.op = opStructFieldPtrHead code.op = ptrHeadOp
case opStructFieldHeadInt: return code
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)
} }
return code return newOpCode(opPtr, typ, e.indent, code)
} }
func (e *Encoder) compilePtr(typ *rtype, root, withIndent bool) (*opcode, error) { func (e *Encoder) compilePtr(typ *rtype, root, withIndent bool) (*opcode, error) {
@ -456,118 +336,7 @@ func (e *Encoder) isIgnoredStructField(field reflect.StructField) bool {
return false return false
} }
func (e *Encoder) optimizeStructHeaderOmitEmptyIndent(op opType) opType { func (e *Encoder) typeToHeaderType(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 { switch op {
case opInt: case opInt:
return opStructFieldHeadInt return opStructFieldHeadInt
@ -601,118 +370,7 @@ func (e *Encoder) optimizeStructHeader(op opType, isOmitEmpty, withIndent bool)
return opStructFieldHead return opStructFieldHead
} }
func (e *Encoder) optimizeStructFieldOmitEmptyIndent(op opType) opType { func (e *Encoder) typeToFieldType(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 { switch op {
case opInt: case opInt:
return opStructFieldInt return opStructFieldInt
@ -746,33 +404,102 @@ func (e *Encoder) optimizeStructField(op opType, isOmitEmpty, withIndent bool) o
return opStructField 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)) typeptr := uintptr(unsafe.Pointer(typ))
if withIndent { if withIndent {
if compiled, exists := e.structTypeToCompiledIndentCode[typeptr]; exists { if compiledCode, exists := e.structTypeToCompiledIndentCode[typeptr]; exists {
return (*opcode)(unsafe.Pointer(&recursiveCode{ return e.recursiveCode(typ, compiledCode)
opcodeHeader: &opcodeHeader{
op: opStructFieldRecursive,
typ: typ,
indent: e.indent,
next: newEndOp(e.indent),
},
jmp: compiled,
})), nil
} }
} else { } else {
if compiled, exists := e.structTypeToCompiledCode[typeptr]; exists { if compiledCode, exists := e.structTypeToCompiledCode[typeptr]; exists {
return (*opcode)(unsafe.Pointer(&recursiveCode{ return e.recursiveCode(typ, compiledCode)
opcodeHeader: &opcodeHeader{
op: opStructFieldRecursive,
typ: typ,
indent: e.indent,
next: newEndOp(e.indent),
},
jmp: compiled,
})), nil
} }
} }
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{} compiled := &compiledCode{}
if withIndent { if withIndent {
e.structTypeToCompiledIndentCode[typeptr] = compiled e.structTypeToCompiledIndentCode[typeptr] = compiled
@ -795,23 +522,25 @@ func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, err
if e.isIgnoredStructField(field) { if e.isIgnoredStructField(field) {
continue continue
} }
keyName := field.Name keyName, isOmitEmpty := e.keyNameAndOmitEmptyFromField(field)
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"
}
fieldType := type2rtype(field.Type) fieldType := type2rtype(field.Type)
valueCode, err := e.compile(fieldType, false, withIndent) valueCode, err := e.compile(fieldType, false, withIndent)
if err != nil { if err != nil {
return nil, err 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) key := fmt.Sprintf(`"%s":`, keyName)
fieldCode := &structFieldCode{ fieldCode := &structFieldCode{
opcodeHeader: &opcodeHeader{ opcodeHeader: &opcodeHeader{
@ -819,44 +548,33 @@ func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, err
next: valueCode, next: valueCode,
indent: e.indent, indent: e.indent,
}, },
key: []byte(key), anonymousKey: field.Anonymous,
offset: field.Offset, key: []byte(key),
offset: field.Offset,
} }
if fieldIdx == 0 { if fieldIdx == 0 {
fieldCode.indent-- code = e.structHeader(fieldCode, valueCode, isOmitEmpty, withIndent)
head = fieldCode head = fieldCode
code = (*opcode)(unsafe.Pointer(fieldCode))
prevField = fieldCode prevField = fieldCode
op := e.optimizeStructHeader(valueCode.op, isOmitEmpty, withIndent)
fieldCode.op = op
switch op {
case opStructFieldHead,
opStructFieldHeadOmitEmpty,
opStructFieldHeadIndent,
opStructFieldHeadOmitEmptyIndent:
code = valueCode.beforeLastCode()
}
} else { } else {
code.next = (*opcode)(unsafe.Pointer(fieldCode)) fcode := (*opcode)(unsafe.Pointer(fieldCode))
prevField.nextField = (*opcode)(unsafe.Pointer(fieldCode)) code.next = fcode
code = e.structField(fieldCode, valueCode, isOmitEmpty, withIndent)
prevField.nextField = fcode
prevField = fieldCode 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++ fieldIdx++
} }
e.indent-- 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 { if withIndent {
structEndCode.op = opStructEndIndent structEndCode.op = opStructEndIndent
} }
@ -882,7 +600,6 @@ func (e *Encoder) compileStruct(typ *rtype, root, withIndent bool) (*opcode, err
} }
head.end = structEndCode head.end = structEndCode
code.next = structEndCode code.next = structEndCode
structEndCode.next = newEndOp(e.indent)
ret := (*opcode)(unsafe.Pointer(head)) ret := (*opcode)(unsafe.Pointer(head))
compiled.code = ret compiled.code = ret
return ret, nil return ret, nil

File diff suppressed because it is too large Load Diff

3589
encode_optype.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -132,6 +132,56 @@ func Test_Marshal(t *testing.T) {
assertErr(t, err) assertErr(t, err)
assertEq(t, "recursive", `{"a":{"b":{"t":{"d":"hello"}},"c":{"t":{"d":"world"}}}}`, string(bytes)) 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) { t.Run("omitempty", func(t *testing.T) {
type T struct { type T struct {
A int `json:",omitempty"` A int `json:",omitempty"`

File diff suppressed because it is too large Load Diff