mirror of https://github.com/goccy/go-json.git
Fix decoding of embeded struct
This commit is contained in:
parent
d7b9036e88
commit
6506007b6c
|
@ -0,0 +1,37 @@
|
|||
package json
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type anonymousFieldDecoder struct {
|
||||
structType *rtype
|
||||
offset uintptr
|
||||
dec decoder
|
||||
}
|
||||
|
||||
func newAnonymousFieldDecoder(structType *rtype, offset uintptr, dec decoder) *anonymousFieldDecoder {
|
||||
return &anonymousFieldDecoder{
|
||||
structType: structType,
|
||||
offset: offset,
|
||||
dec: dec,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *anonymousFieldDecoder) decodeStream(s *stream, p unsafe.Pointer) error {
|
||||
fmt.Println("called anonymous field decoder", *(*unsafe.Pointer)(p))
|
||||
if *(*unsafe.Pointer)(p) == nil {
|
||||
*(*unsafe.Pointer)(p) = unsafe_New(d.structType)
|
||||
}
|
||||
p = *(*unsafe.Pointer)(p)
|
||||
return d.dec.decodeStream(s, unsafe.Pointer(uintptr(p)+d.offset))
|
||||
}
|
||||
|
||||
func (d *anonymousFieldDecoder) decode(buf []byte, cursor int64, p unsafe.Pointer) (int64, error) {
|
||||
if *(*unsafe.Pointer)(p) == nil {
|
||||
*(*unsafe.Pointer)(p) = unsafe_New(d.structType)
|
||||
}
|
||||
p = *(*unsafe.Pointer)(p)
|
||||
return d.dec.decode(buf, cursor, unsafe.Pointer(uintptr(p)+d.offset))
|
||||
}
|
|
@ -215,15 +215,59 @@ func (d *Decoder) compileMap(typ *rtype, structName, fieldName string) (decoder,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newMapDecoder(typ, keyDec, valueDec, structName, fieldName), nil
|
||||
return newMapDecoder(typ, typ.Key(), keyDec, typ.Elem(), valueDec, structName, fieldName), nil
|
||||
}
|
||||
|
||||
func (d *Decoder) compileInterface(typ *rtype, structName, fieldName string) (decoder, error) {
|
||||
return newInterfaceDecoder(typ, structName, fieldName), nil
|
||||
}
|
||||
|
||||
func (d *Decoder) removeConflictFields(fieldMap map[string]*structFieldSet, conflictedMap map[string]struct{}, dec *structDecoder, baseOffset uintptr) {
|
||||
for k, v := range dec.fieldMap {
|
||||
if _, exists := conflictedMap[k]; exists {
|
||||
// already conflicted key
|
||||
continue
|
||||
}
|
||||
set, exists := fieldMap[k]
|
||||
if !exists {
|
||||
fieldSet := &structFieldSet{
|
||||
dec: v.dec,
|
||||
offset: baseOffset + v.offset,
|
||||
isTaggedKey: v.isTaggedKey,
|
||||
}
|
||||
fieldMap[k] = fieldSet
|
||||
fieldMap[strings.ToLower(k)] = fieldSet
|
||||
continue
|
||||
}
|
||||
if set.isTaggedKey {
|
||||
if v.isTaggedKey {
|
||||
// conflict tag key
|
||||
delete(fieldMap, k)
|
||||
conflictedMap[k] = struct{}{}
|
||||
conflictedMap[strings.ToLower(k)] = struct{}{}
|
||||
}
|
||||
} else {
|
||||
if v.isTaggedKey {
|
||||
fieldSet := &structFieldSet{
|
||||
dec: v.dec,
|
||||
offset: baseOffset + v.offset,
|
||||
isTaggedKey: v.isTaggedKey,
|
||||
}
|
||||
fieldMap[k] = fieldSet
|
||||
fieldMap[strings.ToLower(k)] = fieldSet
|
||||
} else {
|
||||
// conflict tag key
|
||||
delete(fieldMap, k)
|
||||
conflictedMap[k] = struct{}{}
|
||||
conflictedMap[strings.ToLower(k)] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Decoder) compileStruct(typ *rtype, structName, fieldName string) (decoder, error) {
|
||||
fieldNum := typ.NumField()
|
||||
conflictedMap := map[string]struct{}{}
|
||||
fieldMap := map[string]*structFieldSet{}
|
||||
typeptr := uintptr(unsafe.Pointer(typ))
|
||||
if dec, exists := d.structTypeToDecoder[typeptr]; exists {
|
||||
|
@ -242,13 +286,75 @@ func (d *Decoder) compileStruct(typ *rtype, structName, fieldName string) (decod
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tag.isString {
|
||||
dec = newWrappedStringDecoder(dec, structName, field.Name)
|
||||
if field.Anonymous && !tag.isTaggedKey {
|
||||
if stDec, ok := dec.(*structDecoder); ok {
|
||||
if type2rtype(field.Type) == typ {
|
||||
// recursive definition
|
||||
continue
|
||||
}
|
||||
d.removeConflictFields(fieldMap, conflictedMap, stDec, uintptr(field.Offset))
|
||||
} else if pdec, ok := dec.(*ptrDecoder); ok {
|
||||
contentDec := pdec.contentDecoder()
|
||||
if pdec.typ == typ {
|
||||
// recursive definition
|
||||
continue
|
||||
}
|
||||
if dec, ok := contentDec.(*structDecoder); ok {
|
||||
for k, v := range dec.fieldMap {
|
||||
if _, exists := conflictedMap[k]; exists {
|
||||
// already conflicted key
|
||||
continue
|
||||
}
|
||||
set, exists := fieldMap[k]
|
||||
if !exists {
|
||||
fieldSet := &structFieldSet{
|
||||
dec: newAnonymousFieldDecoder(pdec.typ, v.offset, v.dec),
|
||||
offset: uintptr(field.Offset),
|
||||
isTaggedKey: v.isTaggedKey,
|
||||
}
|
||||
fieldMap[k] = fieldSet
|
||||
fieldMap[strings.ToLower(k)] = fieldSet
|
||||
continue
|
||||
}
|
||||
if set.isTaggedKey {
|
||||
if v.isTaggedKey {
|
||||
// conflict tag key
|
||||
delete(fieldMap, k)
|
||||
conflictedMap[k] = struct{}{}
|
||||
conflictedMap[strings.ToLower(k)] = struct{}{}
|
||||
}
|
||||
} else {
|
||||
if v.isTaggedKey {
|
||||
fieldSet := &structFieldSet{
|
||||
dec: newAnonymousFieldDecoder(pdec.typ, v.offset, v.dec),
|
||||
offset: uintptr(field.Offset),
|
||||
isTaggedKey: v.isTaggedKey,
|
||||
}
|
||||
fieldMap[k] = fieldSet
|
||||
fieldMap[strings.ToLower(k)] = fieldSet
|
||||
} else {
|
||||
// conflict tag key
|
||||
delete(fieldMap, k)
|
||||
conflictedMap[k] = struct{}{}
|
||||
conflictedMap[strings.ToLower(k)] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if tag.isString {
|
||||
dec = newWrappedStringDecoder(dec, structName, field.Name)
|
||||
}
|
||||
fieldSet := &structFieldSet{dec: dec, offset: field.Offset, isTaggedKey: tag.isTaggedKey}
|
||||
if tag.key != "" {
|
||||
fieldMap[tag.key] = fieldSet
|
||||
fieldMap[strings.ToLower(tag.key)] = fieldSet
|
||||
} else {
|
||||
fieldMap[field.Name] = fieldSet
|
||||
fieldMap[strings.ToLower(field.Name)] = fieldSet
|
||||
}
|
||||
}
|
||||
fieldSet := &structFieldSet{dec: dec, offset: field.Offset}
|
||||
fieldMap[field.Name] = fieldSet
|
||||
fieldMap[tag.key] = fieldSet
|
||||
fieldMap[strings.ToLower(tag.key)] = fieldSet
|
||||
}
|
||||
delete(d.structTypeToDecoder, typeptr)
|
||||
return structDec, nil
|
||||
|
|
|
@ -34,6 +34,9 @@ var (
|
|||
interfaceMapType = type2rtype(
|
||||
reflect.TypeOf((*map[string]interface{})(nil)).Elem(),
|
||||
)
|
||||
stringType = type2rtype(
|
||||
reflect.TypeOf(""),
|
||||
)
|
||||
)
|
||||
|
||||
func (d *interfaceDecoder) decodeStream(s *stream, p unsafe.Pointer) error {
|
||||
|
@ -45,7 +48,9 @@ func (d *interfaceDecoder) decodeStream(s *stream, p unsafe.Pointer) error {
|
|||
ptr := unsafe.Pointer(&v)
|
||||
if err := newMapDecoder(
|
||||
interfaceMapType,
|
||||
stringType,
|
||||
newStringDecoder(d.structName, d.fieldName),
|
||||
interfaceMapType.Elem(),
|
||||
newInterfaceDecoder(d.typ, d.structName, d.fieldName),
|
||||
d.structName,
|
||||
d.fieldName,
|
||||
|
@ -129,7 +134,9 @@ func (d *interfaceDecoder) decode(buf []byte, cursor int64, p unsafe.Pointer) (i
|
|||
ptr := unsafe.Pointer(&v)
|
||||
dec := newMapDecoder(
|
||||
interfaceMapType,
|
||||
stringType,
|
||||
newStringDecoder(d.structName, d.fieldName),
|
||||
interfaceMapType.Elem(),
|
||||
newInterfaceDecoder(d.typ, d.structName, d.fieldName),
|
||||
d.structName, d.fieldName,
|
||||
)
|
||||
|
|
|
@ -6,16 +6,20 @@ import (
|
|||
|
||||
type mapDecoder struct {
|
||||
mapType *rtype
|
||||
keyType *rtype
|
||||
valueType *rtype
|
||||
keyDecoder decoder
|
||||
valueDecoder decoder
|
||||
structName string
|
||||
fieldName string
|
||||
}
|
||||
|
||||
func newMapDecoder(mapType *rtype, keyDec decoder, valueDec decoder, structName, fieldName string) *mapDecoder {
|
||||
func newMapDecoder(mapType *rtype, keyType *rtype, keyDec decoder, valueType *rtype, valueDec decoder, structName, fieldName string) *mapDecoder {
|
||||
return &mapDecoder{
|
||||
mapType: mapType,
|
||||
keyDecoder: keyDec,
|
||||
keyType: keyType,
|
||||
valueType: valueType,
|
||||
valueDecoder: valueDec,
|
||||
structName: structName,
|
||||
fieldName: fieldName,
|
||||
|
@ -39,16 +43,6 @@ func (d *mapDecoder) setValue(buf []byte, cursor int64, key interface{}) (int64,
|
|||
return d.valueDecoder.decode(buf, cursor, header.ptr)
|
||||
}
|
||||
|
||||
func (d *mapDecoder) setKeyStream(s *stream, key interface{}) error {
|
||||
header := (*interfaceHeader)(unsafe.Pointer(&key))
|
||||
return d.keyDecoder.decodeStream(s, header.ptr)
|
||||
}
|
||||
|
||||
func (d *mapDecoder) setValueStream(s *stream, key interface{}) error {
|
||||
header := (*interfaceHeader)(unsafe.Pointer(&key))
|
||||
return d.valueDecoder.decodeStream(s, header.ptr)
|
||||
}
|
||||
|
||||
func (d *mapDecoder) decodeStream(s *stream, p unsafe.Pointer) error {
|
||||
s.skipWhiteSpace()
|
||||
switch s.char() {
|
||||
|
@ -70,8 +64,8 @@ func (d *mapDecoder) decodeStream(s *stream, p unsafe.Pointer) error {
|
|||
}
|
||||
for {
|
||||
s.cursor++
|
||||
var key interface{}
|
||||
if err := d.setKeyStream(s, &key); err != nil {
|
||||
k := unsafe_New(d.keyType)
|
||||
if err := d.keyDecoder.decodeStream(s, k); err != nil {
|
||||
return err
|
||||
}
|
||||
s.skipWhiteSpace()
|
||||
|
@ -82,11 +76,11 @@ func (d *mapDecoder) decodeStream(s *stream, p unsafe.Pointer) error {
|
|||
return errExpected("colon after object key", s.totalOffset())
|
||||
}
|
||||
s.cursor++
|
||||
var value interface{}
|
||||
if err := d.setValueStream(s, &value); err != nil {
|
||||
v := unsafe_New(d.valueType)
|
||||
if err := d.valueDecoder.decodeStream(s, v); err != nil {
|
||||
return err
|
||||
}
|
||||
mapassign(d.mapType, mapValue, unsafe.Pointer(&key), unsafe.Pointer(&value))
|
||||
mapassign(d.mapType, mapValue, k, v)
|
||||
s.skipWhiteSpace()
|
||||
if s.char() == nul {
|
||||
s.read()
|
||||
|
|
|
@ -20,6 +20,14 @@ func newPtrDecoder(dec decoder, typ *rtype, structName, fieldName string) *ptrDe
|
|||
}
|
||||
}
|
||||
|
||||
func (d *ptrDecoder) contentDecoder() decoder {
|
||||
dec, ok := d.dec.(*ptrDecoder)
|
||||
if !ok {
|
||||
return d.dec
|
||||
}
|
||||
return dec.contentDecoder()
|
||||
}
|
||||
|
||||
//go:linkname unsafe_New reflect.unsafe_New
|
||||
func unsafe_New(*rtype) unsafe.Pointer
|
||||
|
||||
|
|
|
@ -6,8 +6,9 @@ import (
|
|||
)
|
||||
|
||||
type structFieldSet struct {
|
||||
dec decoder
|
||||
offset uintptr
|
||||
dec decoder
|
||||
offset uintptr
|
||||
isTaggedKey bool
|
||||
}
|
||||
|
||||
type structDecoder struct {
|
||||
|
|
Loading…
Reference in New Issue