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  }