proto/equal: reduce equal scalar value allocation

Instead of lifting values to interface and then do equality test,
compare scalar values with defined types to reduce allocation
from interface.

Change-Id: Ieb777fbd1a48c83d896d0ebe6ad605433f44c16c
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/253100
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
Trust: Damien Neil <dneil@google.com>
Trust: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/proto/equal.go b/proto/equal.go
index 10902bd..4dba2b9 100644
--- a/proto/equal.go
+++ b/proto/equal.go
@@ -111,18 +111,31 @@
 
 // equalValue compares two singular values.
 func equalValue(fd pref.FieldDescriptor, x, y pref.Value) bool {
-	switch {
-	case fd.Message() != nil:
-		return equalMessage(x.Message(), y.Message())
-	case fd.Kind() == pref.BytesKind:
-		return bytes.Equal(x.Bytes(), y.Bytes())
-	case fd.Kind() == pref.FloatKind, fd.Kind() == pref.DoubleKind:
+	switch fd.Kind() {
+	case pref.BoolKind:
+		return x.Bool() == y.Bool()
+	case pref.EnumKind:
+		return x.Enum() == y.Enum()
+	case pref.Int32Kind, pref.Sint32Kind,
+		pref.Int64Kind, pref.Sint64Kind,
+		pref.Sfixed32Kind, pref.Sfixed64Kind:
+		return x.Int() == y.Int()
+	case pref.Uint32Kind, pref.Uint64Kind,
+		pref.Fixed32Kind, pref.Fixed64Kind:
+		return x.Uint() == y.Uint()
+	case pref.FloatKind, pref.DoubleKind:
 		fx := x.Float()
 		fy := y.Float()
 		if math.IsNaN(fx) || math.IsNaN(fy) {
 			return math.IsNaN(fx) && math.IsNaN(fy)
 		}
 		return fx == fy
+	case pref.StringKind:
+		return x.String() == y.String()
+	case pref.BytesKind:
+		return bytes.Equal(x.Bytes(), y.Bytes())
+	case pref.MessageKind, pref.GroupKind:
+		return equalMessage(x.Message(), y.Message())
 	default:
 		return x.Interface() == y.Interface()
 	}
diff --git a/proto/equal_test.go b/proto/equal_test.go
index 8d85397..2def30f 100644
--- a/proto/equal_test.go
+++ b/proto/equal_test.go
@@ -41,18 +41,15 @@
 			x:  new(testpb.TestAllTypes),
 			y:  new(testpb.TestAllTypes),
 			eq: true,
-		},
-		{
+		}, {
 			x:  (*testpb.TestAllTypes)(nil),
 			y:  (*testpb.TestAllExtensions)(nil),
 			eq: false,
-		},
-		{
+		}, {
 			x:  (*testpb.TestAllTypes)(nil),
 			y:  new(testpb.TestAllExtensions),
 			eq: false,
-		},
-		{
+		}, {
 			x:  new(testpb.TestAllTypes),
 			y:  new(testpb.TestAllExtensions),
 			eq: false,
@@ -113,6 +110,78 @@
 		}, {
 			x: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()},
 			y: &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_BAR.Enum()},
+		}, {
+			x:  &testpb.TestAllTypes{OptionalInt32: proto.Int32(2)},
+			y:  &testpb.TestAllTypes{OptionalInt32: proto.Int32(2)},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalInt64: proto.Int64(2)},
+			y:  &testpb.TestAllTypes{OptionalInt64: proto.Int64(2)},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalUint32: proto.Uint32(2)},
+			y:  &testpb.TestAllTypes{OptionalUint32: proto.Uint32(2)},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalUint64: proto.Uint64(2)},
+			y:  &testpb.TestAllTypes{OptionalUint64: proto.Uint64(2)},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalSint32: proto.Int32(2)},
+			y:  &testpb.TestAllTypes{OptionalSint32: proto.Int32(2)},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalSint64: proto.Int64(2)},
+			y:  &testpb.TestAllTypes{OptionalSint64: proto.Int64(2)},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalFixed32: proto.Uint32(2)},
+			y:  &testpb.TestAllTypes{OptionalFixed32: proto.Uint32(2)},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalFixed64: proto.Uint64(2)},
+			y:  &testpb.TestAllTypes{OptionalFixed64: proto.Uint64(2)},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalSfixed32: proto.Int32(2)},
+			y:  &testpb.TestAllTypes{OptionalSfixed32: proto.Int32(2)},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalSfixed64: proto.Int64(2)},
+			y:  &testpb.TestAllTypes{OptionalSfixed64: proto.Int64(2)},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalFloat: proto.Float32(2)},
+			y:  &testpb.TestAllTypes{OptionalFloat: proto.Float32(2)},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalDouble: proto.Float64(2)},
+			y:  &testpb.TestAllTypes{OptionalDouble: proto.Float64(2)},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalFloat: proto.Float32(float32(math.NaN()))},
+			y:  &testpb.TestAllTypes{OptionalFloat: proto.Float32(float32(math.NaN()))},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalDouble: proto.Float64(float64(math.NaN()))},
+			y:  &testpb.TestAllTypes{OptionalDouble: proto.Float64(float64(math.NaN()))},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalBool: proto.Bool(true)},
+			y:  &testpb.TestAllTypes{OptionalBool: proto.Bool(true)},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalString: proto.String("abc")},
+			y:  &testpb.TestAllTypes{OptionalString: proto.String("abc")},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalBytes: []byte("abc")},
+			y:  &testpb.TestAllTypes{OptionalBytes: []byte("abc")},
+			eq: true,
+		}, {
+			x:  &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()},
+			y:  &testpb.TestAllTypes{OptionalNestedEnum: testpb.TestAllTypes_FOO.Enum()},
+			eq: true,
 		},
 
 		// Proto2 presence.