-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
hui.wang
committed
Jan 25, 2022
1 parent
e47d024
commit 307bdf2
Showing
4 changed files
with
123 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package xutil | ||
|
||
import ( | ||
"reflect" | ||
"time" | ||
) | ||
|
||
// DeepCopyInterface for delegating copy process to type | ||
type DeepCopyInterface interface { | ||
DeepCopy() interface{} | ||
} | ||
|
||
// DeepCopy creates a deep copy of whatever is passed to it and returns the copy | ||
// in an interface{}. The returned value will need to be asserted to the | ||
// correct type. | ||
func DeepCopy(src interface{}) interface{} { | ||
if src == nil { | ||
return nil | ||
} | ||
|
||
// Make the interface a reflect.Value | ||
original := reflect.ValueOf(src) | ||
|
||
// Make a copy of the same type as the original. | ||
cpy := reflect.New(original.Type()).Elem() | ||
|
||
// Recursively copy the original. | ||
copyRecursive(original, cpy) | ||
|
||
// Return the copy as an interface. | ||
return cpy.Interface() | ||
} | ||
|
||
// copyRecursive does the actual copying of the interface. It currently has | ||
// limited support for what it can handle. Add as needed. | ||
func copyRecursive(original, cpy reflect.Value) { | ||
// check for implement deepcopy.Interface | ||
if original.CanInterface() { | ||
if copier, ok := original.Interface().(DeepCopyInterface); ok { | ||
cpy.Set(reflect.ValueOf(copier.DeepCopy())) | ||
return | ||
} | ||
} | ||
|
||
// handle according to original's Kind | ||
switch original.Kind() { | ||
case reflect.Ptr: | ||
// Get the actual value being pointed to. | ||
originalValue := original.Elem() | ||
|
||
// if it isn't valid, return. | ||
if !originalValue.IsValid() { | ||
return | ||
} | ||
cpy.Set(reflect.New(originalValue.Type())) | ||
copyRecursive(originalValue, cpy.Elem()) | ||
|
||
case reflect.Interface: | ||
// If this is a nil, don't do anything | ||
if original.IsNil() { | ||
return | ||
} | ||
// Get the value for the interface, not the pointer. | ||
originalValue := original.Elem() | ||
|
||
// Get the value by calling Elem(). | ||
copyValue := reflect.New(originalValue.Type()).Elem() | ||
copyRecursive(originalValue, copyValue) | ||
cpy.Set(copyValue) | ||
|
||
case reflect.Struct: | ||
t, ok := original.Interface().(time.Time) | ||
if ok { | ||
cpy.Set(reflect.ValueOf(t)) | ||
return | ||
} | ||
// Go through each field of the struct and copy it. | ||
for i := 0; i < original.NumField(); i++ { | ||
// The Type's StructField for a given field is checked to see if StructField.PkgPath | ||
// is set to determine if the field is exported or not because CanSet() returns false | ||
// for settable fields. I'm not sure why. -mohae | ||
if original.Type().Field(i).PkgPath != "" { | ||
continue | ||
} | ||
copyRecursive(original.Field(i), cpy.Field(i)) | ||
} | ||
|
||
case reflect.Slice: | ||
if original.IsNil() { | ||
return | ||
} | ||
// Make a new slice and copy each element. | ||
cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap())) | ||
for i := 0; i < original.Len(); i++ { | ||
copyRecursive(original.Index(i), cpy.Index(i)) | ||
} | ||
|
||
case reflect.Map: | ||
if original.IsNil() { | ||
return | ||
} | ||
cpy.Set(reflect.MakeMap(original.Type())) | ||
for _, key := range original.MapKeys() { | ||
originalValue := original.MapIndex(key) | ||
copyValue := reflect.New(originalValue.Type()).Elem() | ||
copyRecursive(originalValue, copyValue) | ||
copyKey := DeepCopy(key.Interface()) | ||
cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue) | ||
} | ||
|
||
default: | ||
cpy.Set(original) | ||
} | ||
} |