github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/amino/deep_copy.go (about) 1 package amino 2 3 import ( 4 "fmt" 5 "reflect" 6 ) 7 8 //---------------------------------------- 9 // DeepCopy 10 11 // Deeply copies an object. 12 // If anything implements `.DeepCopy() <any>` along the way, 13 // the result of that function will be used. 14 // Otherwise, if it implements `.MarshalAmino() (<any>, error)` and 15 // `.UnmarshalAmino(<any>) error`, the pair will be used to copy. 16 // If .MarshalAmino() or .UnmarshalAmino() returns an error, this 17 // function will panic. 18 func DeepCopy(o interface{}) (r interface{}) { 19 if o == nil { 20 return nil 21 } 22 src := reflect.ValueOf(o) 23 dst := reflect.New(src.Type()).Elem() 24 deepCopy(src, dst) 25 return dst.Interface() 26 } 27 28 // Like DeepCopy, but returns a pointer to the copy. 29 func DeepCopyToPtr(o interface{}) (ptr interface{}) { 30 if o == nil { 31 return nil 32 } 33 src := reflect.ValueOf(o) 34 dst := reflect.New(src.Type()) 35 elm := dst.Elem() 36 deepCopy(src, elm) 37 return dst.Interface() 38 } 39 40 func deepCopy(src, dst reflect.Value) { 41 if isNil(src) { 42 return 43 } 44 if callDeepCopy(src, dst) { 45 return 46 } 47 if callAminoCopy(src, dst) { 48 return 49 } 50 _deepCopy(src, dst) 51 } 52 53 func _deepCopy(src, dst reflect.Value) { 54 switch src.Kind() { 55 case reflect.Ptr: 56 cpy := reflect.New(src.Type().Elem()) 57 _deepCopy(src.Elem(), cpy.Elem()) 58 dst.Set(cpy) 59 return 60 61 case reflect.Interface: 62 cpy := reflect.New(src.Elem().Type()) 63 deepCopy(src.Elem(), cpy.Elem()) 64 dst.Set(cpy.Elem()) 65 return 66 67 case reflect.Array: 68 switch src.Type().Elem().Kind() { 69 case reflect.Int64, reflect.Int32, reflect.Int16, 70 reflect.Int8, reflect.Int, reflect.Uint64, 71 reflect.Uint32, reflect.Uint16, reflect.Uint8, 72 reflect.Uint, reflect.Bool, reflect.Float64, 73 reflect.Float32, reflect.String: 74 75 reflect.Copy(dst, src) 76 return 77 default: 78 for i := 0; i < src.Type().Len(); i++ { 79 esrc := src.Index(i) 80 edst := dst.Index(i) 81 deepCopy(esrc, edst) 82 } 83 return 84 } 85 86 case reflect.Slice: 87 switch src.Type().Elem().Kind() { 88 case reflect.Int64, reflect.Int32, reflect.Int16, 89 reflect.Int8, reflect.Int, reflect.Uint64, 90 reflect.Uint32, reflect.Uint16, reflect.Uint8, 91 reflect.Uint, reflect.Bool, reflect.Float64, 92 reflect.Float32, reflect.String: 93 94 cpy := reflect.MakeSlice( 95 src.Type(), src.Len(), src.Len()) 96 reflect.Copy(cpy, src) 97 dst.Set(src) 98 return 99 default: 100 cpy := reflect.MakeSlice( 101 src.Type(), src.Len(), src.Len()) 102 for i := 0; i < src.Len(); i++ { 103 esrc := src.Index(i) 104 ecpy := cpy.Index(i) 105 deepCopy(esrc, ecpy) 106 } 107 dst.Set(src) 108 return 109 } 110 111 case reflect.Struct: 112 switch src.Type() { 113 case timeType: 114 dst.Set(src) 115 return 116 default: 117 for i := 0; i < src.NumField(); i++ { 118 if !isExported(src.Type().Field(i)) { 119 continue // field is unexported 120 } 121 srcf := src.Field(i) 122 dstf := dst.Field(i) 123 deepCopy(srcf, dstf) 124 } 125 return 126 } 127 128 case reflect.Map: 129 cpy := reflect.MakeMapWithSize(src.Type(), src.Len()) 130 keys := src.MapKeys() 131 for _, key := range keys { 132 val := src.MapIndex(key) 133 cpy.SetMapIndex(key, val) 134 } 135 dst.Set(cpy) 136 return 137 138 // Primitive types 139 case reflect.Int64, reflect.Int32, reflect.Int16, 140 reflect.Int8, reflect.Int: 141 dst.SetInt(src.Int()) 142 case reflect.Uint64, reflect.Uint32, reflect.Uint16, 143 reflect.Uint8, reflect.Uint: 144 dst.SetUint(src.Uint()) 145 case reflect.Bool: 146 dst.SetBool(src.Bool()) 147 case reflect.Float64, reflect.Float32: 148 dst.SetFloat(src.Float()) 149 case reflect.String: 150 dst.SetString(src.String()) 151 152 default: 153 panic(fmt.Sprintf("unsupported type %v", src.Kind())) 154 } 155 } 156 157 //---------------------------------------- 158 // misc. 159 160 // Call .DeepCopy() method if possible. 161 func callDeepCopy(src, dst reflect.Value) bool { 162 dc := src.MethodByName("DeepCopy") 163 if !dc.IsValid() { 164 return false 165 } 166 if dc.Type().NumIn() != 0 { 167 return false 168 } 169 if dc.Type().NumOut() != 1 { 170 return false 171 } 172 otype := dc.Type().Out(0) 173 if dst.Kind() == reflect.Ptr && 174 dst.Type().Elem() == otype { 175 cpy := reflect.New(dst.Type().Elem()) 176 out := dc.Call(nil)[0] 177 cpy.Elem().Set(out) 178 dst.Set(cpy) 179 return true 180 } 181 if dst.Type() == otype { 182 out := dc.Call(nil)[0] 183 dst.Set(out) 184 return true 185 } 186 return false 187 } 188 189 // Call .MarshalAmino() and .UnmarshalAmino to copy if possible. 190 // Panics if .MarshalAmino() or .UnmarshalAmino() return an error. 191 // CONTRACT: src and dst are of equal types. 192 func callAminoCopy(src, dst reflect.Value) bool { 193 if src.Type() != dst.Type() { 194 panic("should not happen") 195 } 196 switch { 197 case src.Kind() == reflect.Ptr: 198 cpy := reflect.New(src.Type().Elem()) 199 dst.Set(cpy) 200 case src.CanAddr(): 201 if !dst.CanAddr() { 202 panic("should not happen") 203 } 204 src = src.Addr() 205 dst = dst.Addr() 206 default: 207 return false 208 } 209 if !canAminoCopy(src) { 210 return false 211 } 212 ma := src.MethodByName("MarshalAmino") 213 ua := dst.MethodByName("UnmarshalAmino") 214 outs := ma.Call(nil) 215 repr, err := outs[0], outs[1] 216 if !err.IsNil() { 217 panic(err.Interface()) 218 } 219 outs = ua.Call([]reflect.Value{repr}) 220 err = outs[0] 221 if !err.IsNil() { 222 panic(err.Interface()) 223 } 224 return true 225 } 226 227 func canAminoCopy(rv reflect.Value) bool { 228 if !rv.MethodByName("UnmarshalAmino").IsValid() { 229 return false 230 } 231 ua := rv.MethodByName("UnmarshalAmino") 232 if !ua.IsValid() { 233 return false 234 } 235 if ua.Type().NumIn() != 1 { 236 return false 237 } 238 if ua.Type().NumOut() != 1 { 239 return false 240 } 241 if ua.Type().Out(0) != errorType { 242 return false 243 } 244 ma := rv.MethodByName("MarshalAmino") 245 if !ma.IsValid() { 246 return false 247 } 248 if ma.Type().NumIn() != 0 { 249 return false 250 } 251 if ma.Type().NumOut() != 2 { 252 return false 253 } 254 if ma.Type().Out(1) != errorType { 255 return false 256 } 257 if ua.Type().In(0) != ma.Type().Out(0) { 258 return false 259 } 260 return true 261 }