From 7ada1b2467b51d4ce27f869da00943b434ceb97f Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Sat, 22 Aug 2020 15:40:18 +0900 Subject: [PATCH] Fix conflicted anonymous fields --- encode_compile.go | 156 +++++++++++++++++++++++++++++----------------- encode_opcode.go | 2 + struct_field.go | 5 +- 3 files changed, 105 insertions(+), 58 deletions(-) diff --git a/encode_compile.go b/encode_compile.go index 4733700..8d81da9 100644 --- a/encode_compile.go +++ b/encode_compile.go @@ -615,9 +615,100 @@ func (e *Encoder) optimizeAnonymousFields(head *structFieldCode) { } type structFieldPair struct { - prevField *structFieldCode - curField *structFieldCode - linked bool + prevField *structFieldCode + curField *structFieldCode + isTaggedKey bool + linked bool +} + +func (e *Encoder) anonymousStructFieldPairMap(typ *rtype, tags structTags, valueCode *structFieldCode) map[string][]structFieldPair { + //fmt.Println("type = ", typ, "valueCode = ", valueCode.dump()) + anonymousFields := map[string][]structFieldPair{} + f := valueCode + var prevAnonymousField *structFieldCode + for { + 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 + } + + anonymousFields[f.displayKey] = append(anonymousFields[f.displayKey], structFieldPair{ + prevField: prevAnonymousField, + curField: f, + isTaggedKey: f.isTaggedKey, + }) + if f.next != nil && f.nextField != f.next && f.next.op.codeType() == codeStructField { + for k, v := range e.anonymousStructFieldPairMap(typ, tags, f.next.toStructFieldCode()) { + anonymousFields[k] = append(anonymousFields[k], v...) + } + } + if f.nextField == nil { + break + } + prevAnonymousField = f + f = f.nextField.toStructFieldCode() + } + return anonymousFields +} + +func (e *Encoder) optimizeConflictAnonymousFields(anonymousFields map[string][]structFieldPair) { + for _, fieldPairs := range anonymousFields { + if len(fieldPairs) == 1 { + continue + } + // conflict anonymous fields + taggedPairs := []structFieldPair{} + for _, fieldPair := range fieldPairs { + if fieldPair.isTaggedKey { + taggedPairs = append(taggedPairs, fieldPair) + } else { + if !fieldPair.linked { + if fieldPair.prevField == nil { + // head operation + fieldPair.curField.op = opStructFieldAnonymousHead + } else { + linkPrevToNextField(fieldPair.prevField, fieldPair.curField) + } + fieldPair.linked = true + } + } + } + if len(taggedPairs) > 1 { + for _, fieldPair := range taggedPairs { + if !fieldPair.linked { + if fieldPair.prevField == nil { + // head operation + fieldPair.curField.op = opStructFieldAnonymousHead + } else { + linkPrevToNextField(fieldPair.prevField, fieldPair.curField) + } + fieldPair.linked = true + } + } + } else { + for _, fieldPair := range taggedPairs { + fieldPair.curField.isTaggedKey = false + } + } + } } func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opcode, error) { @@ -643,7 +734,7 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco ) e.indent++ tags := structTags{} - anonymousFields := map[string]structFieldPair{} + anonymousFields := map[string][]structFieldPair{} for i := 0; i < fieldNum; i++ { field := typ.Field(i) if isIgnoredStructField(field) { @@ -668,59 +759,8 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco return nil, err } if field.Anonymous { - f := valueCode.toStructFieldCode() - var prevAnonymousField *structFieldCode - for { - 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() + for k, v := range e.anonymousStructFieldPairMap(typ, tags, valueCode.toStructFieldCode()) { + anonymousFields[k] = append(anonymousFields[k], v...) } } if fieldNum == 1 && valueCode.op == opPtr { @@ -742,6 +782,7 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco }, anonymousKey: field.Anonymous, key: []byte(key), + isTaggedKey: tag.isTaggedKey, displayKey: tag.key, offset: field.Offset, } @@ -794,6 +835,7 @@ func (e *Encoder) compileStruct(typ *rtype, isPtr, root, withIndent bool) (*opco head.end = structEndCode code.next = structEndCode + e.optimizeConflictAnonymousFields(anonymousFields) e.optimizeAnonymousFields(head) ret := (*opcode)(unsafe.Pointer(head)) diff --git a/encode_opcode.go b/encode_opcode.go index 59deec9..b43dc41 100644 --- a/encode_opcode.go +++ b/encode_opcode.go @@ -306,6 +306,7 @@ type structFieldCode struct { *opcodeHeader key []byte displayKey string + isTaggedKey bool offset uintptr anonymousKey bool nextField *opcode @@ -352,6 +353,7 @@ func (c *structFieldCode) copy(codeMap map[uintptr]*opcode) *opcode { } field := &structFieldCode{ key: c.key, + isTaggedKey: c.isTaggedKey, displayKey: c.displayKey, anonymousKey: c.anonymousKey, offset: c.offset, diff --git a/struct_field.go b/struct_field.go index 95de4bf..b2a6f50 100644 --- a/struct_field.go +++ b/struct_field.go @@ -23,6 +23,7 @@ func isIgnoredStructField(field reflect.StructField) bool { type structTag struct { key string + isTaggedKey bool isOmitEmpty bool isString bool field reflect.StructField @@ -42,13 +43,15 @@ func (t structTags) existsKey(key string) bool { func structTagFromField(field reflect.StructField) *structTag { keyName := field.Name tag := getTag(field) + st := &structTag{field: field} opts := strings.Split(tag, ",") if len(opts) > 0 { if opts[0] != "" { keyName = opts[0] + st.isTaggedKey = true } } - st := &structTag{key: keyName, field: field} + st.key = keyName if len(opts) > 1 { st.isOmitEmpty = opts[1] == "omitempty" st.isString = opts[1] == "string"