package decoder import ( "encoding/json" "fmt" "reflect" "strings" "sync/atomic" "unicode" "unsafe" "github.com/goccy/go-json/internal/errors" "github.com/goccy/go-json/internal/runtime" ) var ( jsonNumberType = reflect.TypeOf(json.Number("")) typeAddr *runtime.TypeAddr cachedDecoderMap unsafe.Pointer // map[uintptr]decoder cachedDecoder []Decoder ) func init() { typeAddr = runtime.AnalyzeTypeAddr() if typeAddr == nil { typeAddr = &runtime.TypeAddr{} } cachedDecoder = make([]Decoder, typeAddr.AddrRange>>typeAddr.AddrShift) } func loadDecoderMap() map[uintptr]Decoder { p := atomic.LoadPointer(&cachedDecoderMap) return *(*map[uintptr]Decoder)(unsafe.Pointer(&p)) } func storeDecoder(typ uintptr, dec Decoder, m map[uintptr]Decoder) { newDecoderMap := make(map[uintptr]Decoder, len(m)+1) newDecoderMap[typ] = dec for k, v := range m { newDecoderMap[k] = v } atomic.StorePointer(&cachedDecoderMap, *(*unsafe.Pointer)(unsafe.Pointer(&newDecoderMap))) } func compileToGetDecoderSlowPath(typeptr uintptr, typ *runtime.Type) (Decoder, error) { decoderMap := loadDecoderMap() if dec, exists := decoderMap[typeptr]; exists { return dec, nil } dec, err := compileHead(typ, map[uintptr]Decoder{}) if err != nil { return nil, err } storeDecoder(typeptr, dec, decoderMap) return dec, nil } func compileHead(typ *runtime.Type, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) { switch { case implementsUnmarshalJSONType(runtime.PtrTo(typ)): return newUnmarshalJSONDecoder(runtime.PtrTo(typ), "", ""), nil case runtime.PtrTo(typ).Implements(unmarshalTextType): return newUnmarshalTextDecoder(runtime.PtrTo(typ), "", ""), nil } return compile(typ.Elem(), "", "", structTypeToDecoder) } func compile(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) { switch { case implementsUnmarshalJSONType(runtime.PtrTo(typ)): return newUnmarshalJSONDecoder(runtime.PtrTo(typ), structName, fieldName), nil case runtime.PtrTo(typ).Implements(unmarshalTextType): return newUnmarshalTextDecoder(runtime.PtrTo(typ), structName, fieldName), nil } switch typ.Kind() { case reflect.Ptr: return compilePtr(typ, structName, fieldName, structTypeToDecoder) case reflect.Struct: return compileStruct(typ, structName, fieldName, structTypeToDecoder) case reflect.Slice: elem := typ.Elem() if elem.Kind() == reflect.Uint8 { return compileBytes(elem, structName, fieldName) } return compileSlice(typ, structName, fieldName, structTypeToDecoder) case reflect.Array: return compileArray(typ, structName, fieldName, structTypeToDecoder) case reflect.Map: return compileMap(typ, structName, fieldName, structTypeToDecoder) case reflect.Interface: return compileInterface(typ, structName, fieldName) case reflect.Uintptr: return compileUint(typ, structName, fieldName) case reflect.Int: return compileInt(typ, structName, fieldName) case reflect.Int8: return compileInt8(typ, structName, fieldName) case reflect.Int16: return compileInt16(typ, structName, fieldName) case reflect.Int32: return compileInt32(typ, structName, fieldName) case reflect.Int64: return compileInt64(typ, structName, fieldName) case reflect.Uint: return compileUint(typ, structName, fieldName) case reflect.Uint8: return compileUint8(typ, structName, fieldName) case reflect.Uint16: return compileUint16(typ, structName, fieldName) case reflect.Uint32: return compileUint32(typ, structName, fieldName) case reflect.Uint64: return compileUint64(typ, structName, fieldName) case reflect.String: return compileString(typ, structName, fieldName) case reflect.Bool: return compileBool(structName, fieldName) case reflect.Float32: return compileFloat32(structName, fieldName) case reflect.Float64: return compileFloat64(structName, fieldName) case reflect.Func: return compileFunc(typ, structName, fieldName) } return nil, &errors.UnmarshalTypeError{ Value: "object", Type: runtime.RType2Type(typ), Offset: 0, Struct: structName, Field: fieldName, } } func isStringTagSupportedType(typ *runtime.Type) bool { switch { case implementsUnmarshalJSONType(runtime.PtrTo(typ)): return false case runtime.PtrTo(typ).Implements(unmarshalTextType): return false } switch typ.Kind() { case reflect.Map: return false case reflect.Slice: return false case reflect.Array: return false case reflect.Struct: return false case reflect.Interface: return false } return true } func compileMapKey(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) { if runtime.PtrTo(typ).Implements(unmarshalTextType) { return newUnmarshalTextDecoder(runtime.PtrTo(typ), structName, fieldName), nil } dec, err := compile(typ, structName, fieldName, structTypeToDecoder) if err != nil { return nil, err } for { switch t := dec.(type) { case *stringDecoder, *interfaceDecoder: return dec, nil case *boolDecoder, *intDecoder, *uintDecoder, *numberDecoder: return newWrappedStringDecoder(typ, dec, structName, fieldName), nil case *ptrDecoder: dec = t.dec default: goto ERROR } } ERROR: return nil, &errors.UnmarshalTypeError{ Value: "object", Type: runtime.RType2Type(typ), Offset: 0, Struct: structName, Field: fieldName, } } func compilePtr(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) { dec, err := compile(typ.Elem(), structName, fieldName, structTypeToDecoder) if err != nil { return nil, err } return newPtrDecoder(dec, typ.Elem(), structName, fieldName), nil } func compileInt(typ *runtime.Type, structName, fieldName string) (Decoder, error) { return newIntDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v int64) { *(*int)(p) = int(v) }), nil } func compileInt8(typ *runtime.Type, structName, fieldName string) (Decoder, error) { return newIntDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v int64) { *(*int8)(p) = int8(v) }), nil } func compileInt16(typ *runtime.Type, structName, fieldName string) (Decoder, error) { return newIntDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v int64) { *(*int16)(p) = int16(v) }), nil } func compileInt32(typ *runtime.Type, structName, fieldName string) (Decoder, error) { return newIntDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v int64) { *(*int32)(p) = int32(v) }), nil } func compileInt64(typ *runtime.Type, structName, fieldName string) (Decoder, error) { return newIntDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v int64) { *(*int64)(p) = v }), nil } func compileUint(typ *runtime.Type, structName, fieldName string) (Decoder, error) { return newUintDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v uint64) { *(*uint)(p) = uint(v) }), nil } func compileUint8(typ *runtime.Type, structName, fieldName string) (Decoder, error) { return newUintDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v uint64) { *(*uint8)(p) = uint8(v) }), nil } func compileUint16(typ *runtime.Type, structName, fieldName string) (Decoder, error) { return newUintDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v uint64) { *(*uint16)(p) = uint16(v) }), nil } func compileUint32(typ *runtime.Type, structName, fieldName string) (Decoder, error) { return newUintDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v uint64) { *(*uint32)(p) = uint32(v) }), nil } func compileUint64(typ *runtime.Type, structName, fieldName string) (Decoder, error) { return newUintDecoder(typ, structName, fieldName, func(p unsafe.Pointer, v uint64) { *(*uint64)(p) = v }), nil } func compileFloat32(structName, fieldName string) (Decoder, error) { return newFloatDecoder(structName, fieldName, func(p unsafe.Pointer, v float64) { *(*float32)(p) = float32(v) }), nil } func compileFloat64(structName, fieldName string) (Decoder, error) { return newFloatDecoder(structName, fieldName, func(p unsafe.Pointer, v float64) { *(*float64)(p) = v }), nil } func compileString(typ *runtime.Type, structName, fieldName string) (Decoder, error) { if typ == runtime.Type2RType(jsonNumberType) { return newNumberDecoder(structName, fieldName, func(p unsafe.Pointer, v json.Number) { *(*json.Number)(p) = v }), nil } return newStringDecoder(structName, fieldName), nil } func compileBool(structName, fieldName string) (Decoder, error) { return newBoolDecoder(structName, fieldName), nil } func compileBytes(typ *runtime.Type, structName, fieldName string) (Decoder, error) { return newBytesDecoder(typ, structName, fieldName), nil } func compileSlice(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) { elem := typ.Elem() decoder, err := compile(elem, structName, fieldName, structTypeToDecoder) if err != nil { return nil, err } return newSliceDecoder(decoder, elem, elem.Size(), structName, fieldName), nil } func compileArray(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) { elem := typ.Elem() decoder, err := compile(elem, structName, fieldName, structTypeToDecoder) if err != nil { return nil, err } return newArrayDecoder(decoder, elem, typ.Len(), structName, fieldName), nil } func compileMap(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) { keyDec, err := compileMapKey(typ.Key(), structName, fieldName, structTypeToDecoder) if err != nil { return nil, err } valueDec, err := compile(typ.Elem(), structName, fieldName, structTypeToDecoder) if err != nil { return nil, err } return newMapDecoder(typ, typ.Key(), keyDec, typ.Elem(), valueDec, structName, fieldName), nil } func compileInterface(typ *runtime.Type, structName, fieldName string) (Decoder, error) { return newInterfaceDecoder(typ, structName, fieldName), nil } func compileFunc(typ *runtime.Type, strutName, fieldName string) (Decoder, error) { return newFuncDecoder(typ, strutName, fieldName), nil } func removeConflictFields(fieldMap map[string]*structFieldSet, conflictedMap map[string]struct{}, dec *structDecoder, field reflect.StructField) { 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: field.Offset + v.offset, isTaggedKey: v.isTaggedKey, key: k, keyLen: int64(len(k)), } fieldMap[k] = fieldSet lower := strings.ToLower(k) if _, exists := fieldMap[lower]; !exists { fieldMap[lower] = fieldSet } continue } if set.isTaggedKey { if v.isTaggedKey { // conflict tag key delete(fieldMap, k) delete(fieldMap, strings.ToLower(k)) conflictedMap[k] = struct{}{} conflictedMap[strings.ToLower(k)] = struct{}{} } } else { if v.isTaggedKey { fieldSet := &structFieldSet{ dec: v.dec, offset: field.Offset + v.offset, isTaggedKey: v.isTaggedKey, key: k, keyLen: int64(len(k)), } fieldMap[k] = fieldSet lower := strings.ToLower(k) if _, exists := fieldMap[lower]; !exists { fieldMap[lower] = fieldSet } } else { // conflict tag key delete(fieldMap, k) delete(fieldMap, strings.ToLower(k)) conflictedMap[k] = struct{}{} conflictedMap[strings.ToLower(k)] = struct{}{} } } } } func compileStruct(typ *runtime.Type, structName, fieldName string, structTypeToDecoder map[uintptr]Decoder) (Decoder, error) { fieldNum := typ.NumField() conflictedMap := map[string]struct{}{} fieldMap := map[string]*structFieldSet{} typeptr := uintptr(unsafe.Pointer(typ)) if dec, exists := structTypeToDecoder[typeptr]; exists { return dec, nil } structDec := newStructDecoder(structName, fieldName, fieldMap) structTypeToDecoder[typeptr] = structDec structName = typ.Name() for i := 0; i < fieldNum; i++ { field := typ.Field(i) if runtime.IsIgnoredStructField(field) { continue } isUnexportedField := unicode.IsLower([]rune(field.Name)[0]) tag := runtime.StructTagFromField(field) dec, err := compile(runtime.Type2RType(field.Type), structName, field.Name, structTypeToDecoder) if err != nil { return nil, err } if field.Anonymous && !tag.IsTaggedKey { if stDec, ok := dec.(*structDecoder); ok { if runtime.Type2RType(field.Type) == typ { // recursive definition continue } removeConflictFields(fieldMap, conflictedMap, stDec, field) } else if pdec, ok := dec.(*ptrDecoder); ok { contentDec := pdec.contentDecoder() if pdec.typ == typ { // recursive definition continue } var fieldSetErr error if isUnexportedField { fieldSetErr = fmt.Errorf( "json: cannot set embedded pointer to unexported struct: %v", field.Type.Elem(), ) } 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: field.Offset, isTaggedKey: v.isTaggedKey, key: k, keyLen: int64(len(k)), err: fieldSetErr, } fieldMap[k] = fieldSet lower := strings.ToLower(k) if _, exists := fieldMap[lower]; !exists { fieldMap[lower] = fieldSet } continue } if set.isTaggedKey { if v.isTaggedKey { // conflict tag key delete(fieldMap, k) delete(fieldMap, strings.ToLower(k)) conflictedMap[k] = struct{}{} conflictedMap[strings.ToLower(k)] = struct{}{} } } else { if v.isTaggedKey { fieldSet := &structFieldSet{ dec: newAnonymousFieldDecoder(pdec.typ, v.offset, v.dec), offset: field.Offset, isTaggedKey: v.isTaggedKey, key: k, keyLen: int64(len(k)), err: fieldSetErr, } fieldMap[k] = fieldSet lower := strings.ToLower(k) if _, exists := fieldMap[lower]; !exists { fieldMap[lower] = fieldSet } } else { // conflict tag key delete(fieldMap, k) delete(fieldMap, strings.ToLower(k)) conflictedMap[k] = struct{}{} conflictedMap[strings.ToLower(k)] = struct{}{} } } } } } } else { if tag.IsString && isStringTagSupportedType(runtime.Type2RType(field.Type)) { dec = newWrappedStringDecoder(runtime.Type2RType(field.Type), dec, structName, field.Name) } var key string if tag.Key != "" { key = tag.Key } else { key = field.Name } fieldSet := &structFieldSet{ dec: dec, offset: field.Offset, isTaggedKey: tag.IsTaggedKey, key: key, keyLen: int64(len(key)), } fieldMap[key] = fieldSet lower := strings.ToLower(key) if _, exists := fieldMap[lower]; !exists { fieldMap[lower] = fieldSet } } } delete(structTypeToDecoder, typeptr) structDec.tryOptimize() return structDec, nil } func implementsUnmarshalJSONType(typ *runtime.Type) bool { return typ.Implements(unmarshalJSONType) || typ.Implements(unmarshalJSONContextType) }