You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Open-IM-Server/internal/api2rpc/copy.go

275 lines
6.5 KiB

package common
import (
"fmt"
"google.golang.org/protobuf/types/known/wrapperspb"
"reflect"
)
func CopyAny(from, to interface{}) {
copyAny(reflect.ValueOf(from), reflect.Indirect(reflect.ValueOf(to)))
}
func copyAny(from, to reflect.Value) {
if !to.CanSet() {
return
}
if isBaseNil(from) {
return
}
if isBaseNil(to) {
to.Set(getBaseZeroValue(to.Type()))
}
btFrom := baseType(from.Type())
btTo := baseType(to.Type())
if btTo.Kind() == reflect.Interface || typeEq(btFrom, btTo) {
setBaseValue(from, to)
return
}
if _, ok := wrapType[btTo.String()]; ok { // string -> wrapperspb.StringValue
val := reflect.New(btTo).Elem()
copyAny(from, val.FieldByName("Value"))
setBaseValue(val, to)
return
}
if _, ok := wrapType[btFrom.String()]; ok { // wrapperspb.StringValue -> string
copyAny(baseValue(from).FieldByName("Value"), to)
return
}
if btFrom.Kind() == reflect.Struct && btTo.Kind() == reflect.Struct {
copyStruct(baseValue(from), baseValue(to))
return
}
if btFrom.Kind() == reflect.Slice && btTo.Kind() == reflect.Slice {
copySlice(baseValue(from), baseValue(to))
return
}
if btFrom.Kind() == reflect.Map && btTo.Kind() == reflect.Map {
copyMap(baseValue(from), baseValue(to))
return
}
if btTo.Kind() == reflect.String {
if isBaseNil(to) {
to.Set(getBaseZeroValue(baseType(to.Type())))
}
setBaseValue(reflect.ValueOf(toString(from)), to)
return
}
if toNumber(from, to) {
return
}
}
func setBaseValue(from, to reflect.Value) {
if isBaseNil(from) {
return
}
var l int
t := to.Type()
for t.Kind() == reflect.Ptr {
l++
t = t.Elem()
}
v := baseValue(from)
for i := 0; i < l; i++ {
t := reflect.New(v.Type())
t.Elem().Set(v)
v = t
}
to.Set(v)
}
func getBaseZeroValue(t reflect.Type) reflect.Value {
var l int
for t.Kind() == reflect.Ptr {
l++
t = t.Elem()
}
v := reflect.New(t)
if l == 0 {
v = v.Elem()
} else {
for i := 1; i < l; i++ {
t := reflect.New(v.Type())
t.Elem().Set(v)
v = t
}
}
r := reflect.New(v.Type()).Elem()
r.Set(v)
return r
}
func isBaseNil(v reflect.Value) bool {
for {
switch v.Kind() {
case reflect.Ptr:
v = v.Elem()
case reflect.Invalid:
return true
default:
return isNil(v)
}
}
}
func baseType(t reflect.Type) reflect.Type {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
return t
}
func typeEq(t1, t2 reflect.Type) bool {
return t1.String() == t2.String()
}
func isNil(value reflect.Value) bool {
switch value.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
return value.IsNil()
}
return false
}
func baseValue(value reflect.Value) reflect.Value {
for value.Kind() == reflect.Ptr {
value = value.Elem()
}
return value
}
func copyStruct(from, to reflect.Value) {
toType := to.Type()
fromType := from.Type()
n := to.NumField()
for i := 0; i < n; i++ {
toFieldType := toType.Field(i)
if _, found := fromType.FieldByName(toFieldType.Name); !found {
continue
}
copyAny(from.FieldByName(toFieldType.Name), to.Field(i))
}
}
func copySlice(from, to reflect.Value) {
size := from.Len()
temp := reflect.MakeSlice(to.Type(), 0, size)
elemTo := to.Type().Elem()
for i := 0; i < size; i++ {
var itemTo reflect.Value
if item := from.Index(i); isBaseNil(item) {
itemTo = reflect.Zero(elemTo)
} else {
itemTo = getBaseZeroValue(elemTo)
copyAny(from.Index(i), itemTo)
}
temp = reflect.Append(temp, itemTo)
}
to.Set(temp)
}
func copyMap(from, to reflect.Value) {
to.Set(reflect.MakeMap(to.Type()))
toTypeKey := to.Type().Key()
toTypeVal := to.Type().Elem()
for r := from.MapRange(); r.Next(); {
key := getBaseZeroValue(toTypeKey)
copyAny(r.Key(), key)
var val reflect.Value
fVal := r.Value()
if isBaseNil(fVal) {
val = reflect.Zero(toTypeVal)
} else {
val = getBaseZeroValue(toTypeVal)
copyAny(fVal, val)
}
to.SetMapIndex(key, val)
}
}
func toString(value reflect.Value) string {
if value.Kind() == reflect.Slice {
switch value.Type().String() {
case "[]uint8": // []byte -> []uint8
return string(value.Interface().([]uint8))
case "[]int32": // []rune -> []int32
return string(value.Interface().([]int32))
}
}
return fmt.Sprint(value.Interface())
}
func toNumber(from, to reflect.Value) bool {
initTo := func() {
if isBaseNil(to) {
to.Set(getBaseZeroValue(to.Type()))
}
}
switch baseValue(from).Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
switch baseValue(to).Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
initTo()
baseValue(to).SetInt(baseValue(from).Int())
return true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
initTo()
baseValue(to).SetUint(uint64(baseValue(from).Int()))
return true
case reflect.Float64, reflect.Float32:
initTo()
baseValue(to).SetFloat(float64(baseValue(from).Int()))
return true
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
switch baseValue(to).Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
initTo()
baseValue(to).SetInt(int64(baseValue(from).Uint()))
return true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
initTo()
baseValue(to).SetInt(int64(baseValue(from).Uint()))
return true
case reflect.Float64, reflect.Float32:
initTo()
baseValue(to).SetFloat(float64(baseValue(from).Uint()))
return true
}
case reflect.Float64, reflect.Float32:
switch baseValue(to).Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
initTo()
baseValue(to).SetInt(int64(baseValue(from).Float()))
return true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
initTo()
baseValue(to).SetUint(uint64(baseValue(from).Float()))
return true
case reflect.Float64, reflect.Float32:
initTo()
baseValue(to).SetFloat(baseValue(from).Float())
return true
}
}
return false
}
func typeName(v interface{}) string {
return reflect.TypeOf(v).String()
}
var wrapType = map[string]struct{}{
typeName(wrapperspb.DoubleValue{}): {},
typeName(wrapperspb.FloatValue{}): {},
typeName(wrapperspb.Int64Value{}): {},
typeName(wrapperspb.UInt64Value{}): {},
typeName(wrapperspb.Int32Value{}): {},
typeName(wrapperspb.UInt32Value{}): {},
typeName(wrapperspb.BoolValue{}): {},
typeName(wrapperspb.StringValue{}): {},
typeName(wrapperspb.BytesValue{}): {},
}