Merge pull request #81 from goccy/feature/coverage

Add many test codes and distribute `case` operation ( of `switch` ) to some switch statements
This commit is contained in:
Masaaki Goshima 2021-01-11 19:36:30 +09:00 committed by GitHub
commit af9168203a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 37138 additions and 52122 deletions

View File

@ -5,7 +5,7 @@ jobs:
name: Test name: Test
strategy: strategy:
matrix: matrix:
os: [ "ubuntu-latest", "windows-latest", "macos-latest" ] os: [ "ubuntu-latest", "macos-latest", "windows-latest" ]
go-version: [ "1.13", "1.14", "1.15" ] go-version: [ "1.13", "1.14", "1.15" ]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:

View File

@ -14,23 +14,18 @@ import (
type opType struct { type opType struct {
Op string Op string
Code string Code string
Indent func() string
Escaped func() string
HeadToPtrHead func() string HeadToPtrHead func() string
HeadToNPtrHead func() string HeadToNPtrHead func() string
HeadToAnonymousHead func() string HeadToAnonymousHead func() string
HeadToOmitEmptyHead func() string HeadToOmitEmptyHead func() string
HeadToStringTagHead func() string HeadToStringTagHead func() string
HeadToOnlyHead func() string
PtrHeadToHead func() string PtrHeadToHead func() string
FieldToEnd func() string FieldToEnd func() string
FieldToOmitEmptyField func() string FieldToOmitEmptyField func() string
FieldToStringTagField func() string FieldToStringTagField func() string
} }
func (t opType) IsEscaped() bool {
return t.Op != t.Escaped()
}
func (t opType) IsHeadToPtrHead() bool { func (t opType) IsHeadToPtrHead() bool {
return t.Op != t.HeadToPtrHead() return t.Op != t.HeadToPtrHead()
} }
@ -55,6 +50,10 @@ func (t opType) IsPtrHeadToHead() bool {
return t.Op != t.PtrHeadToHead() return t.Op != t.PtrHeadToHead()
} }
func (t opType) IsHeadToOnlyHead() bool {
return t.Op != t.HeadToOnlyHead()
}
func (t opType) IsFieldToEnd() bool { func (t opType) IsFieldToEnd() bool {
return t.Op != t.FieldToEnd() return t.Op != t.FieldToEnd()
} }
@ -71,13 +70,12 @@ func createOpType(op, code string) opType {
return opType{ return opType{
Op: op, Op: op,
Code: code, Code: code,
Indent: func() string { return fmt.Sprintf("%sIndent", op) },
Escaped: func() string { return op },
HeadToPtrHead: func() string { return op }, HeadToPtrHead: func() string { return op },
HeadToNPtrHead: func() string { return op }, HeadToNPtrHead: func() string { return op },
HeadToAnonymousHead: func() string { return op }, HeadToAnonymousHead: func() string { return op },
HeadToOmitEmptyHead: func() string { return op }, HeadToOmitEmptyHead: func() string { return op },
HeadToStringTagHead: func() string { return op }, HeadToStringTagHead: func() string { return op },
HeadToOnlyHead: func() string { return op },
PtrHeadToHead: func() string { return op }, PtrHeadToHead: func() string { return op },
FieldToEnd: func() string { return op }, FieldToEnd: func() string { return op },
FieldToOmitEmptyField: func() string { return op }, FieldToOmitEmptyField: func() string { return op },
@ -89,6 +87,10 @@ func _main() error {
tmpl, err := template.New("").Parse(`// Code generated by cmd/generator. DO NOT EDIT! tmpl, err := template.New("").Parse(`// Code generated by cmd/generator. DO NOT EDIT!
package json package json
import (
"strings"
)
type codeType int type codeType int
const ( const (
@ -106,12 +108,8 @@ const (
) )
func (t opType) String() string { func (t opType) String() string {
if int(t) >= {{ .OpLen }} {
return t.toNotIndent().String() + "Indent"
}
switch t { switch t {
{{- range $type := .OpNotIndentTypes }} {{- range $type := .OpTypes }}
case op{{ $type.Op }}: case op{{ $type.Op }}:
return "{{ $type.Op }}" return "{{ $type.Op }}"
{{- end }} {{- end }}
@ -120,152 +118,136 @@ func (t opType) String() string {
} }
func (t opType) codeType() codeType { func (t opType) codeType() codeType {
if int(t) >= {{ .OpLen }} { if strings.Contains(t.String(), "Struct") {
return t.toNotIndent().codeType() if strings.Contains(t.String(), "End") {
return codeStructEnd
}
return codeStructField
}
if strings.Contains(t.String(), "ArrayHead") {
return codeArrayHead
}
if strings.Contains(t.String(), "ArrayElem") {
return codeArrayElem
}
if strings.Contains(t.String(), "SliceHead") {
return codeSliceHead
}
if strings.Contains(t.String(), "SliceElem") {
return codeSliceElem
}
if strings.Contains(t.String(), "MapHead") {
return codeMapHead
}
if strings.Contains(t.String(), "MapKey") {
return codeMapKey
}
if strings.Contains(t.String(), "MapValue") {
return codeMapValue
}
if strings.Contains(t.String(), "MapEnd") {
return codeMapEnd
} }
switch t {
{{- range $type := .OpNotIndentTypes }}
case op{{ $type.Op }}:
return code{{ $type.Code }}
{{- end }}
}
return codeOp return codeOp
} }
func (t opType) toNotIndent() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t) - {{ .OpLen }})
}
return t
}
func (t opType) toIndent() opType {
if int(t) >= {{ .OpLen }} {
return t
}
return opType(int(t) + {{ .OpLen }})
}
func (t opType) toEscaped() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t.toNotIndent().toEscaped()) + {{ .OpLen }})
}
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsEscaped }}
case op{{ $type.Op }}:
return op{{ call $type.Escaped }}
{{- end }}
{{- end }}
}
return t
}
func (t opType) headToPtrHead() opType { func (t opType) headToPtrHead() opType {
if int(t) >= {{ .OpLen }} { if strings.Index(t.String(), "PtrHead") > 0 {
return opType(int(t.toNotIndent().headToPtrHead()) + {{ .OpLen }}) return t
}
if strings.Index(t.String(), "PtrAnonymousHead") > 0 {
return t
} }
switch t { idx := strings.Index(t.String(), "Field")
{{- range $type := .OpNotIndentTypes }} if idx == -1 {
{{- if $type.IsHeadToPtrHead }} return t
case op{{ $type.Op }}: }
return op{{ call $type.HeadToPtrHead }} suffix := "Ptr"+t.String()[idx+len("Field"):]
{{- end }}
{{- end }} const toPtrOffset = 12
if strings.Contains(opType(int(t) + toPtrOffset).String(), suffix) {
return opType(int(t) + toPtrOffset)
} }
return t return t
} }
func (t opType) headToNPtrHead() opType { func (t opType) headToNPtrHead() opType {
if int(t) >= {{ .OpLen }} { if strings.Index(t.String(), "PtrHead") > 0 {
return opType(int(t.toNotIndent().headToNPtrHead()) + {{ .OpLen }}) return t
}
if strings.Index(t.String(), "PtrAnonymousHead") > 0 {
return t
} }
switch t { idx := strings.Index(t.String(), "Field")
{{- range $type := .OpNotIndentTypes }} if idx == -1 {
{{- if $type.IsHeadToNPtrHead }} return t
case op{{ $type.Op }}: }
return op{{ call $type.HeadToNPtrHead }} suffix := "NPtr"+t.String()[idx+len("Field"):]
{{- end }}
{{- end }} const toPtrOffset = 24
if strings.Contains(opType(int(t) + toPtrOffset).String(), suffix) {
return opType(int(t) + toPtrOffset)
} }
return t return t
} }
func (t opType) headToAnonymousHead() opType { func (t opType) headToAnonymousHead() opType {
if int(t) >= {{ .OpLen }} { const toAnonymousOffset = 6
return opType(int(t.toNotIndent().headToAnonymousHead()) + {{ .OpLen }}) if strings.Contains(opType(int(t) + toAnonymousOffset).String(), "Anonymous") {
} return opType(int(t) + toAnonymousOffset)
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsHeadToAnonymousHead }}
case op{{ $type.Op }}:
return op{{ call $type.HeadToAnonymousHead }}
{{- end }}
{{- end }}
} }
return t return t
} }
func (t opType) headToOmitEmptyHead() opType { func (t opType) headToOmitEmptyHead() opType {
if int(t) >= {{ .OpLen }} { const toOmitEmptyOffset = 2
return opType(int(t.toNotIndent().headToOmitEmptyHead()) + {{ .OpLen }}) if strings.Contains(opType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
return opType(int(t) + toOmitEmptyOffset)
} }
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsHeadToOmitEmptyHead }}
case op{{ $type.Op }}:
return op{{ call $type.HeadToOmitEmptyHead }}
{{- end }}
{{- end }}
}
return t return t
} }
func (t opType) headToStringTagHead() opType { func (t opType) headToStringTagHead() opType {
if int(t) >= {{ .OpLen }} { const toStringTagOffset = 4
return opType(int(t.toNotIndent().headToStringTagHead()) + {{ .OpLen }}) if strings.Contains(opType(int(t) + toStringTagOffset).String(), "StringTag") {
return opType(int(t) + toStringTagOffset)
}
return t
}
func (t opType) headToOnlyHead() opType {
if strings.HasSuffix(t.String(), "Head") || strings.HasSuffix(t.String(), "HeadOmitEmpty") || strings.HasSuffix(t.String(), "HeadStringTag") {
return t
} }
switch t { const toOnlyOffset = 1
{{- range $type := .OpNotIndentTypes }} if opType(int(t) + toOnlyOffset).String() == t.String() + "Only" {
{{- if $type.IsHeadToStringTagHead }} return opType(int(t) + toOnlyOffset)
case op{{ $type.Op }}:
return op{{ call $type.HeadToStringTagHead }}
{{- end }}
{{- end }}
} }
return t return t
} }
func (t opType) ptrHeadToHead() opType { func (t opType) ptrHeadToHead() opType {
if int(t) >= {{ .OpLen }} { idx := strings.Index(t.String(), "Ptr")
return opType(int(t.toNotIndent().ptrHeadToHead()) + {{ .OpLen }}) if idx == -1 {
return t
} }
suffix := t.String()[idx+len("Ptr"):]
switch t { const toPtrOffset = 12
{{- range $type := .OpNotIndentTypes }} if strings.Contains(opType(int(t) - toPtrOffset).String(), suffix) {
{{- if $type.IsPtrHeadToHead }} return opType(int(t) - toPtrOffset)
case op{{ $type.Op }}:
return op{{ call $type.PtrHeadToHead }}
{{- end }}
{{- end }}
} }
return t return t
} }
func (t opType) fieldToEnd() opType { func (t opType) fieldToEnd() opType {
if int(t) >= {{ .OpLen }} {
return opType(int(t.toNotIndent().fieldToEnd()) + {{ .OpLen }})
}
switch t { switch t {
{{- range $type := .OpNotIndentTypes }} {{- range $type := .OpTypes }}
{{- if $type.IsFieldToEnd }} {{- if $type.IsFieldToEnd }}
case op{{ $type.Op }}: case op{{ $type.Op }}:
return op{{ call $type.FieldToEnd }} return op{{ call $type.FieldToEnd }}
@ -276,33 +258,17 @@ func (t opType) fieldToEnd() opType {
} }
func (t opType) fieldToOmitEmptyField() opType { func (t opType) fieldToOmitEmptyField() opType {
if int(t) >= {{ .OpLen }} { const toOmitEmptyOffset = 1
return opType(int(t.toNotIndent().fieldToOmitEmptyField()) + {{ .OpLen }}) if strings.Contains(opType(int(t) + toOmitEmptyOffset).String(), "OmitEmpty") {
} return opType(int(t) + toOmitEmptyOffset)
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsFieldToOmitEmptyField }}
case op{{ $type.Op }}:
return op{{ call $type.FieldToOmitEmptyField }}
{{- end }}
{{- end }}
} }
return t return t
} }
func (t opType) fieldToStringTagField() opType { func (t opType) fieldToStringTagField() opType {
if int(t) >= {{ .OpLen }} { const toStringTagOffset = 2
return opType(int(t.toNotIndent().fieldToStringTagField()) + {{ .OpLen }}) if strings.Contains(opType(int(t) + toStringTagOffset).String(), "StringTag") {
} return opType(int(t) + toStringTagOffset)
switch t {
{{- range $type := .OpNotIndentTypes }}
{{- if $type.IsFieldToStringTagField }}
case op{{ $type.Op }}:
return op{{ call $type.FieldToStringTagField }}
{{- end }}
{{- end }}
} }
return t return t
} }
@ -328,16 +294,16 @@ func (t opType) fieldToStringTagField() opType {
primitiveTypes := []string{ primitiveTypes := []string{
"int", "int8", "int16", "int32", "int64", "int", "int8", "int16", "int32", "int64",
"uint", "uint8", "uint16", "uint32", "uint64", "uint", "uint8", "uint16", "uint32", "uint64",
"float32", "float64", "bool", "string", "escapedString", "bytes", "float32", "float64", "bool", "string", "bytes",
"array", "map", "mapLoad", "slice", "struct", "MarshalJSON", "MarshalText", "recursive", "array", "map", "mapLoad", "slice", "struct", "MarshalJSON", "MarshalText", "recursive",
"intString", "int8String", "int16String", "int32String", "int64String", "intString", "int8String", "int16String", "int32String", "int64String",
"uintString", "uint8String", "uint16String", "uint32String", "uint64String", "uintString", "uint8String", "uint16String", "uint32String", "uint64String",
"intPtr", "int8Ptr", "int16Ptr", "int32Ptr", "int64Ptr", "intPtr", "int8Ptr", "int16Ptr", "int32Ptr", "int64Ptr",
"uintPtr", "uint8Ptr", "uint16Ptr", "uint32Ptr", "uint64Ptr", "uintPtr", "uint8Ptr", "uint16Ptr", "uint32Ptr", "uint64Ptr",
"float32Ptr", "float64Ptr", "boolPtr", "stringPtr", "escapedStringPtr", "bytesPtr", "float32Ptr", "float64Ptr", "boolPtr", "stringPtr", "bytesPtr",
"intNPtr", "int8NPtr", "int16NPtr", "int32NPtr", "int64NPtr", "intNPtr", "int8NPtr", "int16NPtr", "int32NPtr", "int64NPtr",
"uintNPtr", "uint8NPtr", "uint16NPtr", "uint32NPtr", "uint64NPtr", "uintNPtr", "uint8NPtr", "uint16NPtr", "uint32NPtr", "uint64NPtr",
"float32NPtr", "float64NPtr", "boolNPtr", "stringNPtr", "escapedStringNPtr", "bytesNPtr", "float32NPtr", "float64NPtr", "boolNPtr", "stringNPtr", "bytesNPtr",
} }
primitiveTypesUpper := []string{} primitiveTypesUpper := []string{}
for _, typ := range primitiveTypes { for _, typ := range primitiveTypes {
@ -367,109 +333,98 @@ func (t opType) fieldToStringTagField() opType {
} }
for _, typ := range primitiveTypesUpper { for _, typ := range primitiveTypesUpper {
typ := typ typ := typ
optype := createOpType(typ, "Op") opTypes = append(opTypes, createOpType(typ, "Op"))
switch typ {
case "String", "StringPtr", "StringNPtr":
optype.Escaped = func() string {
return fmt.Sprintf("Escaped%s", typ)
} }
} for _, typ := range append(primitiveTypesUpper, "") {
opTypes = append(opTypes, optype)
}
for _, escapedOrNot := range []string{"", "Escaped"} {
for _, ptrOrNot := range []string{"", "Ptr", "NPtr"} { for _, ptrOrNot := range []string{"", "Ptr", "NPtr"} {
for _, headType := range []string{"", "Anonymous"} { for _, headType := range []string{"", "Anonymous"} {
for _, opt := range []string{"", "OmitEmpty", "StringTag"} { for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
for _, typ := range append(primitiveTypesUpper, "") { for _, onlyOrNot := range []string{"", "Only"} {
escapedOrNot := escapedOrNot
ptrOrNot := ptrOrNot ptrOrNot := ptrOrNot
headType := headType headType := headType
opt := opt opt := opt
typ := typ typ := typ
onlyOrNot := onlyOrNot
op := fmt.Sprintf( op := fmt.Sprintf(
"Struct%sField%s%sHead%s%s", "StructField%s%sHead%s%s%s",
escapedOrNot,
ptrOrNot, ptrOrNot,
headType, headType,
opt, opt,
typ, typ,
onlyOrNot,
) )
opTypes = append(opTypes, opType{ opTypes = append(opTypes, opType{
Op: op, Op: op,
Code: "StructField", Code: "StructField",
Indent: func() string { return fmt.Sprintf("%sIndent", op) },
Escaped: func() string {
switch typ {
case "String", "StringPtr", "StringNPtr":
return fmt.Sprintf(
"StructEscapedField%s%sHead%sEscaped%s",
ptrOrNot,
headType,
opt,
typ,
)
}
return fmt.Sprintf(
"StructEscapedField%s%sHead%s%s",
ptrOrNot,
headType,
opt,
typ,
)
},
HeadToPtrHead: func() string { HeadToPtrHead: func() string {
return fmt.Sprintf( return fmt.Sprintf(
"Struct%sFieldPtr%sHead%s%s", "StructFieldPtr%sHead%s%s%s",
escapedOrNot,
headType, headType,
opt, opt,
typ, typ,
onlyOrNot,
) )
}, },
HeadToNPtrHead: func() string { HeadToNPtrHead: func() string {
return fmt.Sprintf( return fmt.Sprintf(
"Struct%sFieldNPtr%sHead%s%s", "StructFieldNPtr%sHead%s%s%s",
escapedOrNot,
headType, headType,
opt, opt,
typ, typ,
onlyOrNot,
) )
}, },
HeadToAnonymousHead: func() string { HeadToAnonymousHead: func() string {
return fmt.Sprintf( return fmt.Sprintf(
"Struct%sField%sAnonymousHead%s%s", "StructField%sAnonymousHead%s%s%s",
escapedOrNot,
ptrOrNot, ptrOrNot,
opt, opt,
typ, typ,
onlyOrNot,
) )
}, },
HeadToOmitEmptyHead: func() string { HeadToOmitEmptyHead: func() string {
return fmt.Sprintf( return fmt.Sprintf(
"Struct%sField%s%sHeadOmitEmpty%s", "StructField%s%sHeadOmitEmpty%s%s",
escapedOrNot,
ptrOrNot, ptrOrNot,
headType, headType,
typ, typ,
onlyOrNot,
) )
}, },
HeadToStringTagHead: func() string { HeadToStringTagHead: func() string {
return fmt.Sprintf( return fmt.Sprintf(
"Struct%sField%s%sHeadStringTag%s", "StructField%s%sHeadStringTag%s%s",
escapedOrNot,
ptrOrNot, ptrOrNot,
headType, headType,
typ, typ,
onlyOrNot,
)
},
HeadToOnlyHead: func() string {
switch typ {
case "", "Array", "Map", "MapLoad", "Slice",
"Struct", "Recursive", "MarshalJSON", "MarshalText",
"IntString", "Int8String", "Int16String", "Int32String", "Int64String",
"UintString", "Uint8String", "Uint16String", "Uint32String", "Uint64String":
return op
}
return fmt.Sprintf(
"StructField%s%sHead%s%sOnly",
ptrOrNot,
headType,
opt,
typ,
) )
}, },
PtrHeadToHead: func() string { PtrHeadToHead: func() string {
return fmt.Sprintf( return fmt.Sprintf(
"Struct%sField%sHead%s%s", "StructField%sHead%s%s%s",
escapedOrNot,
headType, headType,
opt, opt,
typ, typ,
onlyOrNot,
) )
}, },
FieldToEnd: func() string { return op }, FieldToEnd: func() string { return op },
@ -481,43 +436,25 @@ func (t opType) fieldToStringTagField() opType {
} }
} }
} }
for _, escapedOrNot := range []string{"", "Escaped"} {
for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
for _, typ := range append(primitiveTypesUpper, "") { for _, typ := range append(primitiveTypesUpper, "") {
escapedOrNot := escapedOrNot for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
opt := opt opt := opt
typ := typ typ := typ
op := fmt.Sprintf( op := fmt.Sprintf(
"Struct%sField%s%s", "StructField%s%s",
escapedOrNot,
opt, opt,
typ, typ,
) )
opTypes = append(opTypes, opType{ opTypes = append(opTypes, opType{
Op: op, Op: op,
Code: "StructField", Code: "StructField",
Indent: func() string { return fmt.Sprintf("%sIndent", op) },
Escaped: func() string {
switch typ {
case "String", "StringPtr", "StringNPtr":
return fmt.Sprintf(
"StructEscapedField%sEscaped%s",
opt,
typ,
)
}
return fmt.Sprintf(
"StructEscapedField%s%s",
opt,
typ,
)
},
HeadToPtrHead: func() string { return op }, HeadToPtrHead: func() string { return op },
HeadToNPtrHead: func() string { return op }, HeadToNPtrHead: func() string { return op },
HeadToAnonymousHead: func() string { return op }, HeadToAnonymousHead: func() string { return op },
HeadToOmitEmptyHead: func() string { return op }, HeadToOmitEmptyHead: func() string { return op },
HeadToStringTagHead: func() string { return op }, HeadToStringTagHead: func() string { return op },
HeadToOnlyHead: func() string { return op },
PtrHeadToHead: func() string { return op }, PtrHeadToHead: func() string { return op },
FieldToEnd: func() string { FieldToEnd: func() string {
switch typ { switch typ {
@ -525,67 +462,45 @@ func (t opType) fieldToStringTagField() opType {
return op return op
} }
return fmt.Sprintf( return fmt.Sprintf(
"Struct%sEnd%s%s", "StructEnd%s%s",
escapedOrNot,
opt, opt,
typ, typ,
) )
}, },
FieldToOmitEmptyField: func() string { FieldToOmitEmptyField: func() string {
return fmt.Sprintf( return fmt.Sprintf(
"Struct%sFieldOmitEmpty%s", "StructFieldOmitEmpty%s",
escapedOrNot,
typ, typ,
) )
}, },
FieldToStringTagField: func() string { FieldToStringTagField: func() string {
return fmt.Sprintf( return fmt.Sprintf(
"Struct%sFieldStringTag%s", "StructFieldStringTag%s",
escapedOrNot,
typ, typ,
) )
}, },
}) })
} }
} }
}
for _, escapedOrNot := range []string{"", "Escaped"} {
for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
for _, typ := range append(primitiveTypesUpper, "") { for _, typ := range append(primitiveTypesUpper, "") {
escapedOrNot := escapedOrNot for _, opt := range []string{"", "OmitEmpty", "StringTag"} {
opt := opt opt := opt
typ := typ typ := typ
op := fmt.Sprintf( op := fmt.Sprintf(
"Struct%sEnd%s%s", "StructEnd%s%s",
escapedOrNot,
opt, opt,
typ, typ,
) )
opTypes = append(opTypes, opType{ opTypes = append(opTypes, opType{
Op: op, Op: op,
Code: "StructEnd", Code: "StructEnd",
Indent: func() string { return fmt.Sprintf("%sIndent", op) },
Escaped: func() string {
switch typ {
case "String", "StringPtr", "StringNPtr":
return fmt.Sprintf(
"StructEscapedEnd%sEscaped%s",
opt,
typ,
)
}
return fmt.Sprintf(
"StructEscapedEnd%s%s",
opt,
typ,
)
},
HeadToPtrHead: func() string { return op }, HeadToPtrHead: func() string { return op },
HeadToNPtrHead: func() string { return op }, HeadToNPtrHead: func() string { return op },
HeadToAnonymousHead: func() string { return op }, HeadToAnonymousHead: func() string { return op },
HeadToOmitEmptyHead: func() string { return op }, HeadToOmitEmptyHead: func() string { return op },
HeadToStringTagHead: func() string { return op }, HeadToStringTagHead: func() string { return op },
HeadToOnlyHead: func() string { return op },
PtrHeadToHead: func() string { return op }, PtrHeadToHead: func() string { return op },
FieldToEnd: func() string { return op }, FieldToEnd: func() string { return op },
FieldToOmitEmptyField: func() string { return op }, FieldToOmitEmptyField: func() string { return op },
@ -593,57 +508,15 @@ func (t opType) fieldToStringTagField() opType {
}) })
} }
} }
}
indentOpTypes := []opType{}
for _, typ := range opTypes {
typ := typ
indentOpTypes = append(indentOpTypes, opType{
Op: fmt.Sprintf("%sIndent", typ.Op),
Code: typ.Code,
Indent: func() string { return fmt.Sprintf("%sIndent", typ.Op) },
Escaped: func() string { return fmt.Sprintf("%sIndent", typ.Escaped()) },
HeadToPtrHead: func() string {
return fmt.Sprintf("%sIndent", typ.HeadToPtrHead())
},
HeadToNPtrHead: func() string {
return fmt.Sprintf("%sIndent", typ.HeadToNPtrHead())
},
HeadToAnonymousHead: func() string {
return fmt.Sprintf("%sIndent", typ.HeadToAnonymousHead())
},
HeadToOmitEmptyHead: func() string {
return fmt.Sprintf("%sIndent", typ.HeadToOmitEmptyHead())
},
HeadToStringTagHead: func() string {
return fmt.Sprintf("%sIndent", typ.HeadToStringTagHead())
},
PtrHeadToHead: func() string {
return fmt.Sprintf("%sIndent", typ.PtrHeadToHead())
},
FieldToOmitEmptyField: func() string {
return fmt.Sprintf("%sIndent", typ.FieldToOmitEmptyField())
},
FieldToStringTagField: func() string {
return fmt.Sprintf("%sIndent", typ.FieldToStringTagField())
},
FieldToEnd: func() string {
return fmt.Sprintf("%sIndent", typ.FieldToEnd())
},
})
}
var b bytes.Buffer var b bytes.Buffer
if err := tmpl.Execute(&b, struct { if err := tmpl.Execute(&b, struct {
CodeTypes []string CodeTypes []string
OpTypes []opType OpTypes []opType
OpNotIndentTypes []opType
OpLen int OpLen int
OpIndentLen int
}{ }{
CodeTypes: codeTypes, CodeTypes: codeTypes,
OpTypes: append(opTypes, indentOpTypes...), OpTypes: opTypes,
OpNotIndentTypes: opTypes,
OpLen: len(opTypes), OpLen: len(opTypes),
OpIndentLen: len(indentOpTypes),
}); err != nil { }); err != nil {
return err return err
} }

12414
coverage_test.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -42,10 +42,7 @@ const (
) )
type opcodeSet struct { type opcodeSet struct {
escapedCode *opcode
escapedCodeIndent *opcode
code *opcode code *opcode
codeIndent *opcode
codeLength int codeLength int
} }
@ -194,24 +191,21 @@ func (e *Encoder) encode(header *interfaceHeader, isNil bool) ([]byte, error) {
typeptr := uintptr(unsafe.Pointer(typ)) typeptr := uintptr(unsafe.Pointer(typ))
opcodeMap := loadOpcodeMap() opcodeMap := loadOpcodeMap()
if codeSet, exists := opcodeMap[typeptr]; exists { if codeSet, exists := opcodeMap[typeptr]; exists {
var code *opcode
if e.enabledIndent {
if e.enabledHTMLEscape {
code = codeSet.escapedCodeIndent
} else {
code = codeSet.codeIndent
}
} else {
if e.enabledHTMLEscape {
code = codeSet.escapedCode
} else {
code = codeSet.code
}
}
ctx := e.ctx ctx := e.ctx
p := uintptr(header.ptr) p := uintptr(header.ptr)
ctx.init(p, codeSet.codeLength) ctx.init(p, codeSet.codeLength)
return e.run(ctx, b, code)
if e.enabledIndent {
if e.enabledHTMLEscape {
return e.runEscapedIndent(ctx, b, codeSet.code)
} else {
return e.runIndent(ctx, b, codeSet.code)
}
}
if e.enabledHTMLEscape {
return e.runEscaped(ctx, b, codeSet.code)
}
return e.run(ctx, b, codeSet.code)
} }
// noescape trick for header.typ ( reflect.*rtype ) // noescape trick for header.typ ( reflect.*rtype )
@ -226,13 +220,9 @@ func (e *Encoder) encode(header *interfaceHeader, isNil bool) ([]byte, error) {
return nil, err return nil, err
} }
code = copyOpcode(code) code = copyOpcode(code)
codeIndent := toIndent(code)
codeLength := code.totalLength() codeLength := code.totalLength()
codeSet := &opcodeSet{ codeSet := &opcodeSet{
escapedCode: toEscaped(code),
escapedCodeIndent: toEscaped(codeIndent),
code: code, code: code,
codeIndent: codeIndent,
codeLength: codeLength, codeLength: codeLength,
} }
@ -241,26 +231,17 @@ func (e *Encoder) encode(header *interfaceHeader, isNil bool) ([]byte, error) {
ctx := e.ctx ctx := e.ctx
ctx.init(p, codeLength) ctx.init(p, codeLength)
var c *opcode
if e.enabledIndent { if e.enabledIndent {
if e.enabledHTMLEscape { if e.enabledHTMLEscape {
c = codeSet.escapedCodeIndent return e.runEscapedIndent(ctx, b, codeSet.code)
} else { } else {
c = codeSet.codeIndent return e.runIndent(ctx, b, codeSet.code)
}
} }
} else {
if e.enabledHTMLEscape { if e.enabledHTMLEscape {
c = codeSet.escapedCode return e.runEscaped(ctx, b, codeSet.code)
} else {
c = codeSet.code
} }
} return e.run(ctx, b, codeSet.code)
b, err = e.run(ctx, b, c)
if err != nil {
return nil, err
}
return b, nil
} }
func encodeFloat32(b []byte, v float32) []byte { func encodeFloat32(b []byte, v float32) []byte {

View File

@ -32,6 +32,7 @@ func (e *Encoder) compileHead(ctx *encodeCompileContext) (*opcode, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
e.convertHeadOnlyCode(code, isPtr)
e.optimizeStructEnd(code) e.optimizeStructEnd(code)
return code, nil return code, nil
} else if isPtr && typ.Implements(marshalTextType) { } else if isPtr && typ.Implements(marshalTextType) {
@ -43,6 +44,7 @@ func (e *Encoder) compileHead(ctx *encodeCompileContext) (*opcode, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
e.convertHeadOnlyCode(code, isPtr)
e.optimizeStructEnd(code) e.optimizeStructEnd(code)
return code, nil return code, nil
} }
@ -88,6 +90,45 @@ func (e *Encoder) optimizeStructEnd(c *opcode) {
} }
} }
func (e *Encoder) convertHeadOnlyCode(c *opcode, isPtrHead bool) {
if c.nextField == nil {
return
}
if c.nextField.op.codeType() != codeStructEnd {
return
}
switch c.op {
case opStructFieldHead:
e.convertHeadOnlyCode(c.next, false)
if !strings.Contains(c.next.op.String(), "Only") {
return
}
c.op = opStructFieldHeadOnly
case opStructFieldHeadOmitEmpty:
return
case opStructFieldPtrHead:
}
if strings.Contains(c.op.String(), "Marshal") {
return
}
if strings.Contains(c.op.String(), "Slice") {
return
}
if strings.Contains(c.op.String(), "Map") {
return
}
isPtrOp := strings.Contains(c.op.String(), "Ptr")
if isPtrOp && !isPtrHead {
c.op = c.op.headToOnlyHead()
} else if !isPtrOp && isPtrHead {
c.op = c.op.headToPtrHead().headToOnlyHead()
} else if isPtrOp && isPtrHead {
c.op = c.op.headToPtrHead().headToOnlyHead()
}
}
func (e *Encoder) implementsMarshaler(typ *rtype) bool { func (e *Encoder) implementsMarshaler(typ *rtype) bool {
switch { switch {
case typ.Implements(marshalJSONType): case typ.Implements(marshalJSONType):
@ -1102,17 +1143,6 @@ func (e *Encoder) compileStruct(ctx *encodeCompileContext, isPtr bool) (*opcode,
anonymousFields[k] = append(anonymousFields[k], v...) anonymousFields[k] = append(anonymousFields[k], v...)
} }
} }
if fieldNum == 1 && valueCode.op == opPtr {
// if field number is one and primitive pointer type,
// it should encode as **not** pointer .
switch valueCode.next.op {
case opInt, opInt8, opInt16, opInt32, opInt64,
opUint, opUint8, opUint16, opUint32, opUint64,
opFloat32, opFloat64, opBool, opString, opBytes:
valueCode = valueCode.next
ctx.decOpcodeIndex()
}
}
key := fmt.Sprintf(`"%s":`, tag.key) key := fmt.Sprintf(`"%s":`, tag.key)
escapedKey := fmt.Sprintf(`%s:`, string(encodeEscapedString([]byte{}, tag.key))) escapedKey := fmt.Sprintf(`%s:`, string(encodeEscapedString([]byte{}, tag.key)))
fieldCode := &opcode{ fieldCode := &opcode{

View File

@ -53,34 +53,6 @@ func copyOpcode(code *opcode) *opcode {
return code.copy(codeMap) return code.copy(codeMap)
} }
func toIndent(c *opcode) *opcode {
c = copyOpcode(c)
for code := c; code.op != opEnd; {
code.op = code.op.toIndent()
switch code.op.codeType() {
case codeArrayElem, codeSliceElem, codeMapKey:
code = code.end
default:
code = code.next
}
}
return c
}
func toEscaped(c *opcode) *opcode {
c = copyOpcode(c)
for code := c; code.op != opEnd; {
code.op = code.op.toEscaped()
switch code.op.codeType() {
case codeArrayElem, codeSliceElem, codeMapKey:
code = code.end
default:
code = code.next
}
}
return c
}
func newOpCodeWithNext(ctx *encodeCompileContext, op opType, next *opcode) *opcode { func newOpCodeWithNext(ctx *encodeCompileContext, op opType, next *opcode) *opcode {
return &opcode{ return &opcode{
op: op, op: op,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

5700
encode_vm_escaped.go Normal file

File diff suppressed because it is too large Load Diff

4850
encode_vm_escaped_indent.go Normal file

File diff suppressed because it is too large Load Diff

4850
encode_vm_indent.go Normal file

File diff suppressed because it is too large Load Diff