blob: ef1b37050f6d876dbbdfcca7b2c482b4fec87e1c [file] [log] [blame]
//go:build go1.12
// +build go1.12
package dynamic
import (
"github.com/jhump/protoreflect/desc"
"reflect"
)
// With Go 1.12 and above, we can use reflect.Value.MapRange to iterate
// over maps more efficiently than using reflect.Value.MapKeys.
func mapsEqual(a, b reflect.Value) bool {
if a.Len() != b.Len() {
return false
}
if a.Len() == 0 && b.Len() == 0 {
// Optimize the case where maps are frequently empty
return true
}
iter := a.MapRange()
for iter.Next() {
k := iter.Key()
av := iter.Value()
bv := b.MapIndex(k)
if !bv.IsValid() {
return false
}
if !fieldsEqual(av.Interface(), bv.Interface()) {
return false
}
}
return true
}
func validFieldValueForMapField(fd *desc.FieldDescriptor, val reflect.Value) (interface{}, error) {
// make a defensive copy while we check the contents
// (also converts to map[interface{}]interface{} if it's some other type)
keyField := fd.GetMessageType().GetFields()[0]
valField := fd.GetMessageType().GetFields()[1]
m := map[interface{}]interface{}{}
iter := val.MapRange()
for iter.Next() {
k := iter.Key()
if k.Kind() == reflect.Interface {
// unwrap it
k = reflect.ValueOf(k.Interface())
}
kk, err := validElementFieldValueForRv(keyField, k, false)
if err != nil {
return nil, err
}
v := iter.Value()
if v.Kind() == reflect.Interface {
// unwrap it
v = reflect.ValueOf(v.Interface())
}
vv, err := validElementFieldValueForRv(valField, v, true)
if err != nil {
return nil, err
}
m[kk] = vv
}
return m, nil
}
func canConvertMap(src reflect.Value, target reflect.Type) bool {
kt := target.Key()
vt := target.Elem()
iter := src.MapRange()
for iter.Next() {
if !canConvert(iter.Key(), kt) {
return false
}
if !canConvert(iter.Value(), vt) {
return false
}
}
return true
}
func mergeMapVal(src, target reflect.Value, targetType reflect.Type, deterministic bool) error {
tkt := targetType.Key()
tvt := targetType.Elem()
iter := src.MapRange()
for iter.Next() {
k := iter.Key()
v := iter.Value()
skt := k.Type()
svt := v.Type()
var nk, nv reflect.Value
if tkt == skt {
nk = k
} else if tkt.Kind() == reflect.Ptr && tkt.Elem() == skt {
nk = k.Addr()
} else {
nk = reflect.New(tkt).Elem()
if err := mergeVal(k, nk, deterministic); err != nil {
return err
}
}
if tvt == svt {
nv = v
} else if tvt.Kind() == reflect.Ptr && tvt.Elem() == svt {
nv = v.Addr()
} else {
nv = reflect.New(tvt).Elem()
if err := mergeVal(v, nv, deterministic); err != nil {
return err
}
}
if target.IsNil() {
target.Set(reflect.MakeMap(targetType))
}
target.SetMapIndex(nk, nv)
}
return nil
}
func mergeMapField(m *Message, fd *desc.FieldDescriptor, rv reflect.Value) error {
iter := rv.MapRange()
for iter.Next() {
k := iter.Key()
v := iter.Value()
if k.Kind() == reflect.Interface && !k.IsNil() {
k = k.Elem()
}
if v.Kind() == reflect.Interface && !v.IsNil() {
v = v.Elem()
}
if err := m.putMapField(fd, k.Interface(), v.Interface()); err != nil {
return err
}
}
return nil
}