github.com/gogf/gf/v2@v2.7.4/util/gconv/internal/structcache/structcache_cached.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package structcache 8 9 import ( 10 "reflect" 11 "sync" 12 "time" 13 14 "github.com/gogf/gf/v2/internal/utils" 15 "github.com/gogf/gf/v2/os/gtime" 16 "github.com/gogf/gf/v2/util/gtag" 17 ) 18 19 // CommonConverter holds some converting functions of common types for internal usage. 20 type CommonConverter struct { 21 Int64 func(any interface{}) int64 22 Uint64 func(any interface{}) uint64 23 String func(any interface{}) string 24 Float32 func(any interface{}) float32 25 Float64 func(any interface{}) float64 26 Time func(any interface{}, format ...string) time.Time 27 GTime func(any interface{}, format ...string) *gtime.Time 28 Bytes func(any interface{}) []byte 29 Bool func(any interface{}) bool 30 } 31 32 var ( 33 // map[reflect.Type]*CachedStructInfo 34 cachedStructsInfoMap = sync.Map{} 35 36 // localCommonConverter holds some converting functions of common types for internal usage. 37 localCommonConverter CommonConverter 38 ) 39 40 // RegisterCommonConverter registers the CommonConverter for local usage. 41 func RegisterCommonConverter(commonConverter CommonConverter) { 42 localCommonConverter = commonConverter 43 } 44 45 // GetCachedStructInfo retrieves or parses and returns a cached info for certain struct type. 46 // The given `structType` should be type of struct. 47 func GetCachedStructInfo(structType reflect.Type, priorityTag string) *CachedStructInfo { 48 if structType.Kind() != reflect.Struct { 49 return nil 50 } 51 // check if it has been cached. 52 cachedStructInfo, ok := getCachedConvertStructInfo(structType) 53 if ok { 54 // directly returns the cached struct info if already exists. 55 return cachedStructInfo 56 } 57 58 // else create one. 59 60 // it parses and generates a cache info for given struct type. 61 cachedStructInfo = &CachedStructInfo{ 62 tagOrFiledNameToFieldInfoMap: make(map[string]*CachedFieldInfo), 63 } 64 var ( 65 priorityTagArray []string 66 parentIndex = make([]int, 0) 67 ) 68 if priorityTag != "" { 69 priorityTagArray = append(utils.SplitAndTrim(priorityTag, ","), gtag.StructTagPriority...) 70 } else { 71 priorityTagArray = gtag.StructTagPriority 72 } 73 parseStructToCachedStructInfo(structType, parentIndex, cachedStructInfo, priorityTagArray) 74 storeCachedStructInfo(structType, cachedStructInfo) 75 return cachedStructInfo 76 } 77 78 func storeCachedStructInfo(structType reflect.Type, cachedStructInfo *CachedStructInfo) { 79 // Temporarily enabled as an experimental feature 80 cachedStructsInfoMap.Store(structType, cachedStructInfo) 81 } 82 83 func getCachedConvertStructInfo(structType reflect.Type) (*CachedStructInfo, bool) { 84 // Temporarily enabled as an experimental feature 85 v, ok := cachedStructsInfoMap.Load(structType) 86 if ok { 87 return v.(*CachedStructInfo), ok 88 } 89 return nil, false 90 } 91 92 // parseStructToCachedStructInfo parses given struct reflection type and stores its fields info into given CachedStructInfo. 93 // It stores nothing into CachedStructInfo if given struct reflection type has no fields. 94 func parseStructToCachedStructInfo( 95 structType reflect.Type, 96 fieldIndexes []int, 97 cachedStructInfo *CachedStructInfo, 98 priorityTagArray []string, 99 ) { 100 var ( 101 fieldName string 102 structField reflect.StructField 103 fieldType reflect.Type 104 ) 105 // TODO: 106 // Check if the structure has already been cached in the cache. 107 // If it has been cached, some information can be reused, 108 // but the [FieldIndex] needs to be reset. 109 // We will not implement it temporarily because it is somewhat complex 110 for i := 0; i < structType.NumField(); i++ { 111 structField = structType.Field(i) 112 fieldType = structField.Type 113 fieldName = structField.Name 114 // Only do converting to public attributes. 115 if !utils.IsLetterUpper(fieldName[0]) { 116 continue 117 } 118 119 copyFieldIndexes := make([]int, len(fieldIndexes)) 120 copy(copyFieldIndexes, fieldIndexes) 121 122 // normal basic attributes. 123 if structField.Anonymous { 124 // handle struct attributes, it might be struct/*struct embedded.. 125 if fieldType.Kind() == reflect.Ptr { 126 fieldType = fieldType.Elem() 127 } 128 if fieldType.Kind() != reflect.Struct { 129 continue 130 } 131 // Skip the embedded structure of the 0 field, 132 if fieldType.NumField() == 0 { 133 continue 134 } 135 if structField.Tag != "" { 136 // Do not add anonymous structures without tags 137 cachedStructInfo.AddField(structField, append(copyFieldIndexes, i), priorityTagArray) 138 } 139 parseStructToCachedStructInfo(fieldType, append(copyFieldIndexes, i), cachedStructInfo, priorityTagArray) 140 continue 141 } 142 // Do not directly use append(fieldIndexes, i) 143 // When the structure is nested deeply, it may lead to bugs, 144 // which are caused by the slice expansion mechanism 145 // So it is necessary to allocate a separate index for each field 146 // See details https://github.com/gogf/gf/issues/3789 147 cachedStructInfo.AddField(structField, append(copyFieldIndexes, i), priorityTagArray) 148 } 149 }