mirror of
https://github.com/gusaul/grpcox.git
synced 2025-01-26 06:14:40 +00:00
182 lines
3.7 KiB
Go
182 lines
3.7 KiB
Go
package dynamic
|
|
|
|
import (
|
|
"bytes"
|
|
"reflect"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
|
|
"github.com/jhump/protoreflect/desc"
|
|
)
|
|
|
|
// Equal returns true if the given two dynamic messages are equal. Two messages are equal when they
|
|
// have the same message type and same fields set to equal values. For proto3 messages, fields set
|
|
// to their zero value are considered unset.
|
|
func Equal(a, b *Message) bool {
|
|
if a.md.GetFullyQualifiedName() != b.md.GetFullyQualifiedName() {
|
|
return false
|
|
}
|
|
if len(a.values) != len(b.values) {
|
|
return false
|
|
}
|
|
if len(a.unknownFields) != len(b.unknownFields) {
|
|
return false
|
|
}
|
|
for tag, aval := range a.values {
|
|
bval, ok := b.values[tag]
|
|
if !ok {
|
|
return false
|
|
}
|
|
if !fieldsEqual(aval, bval) {
|
|
return false
|
|
}
|
|
}
|
|
for tag, au := range a.unknownFields {
|
|
bu, ok := b.unknownFields[tag]
|
|
if !ok {
|
|
return false
|
|
}
|
|
if len(au) != len(bu) {
|
|
return false
|
|
}
|
|
for i, aval := range au {
|
|
bval := bu[i]
|
|
if aval.Encoding != bval.Encoding {
|
|
return false
|
|
}
|
|
if aval.Encoding == proto.WireBytes || aval.Encoding == proto.WireStartGroup {
|
|
if !bytes.Equal(aval.Contents, bval.Contents) {
|
|
return false
|
|
}
|
|
} else if aval.Value != bval.Value {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
// all checks pass!
|
|
return true
|
|
}
|
|
|
|
func fieldsEqual(aval, bval interface{}) bool {
|
|
arv := reflect.ValueOf(aval)
|
|
brv := reflect.ValueOf(bval)
|
|
if arv.Type() != brv.Type() {
|
|
// it is possible that one is a dynamic message and one is not
|
|
apm, ok := aval.(proto.Message)
|
|
if !ok {
|
|
return false
|
|
}
|
|
bpm, ok := bval.(proto.Message)
|
|
if !ok {
|
|
return false
|
|
}
|
|
if !MessagesEqual(apm, bpm) {
|
|
return false
|
|
}
|
|
} else {
|
|
switch arv.Kind() {
|
|
case reflect.Ptr:
|
|
apm, ok := aval.(proto.Message)
|
|
if !ok {
|
|
// Don't know how to compare pointer values that aren't messages!
|
|
// Maybe this should panic?
|
|
return false
|
|
}
|
|
bpm := bval.(proto.Message) // we know it will succeed because we know a and b have same type
|
|
if !MessagesEqual(apm, bpm) {
|
|
return false
|
|
}
|
|
|
|
case reflect.Map:
|
|
if !mapsEqual(arv, brv) {
|
|
return false
|
|
}
|
|
|
|
case reflect.Slice:
|
|
if arv.Type() == typeOfBytes {
|
|
if !bytes.Equal(aval.([]byte), bval.([]byte)) {
|
|
return false
|
|
}
|
|
} else {
|
|
if !slicesEqual(arv, brv) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
default:
|
|
if aval != bval {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func mapsEqual(a, b reflect.Value) bool {
|
|
if a.Len() != b.Len() {
|
|
return false
|
|
}
|
|
for _, k := range a.MapKeys() {
|
|
av := a.MapIndex(k)
|
|
bv := b.MapIndex(k)
|
|
if !bv.IsValid() {
|
|
return false
|
|
}
|
|
if !fieldsEqual(av.Interface(), bv.Interface()) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func slicesEqual(a, b reflect.Value) bool {
|
|
if a.Len() != b.Len() {
|
|
return false
|
|
}
|
|
for i := 0; i < a.Len(); i++ {
|
|
ai := a.Index(i)
|
|
bi := b.Index(i)
|
|
if !fieldsEqual(ai.Interface(), bi.Interface()) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// MessagesEqual returns true if the given two messages are equal. Use this instead of proto.Equal
|
|
// when one or both of the messages might be a dynamic message.
|
|
func MessagesEqual(a, b proto.Message) bool {
|
|
da, aok := a.(*Message)
|
|
db, bok := b.(*Message)
|
|
// Both dynamic messages
|
|
if aok && bok {
|
|
return Equal(da, db)
|
|
}
|
|
// Neither dynamic messages
|
|
if !aok && !bok {
|
|
return proto.Equal(a, b)
|
|
}
|
|
// Mixed
|
|
if aok {
|
|
md, err := desc.LoadMessageDescriptorForMessage(b)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
db = NewMessageWithMessageFactory(md, da.mf)
|
|
if db.ConvertFrom(b) != nil {
|
|
return false
|
|
}
|
|
return Equal(da, db)
|
|
} else {
|
|
md, err := desc.LoadMessageDescriptorForMessage(a)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
da = NewMessageWithMessageFactory(md, db.mf)
|
|
if da.ConvertFrom(a) != nil {
|
|
return false
|
|
}
|
|
return Equal(da, db)
|
|
}
|
|
}
|