equal.go raw

   1  // Copyright 2019 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package proto
   6  
   7  import (
   8  	"reflect"
   9  
  10  	"google.golang.org/protobuf/reflect/protoreflect"
  11  	"google.golang.org/protobuf/runtime/protoiface"
  12  )
  13  
  14  // Equal reports whether two messages are equal,
  15  // by recursively comparing the fields of the message.
  16  //
  17  //   - Bytes fields are equal if they contain identical bytes.
  18  //     Empty bytes (regardless of nil-ness) are considered equal.
  19  //
  20  //   - Floating-point fields are equal if they contain the same value.
  21  //     Unlike the == operator, a NaN is equal to another NaN.
  22  //
  23  //   - Other scalar fields are equal if they contain the same value.
  24  //
  25  //   - Message fields are equal if they have
  26  //     the same set of populated known and extension field values, and
  27  //     the same set of unknown fields values.
  28  //
  29  //   - Lists are equal if they are the same length and
  30  //     each corresponding element is equal.
  31  //
  32  //   - Maps are equal if they have the same set of keys and
  33  //     the corresponding value for each key is equal.
  34  //
  35  // An invalid message is not equal to a valid message.
  36  // An invalid message is only equal to another invalid message of the
  37  // same type. An invalid message often corresponds to a nil pointer
  38  // of the concrete message type. For example, (*pb.M)(nil) is not equal
  39  // to &pb.M{}.
  40  // If two valid messages marshal to the same bytes under deterministic
  41  // serialization, then Equal is guaranteed to report true.
  42  func Equal(x, y Message) bool {
  43  	if x == nil || y == nil {
  44  		return x == nil && y == nil
  45  	}
  46  	if reflect.TypeOf(x).Kind() == reflect.Ptr && x == y {
  47  		// Avoid an expensive comparison if both inputs are identical pointers.
  48  		return true
  49  	}
  50  	mx := x.ProtoReflect()
  51  	my := y.ProtoReflect()
  52  	if mx.IsValid() != my.IsValid() {
  53  		return false
  54  	}
  55  
  56  	// Only one of the messages needs to implement the fast-path for it to work.
  57  	pmx := protoMethods(mx)
  58  	pmy := protoMethods(my)
  59  	if pmx != nil && pmy != nil && pmx.Equal != nil && pmy.Equal != nil {
  60  		return pmx.Equal(protoiface.EqualInput{MessageA: mx, MessageB: my}).Equal
  61  	}
  62  
  63  	vx := protoreflect.ValueOfMessage(mx)
  64  	vy := protoreflect.ValueOfMessage(my)
  65  	return vx.Equal(vy)
  66  }
  67