mirror of https://github.com/goccy/go-json.git
Fix anonymous fields
This commit is contained in:
parent
3e03bdc53f
commit
a718a9a1ef
|
@ -566,6 +566,60 @@ func (e *Encoder) structField(fieldCode *structFieldCode, valueCode *opcode, tag
|
|||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func (e *Encoder) isNotExistsField(head *structFieldCode) bool {
|
||||
if head == nil {
|
||||
return false
|
||||
}
|
||||
if head.op != opStructFieldAnonymousHead {
|
||||
return false
|
||||
}
|
||||
if head.next == nil {
|
||||
return false
|
||||
}
|
||||
if head.nextField == nil {
|
||||
return false
|
||||
}
|
||||
if head.nextField.op != opStructAnonymousEnd {
|
||||
return false
|
||||
}
|
||||
if head.next.op == opStructAnonymousEnd {
|
||||
return true
|
||||
}
|
||||
if head.next.op.codeType() != codeStructField {
|
||||
return false
|
||||
}
|
||||
return e.isNotExistsField(head.next.toStructFieldCode())
|
||||
}
|
||||
|
||||
func (e *Encoder) optimizeAnonymousFields(head *structFieldCode) {
|
||||
code := head
|
||||
var prev *structFieldCode
|
||||
for {
|
||||
if code.op == opStructEnd || code.op == opStructEndIndent {
|
||||
break
|
||||
}
|
||||
if code.op == opStructField || code.op == opStructFieldIndent {
|
||||
codeType := code.next.op.codeType()
|
||||
if codeType == codeStructField {
|
||||
if e.isNotExistsField(code.next.toStructFieldCode()) {
|
||||
code.next = code.nextField
|
||||
linkPrevToNextField(prev, code)
|
||||
code = prev
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = code
|
||||
code = code.nextField.toStructFieldCode()
|
||||
}
|
||||
}
|
||||
|
||||
type structFieldPair struct {
|
||||
prevField *structFieldCode
|
||||
curField *structFieldCode
|
||||
linked bool
|
||||
}
|
||||
|
||||
func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opcode, error) {
|
||||
if code := e.compiledCode(typ, withIndent); code != nil {
|
||||
return code, nil
|
||||
|
@ -588,12 +642,17 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
|
|||
prevField *structFieldCode
|
||||
)
|
||||
e.indent++
|
||||
tags := structTags{}
|
||||
anonymousFields := map[string]structFieldPair{}
|
||||
for i := 0; i < fieldNum; i++ {
|
||||
field := typ.Field(i)
|
||||
if isIgnoredStructField(field) {
|
||||
continue
|
||||
}
|
||||
tag := structTagFromField(field)
|
||||
tags = append(tags, structTagFromField(field))
|
||||
}
|
||||
for i, tag := range tags {
|
||||
field := tag.field
|
||||
fieldType := type2rtype(field.Type)
|
||||
if isPtr && i == 0 {
|
||||
// head field of pointer structure at top level
|
||||
|
@ -610,14 +669,57 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
|
|||
}
|
||||
if field.Anonymous {
|
||||
f := valueCode.toStructFieldCode()
|
||||
var prevAnonymousField *structFieldCode
|
||||
for {
|
||||
f.op = f.op.headToAnonymousHead()
|
||||
if f.op == opStructEnd {
|
||||
existsKey := tags.existsKey(f.displayKey)
|
||||
op := f.op.headToAnonymousHead()
|
||||
if op != f.op {
|
||||
if existsKey {
|
||||
f.op = opStructFieldAnonymousHead
|
||||
} else {
|
||||
f.op = op
|
||||
}
|
||||
} else if f.op == opStructEnd {
|
||||
f.op = opStructAnonymousEnd
|
||||
} else if existsKey {
|
||||
linkPrevToNextField(prevAnonymousField, f)
|
||||
}
|
||||
|
||||
if f.displayKey == "" {
|
||||
if f.nextField == nil {
|
||||
break
|
||||
}
|
||||
prevAnonymousField = f
|
||||
f = f.nextField.toStructFieldCode()
|
||||
continue
|
||||
}
|
||||
|
||||
// conflict anonymous fields
|
||||
if existsFieldSet, exists := anonymousFields[f.displayKey]; exists {
|
||||
if !existsFieldSet.linked {
|
||||
if existsFieldSet.prevField == nil {
|
||||
// head operation
|
||||
existsFieldSet.curField.op = opStructFieldAnonymousHead
|
||||
} else {
|
||||
linkPrevToNextField(existsFieldSet.prevField, existsFieldSet.curField)
|
||||
}
|
||||
existsFieldSet.linked = true
|
||||
}
|
||||
if prevAnonymousField == nil {
|
||||
// head operation
|
||||
f.op = opStructFieldAnonymousHead
|
||||
} else {
|
||||
linkPrevToNextField(prevAnonymousField, f)
|
||||
}
|
||||
}
|
||||
anonymousFields[f.displayKey] = structFieldPair{
|
||||
prevField: prevAnonymousField,
|
||||
curField: f,
|
||||
}
|
||||
if f.nextField == nil {
|
||||
break
|
||||
}
|
||||
prevAnonymousField = f
|
||||
f = f.nextField.toStructFieldCode()
|
||||
}
|
||||
}
|
||||
|
@ -640,6 +742,7 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
|
|||
},
|
||||
anonymousKey: field.Anonymous,
|
||||
key: []byte(key),
|
||||
displayKey: tag.key,
|
||||
offset: field.Offset,
|
||||
}
|
||||
if fieldIdx == 0 {
|
||||
|
@ -690,6 +793,9 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco
|
|||
}
|
||||
head.end = structEndCode
|
||||
code.next = structEndCode
|
||||
|
||||
e.optimizeAnonymousFields(head)
|
||||
|
||||
ret := (*opcode)(unsafe.Pointer(head))
|
||||
compiled.code = ret
|
||||
|
||||
|
|
|
@ -53,12 +53,12 @@ func (c *opcode) beforeLastCode() *opcode {
|
|||
code := c
|
||||
for {
|
||||
var nextCode *opcode
|
||||
switch code.op {
|
||||
case opArrayElem, opArrayElemIndent:
|
||||
switch code.op.codeType() {
|
||||
case codeArrayElem:
|
||||
nextCode = code.toArrayElemCode().end
|
||||
case opSliceElem, opSliceElemIndent, opRootSliceElemIndent:
|
||||
case codeSliceElem:
|
||||
nextCode = code.toSliceElemCode().end
|
||||
case opMapKey, opMapKeyIndent, opRootMapKeyIndent:
|
||||
case codeMapKey:
|
||||
nextCode = code.toMapKeyCode().end
|
||||
default:
|
||||
nextCode = code.next
|
||||
|
@ -112,13 +112,13 @@ func (c *opcode) dump() string {
|
|||
codes := []string{}
|
||||
for code := c; code.op != opEnd; {
|
||||
indent := strings.Repeat(" ", code.indent)
|
||||
codes = append(codes, fmt.Sprintf("%s%s", indent, code.op))
|
||||
switch code.op {
|
||||
case opArrayElem, opArrayElemIndent:
|
||||
codes = append(codes, fmt.Sprintf("%s%s ( %p )", indent, code.op, unsafe.Pointer(code)))
|
||||
switch code.op.codeType() {
|
||||
case codeArrayElem:
|
||||
code = code.toArrayElemCode().end
|
||||
case opSliceElem, opSliceElemIndent, opRootSliceElemIndent:
|
||||
case codeSliceElem:
|
||||
code = code.toSliceElemCode().end
|
||||
case opMapKey, opMapKeyIndent, opRootMapKeyIndent:
|
||||
case codeMapKey:
|
||||
code = code.toMapKeyCode().end
|
||||
default:
|
||||
code = code.next
|
||||
|
@ -305,12 +305,43 @@ func (c *arrayElemCode) copy(codeMap map[uintptr]*opcode) *opcode {
|
|||
type structFieldCode struct {
|
||||
*opcodeHeader
|
||||
key []byte
|
||||
displayKey string
|
||||
offset uintptr
|
||||
anonymousKey bool
|
||||
nextField *opcode
|
||||
end *opcode
|
||||
}
|
||||
|
||||
func linkPrevToNextField(prev, cur *structFieldCode) {
|
||||
prev.nextField = cur.nextField
|
||||
code := prev.toOpcode()
|
||||
fcode := cur.toOpcode()
|
||||
for {
|
||||
var nextCode *opcode
|
||||
switch code.op.codeType() {
|
||||
case codeArrayElem:
|
||||
nextCode = code.toArrayElemCode().end
|
||||
case codeSliceElem:
|
||||
nextCode = code.toSliceElemCode().end
|
||||
case codeMapKey:
|
||||
nextCode = code.toMapKeyCode().end
|
||||
default:
|
||||
nextCode = code.next
|
||||
}
|
||||
if nextCode == fcode {
|
||||
code.next = fcode.next
|
||||
break
|
||||
} else if nextCode.op == opEnd {
|
||||
break
|
||||
}
|
||||
code = nextCode
|
||||
}
|
||||
}
|
||||
|
||||
func (c *structFieldCode) toOpcode() *opcode {
|
||||
return (*opcode)(unsafe.Pointer(c))
|
||||
}
|
||||
|
||||
func (c *structFieldCode) copy(codeMap map[uintptr]*opcode) *opcode {
|
||||
if c == nil {
|
||||
return nil
|
||||
|
@ -321,6 +352,7 @@ func (c *structFieldCode) copy(codeMap map[uintptr]*opcode) *opcode {
|
|||
}
|
||||
field := &structFieldCode{
|
||||
key: c.key,
|
||||
displayKey: c.displayKey,
|
||||
anonymousKey: c.anonymousKey,
|
||||
offset: c.offset,
|
||||
}
|
||||
|
|
14
encode_vm.go
14
encode_vm.go
|
@ -549,6 +549,16 @@ func (e *Encoder) run(code *opcode) error {
|
|||
code.ptr = ptr
|
||||
field.nextField.ptr = ptr
|
||||
}
|
||||
case opStructFieldAnonymousHead:
|
||||
field := code.toStructFieldCode()
|
||||
ptr := field.ptr
|
||||
if ptr == 0 {
|
||||
code = field.end.next
|
||||
} else {
|
||||
code = field.next
|
||||
code.ptr = ptr
|
||||
field.nextField.ptr = ptr
|
||||
}
|
||||
case opStructFieldPtrHeadInt:
|
||||
code.ptr = e.ptrToPtr(code.ptr)
|
||||
fallthrough
|
||||
|
@ -3872,7 +3882,9 @@ func (e *Encoder) run(code *opcode) error {
|
|||
e.encodeByte(',')
|
||||
}
|
||||
c := code.toStructFieldCode()
|
||||
e.encodeBytes(c.key)
|
||||
if !c.anonymousKey {
|
||||
e.encodeBytes(c.key)
|
||||
}
|
||||
code = code.next
|
||||
code.ptr = c.ptr + c.offset
|
||||
c.nextField.ptr = c.ptr
|
||||
|
|
|
@ -25,6 +25,18 @@ type structTag struct {
|
|||
key string
|
||||
isOmitEmpty bool
|
||||
isString bool
|
||||
field reflect.StructField
|
||||
}
|
||||
|
||||
type structTags []*structTag
|
||||
|
||||
func (t structTags) existsKey(key string) bool {
|
||||
for _, tt := range t {
|
||||
if tt.key == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func structTagFromField(field reflect.StructField) *structTag {
|
||||
|
@ -36,7 +48,7 @@ func structTagFromField(field reflect.StructField) *structTag {
|
|||
keyName = opts[0]
|
||||
}
|
||||
}
|
||||
st := &structTag{key: keyName}
|
||||
st := &structTag{key: keyName, field: field}
|
||||
if len(opts) > 1 {
|
||||
st.isOmitEmpty = opts[1] == "omitempty"
|
||||
st.isString = opts[1] == "string"
|
||||
|
|
Loading…
Reference in New Issue