goserver_sync/core/utils/clone.go

135 lines
3.1 KiB
Go

package utils
import (
"reflect"
)
//unsupport [Complex64,Complex128,Chan,Func,Interface,UnsafePointer]
func Clone(src interface{}) (dst interface{}) {
if !isStructPtr(reflect.TypeOf(src)) {
return nil
}
sv := reflect.Indirect(reflect.ValueOf(src))
if !sv.IsValid() {
return nil
}
st := sv.Type()
dv := reflect.New(st)
if !dv.IsValid() {
return nil
}
deepCopy(sv, dv.Elem(), st)
return dv.Interface()
}
func deepCopy(src, dst reflect.Value, t reflect.Type) {
switch src.Kind() {
case reflect.String:
dst.SetString(src.String())
case reflect.Bool:
dst.SetBool(src.Bool())
case reflect.Float32, reflect.Float64:
dst.SetFloat(src.Float())
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
dst.SetInt(src.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
dst.SetUint(src.Uint())
case reflect.Map:
deepCopyMap(src, dst, t)
case reflect.Array, reflect.Slice:
deepCopySlice(src, dst, t)
case reflect.Struct:
deepCopyStruct(src, dst, t)
}
}
func deepCopyMap(src, dst reflect.Value, t reflect.Type) {
for _, key := range src.MapKeys() {
var nkey, nval reflect.Value
if key.IsValid() && key.CanSet() {
if key.Kind() == reflect.Ptr {
nkey = reflect.New(key.Elem().Type())
} else {
nkey = reflect.New(key.Type())
nkey = reflect.Indirect(nkey)
}
s := reflect.Indirect(key)
d := reflect.Indirect(nkey)
if s.IsValid() && d.IsValid() {
tt := s.Type()
deepCopy(s, d, tt)
}
} else {
nkey = key
}
if val := src.MapIndex(key); val.IsValid() && val.CanSet() {
if val.Kind() == reflect.Ptr {
nval = reflect.New(val.Elem().Type())
} else {
nval = reflect.New(val.Type())
nval = reflect.Indirect(nval)
}
s := reflect.Indirect(val)
d := reflect.Indirect(nval)
if s.IsValid() && d.IsValid() {
tt := s.Type()
deepCopy(s, d, tt)
}
} else {
nval = val
}
dst.SetMapIndex(nkey, nval)
}
}
func deepCopySlice(src, dst reflect.Value, t reflect.Type) {
for i := 0; i < src.Len(); i++ {
sf := src.Index(i)
df := dst.Index(i)
if sf.Kind() == reflect.Ptr {
df = reflect.New(sf.Elem().Type())
dst.Index(i).Set(df)
}
sf = reflect.Indirect(sf)
df = reflect.Indirect(df)
if sf.IsValid() && df.IsValid() {
tt := sf.Type()
deepCopy(sf, df, tt)
}
}
}
func deepCopyStruct(src, dst reflect.Value, t reflect.Type) {
for i := 0; i < t.NumField(); i++ {
sv := src.Field(i)
if sv.CanSet() && sv.IsValid() {
switch sv.Kind() {
case reflect.Ptr:
if !sv.IsNil() {
dst.Field(i).Set(reflect.New(sv.Elem().Type()))
}
case reflect.Array, reflect.Slice:
if !sv.IsNil() {
dst.Field(i).Set(reflect.MakeSlice(sv.Type(), sv.Len(), sv.Cap()))
}
case reflect.Map:
if !sv.IsNil() {
dst.Field(i).Set(reflect.MakeMap(sv.Type()))
}
}
sf := reflect.Indirect(sv)
df := reflect.Indirect(dst.Field(i))
if sf.IsValid() && df.IsValid() {
tt := sf.Type()
deepCopy(sf, df, tt)
}
}
}
}
func isStructPtr(t reflect.Type) bool {
return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
}