aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/google.golang.org/protobuf/reflect/protoreflect/value_equal.go
blob: 591652541f2887caf1301f0a41e2b87f0879e71f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package protoreflect

import (
	"bytes"
	"fmt"
	"math"
	"reflect"

	"google.golang.org/protobuf/encoding/protowire"
)

// Equal reports whether v1 and v2 are recursively equal.
//
//   - Values of different types are always unequal.
//
//   - Bytes values are equal if they contain identical bytes.
//     Empty bytes (regardless of nil-ness) are considered equal.
//
//   - Floating point values are equal if they contain the same value.
//     Unlike the == operator, a NaN is equal to another NaN.
//
//   - Enums are equal if they contain the same number.
//     Since Value does not contain an enum descriptor,
//     enum values do not consider the type of the enum.
//
//   - Other scalar values are equal if they contain the same value.
//
//   - Message values are equal if they belong to the same message descriptor,
//     have the same set of populated known and extension field values,
//     and the same set of unknown fields values.
//
//   - Lists are equal if they are the same length and
//     each corresponding element is equal.
//
//   - Maps are equal if they have the same set of keys and
//     the corresponding value for each key is equal.
func (v1 Value) Equal(v2 Value) bool {
	return equalValue(v1, v2)
}

func equalValue(x, y Value) bool {
	eqType := x.typ == y.typ
	switch x.typ {
	case nilType:
		return eqType
	case boolType:
		return eqType && x.Bool() == y.Bool()
	case int32Type, int64Type:
		return eqType && x.Int() == y.Int()
	case uint32Type, uint64Type:
		return eqType && x.Uint() == y.Uint()
	case float32Type, float64Type:
		return eqType && equalFloat(x.Float(), y.Float())
	case stringType:
		return eqType && x.String() == y.String()
	case bytesType:
		return eqType && bytes.Equal(x.Bytes(), y.Bytes())
	case enumType:
		return eqType && x.Enum() == y.Enum()
	default:
		switch x := x.Interface().(type) {
		case Message:
			y, ok := y.Interface().(Message)
			return ok && equalMessage(x, y)
		case List:
			y, ok := y.Interface().(List)
			return ok && equalList(x, y)
		case Map:
			y, ok := y.Interface().(Map)
			return ok && equalMap(x, y)
		default:
			panic(fmt.Sprintf("unknown type: %T", x))
		}
	}
}

// equalFloat compares two floats, where NaNs are treated as equal.
func equalFloat(x, y float64) bool {
	if math.IsNaN(x) || math.IsNaN(y) {
		return math.IsNaN(x) && math.IsNaN(y)
	}
	return x == y
}

// equalMessage compares two messages.
func equalMessage(mx, my Message) bool {
	if mx.Descriptor() != my.Descriptor() {
		return false
	}

	nx := 0
	equal := true
	mx.Range(func(fd FieldDescriptor, vx Value) bool {
		nx++
		vy := my.Get(fd)
		equal = my.Has(fd) && equalValue(vx, vy)
		return equal
	})
	if !equal {
		return false
	}
	ny := 0
	my.Range(func(fd FieldDescriptor, vx Value) bool {
		ny++
		return true
	})
	if nx != ny {
		return false
	}

	return equalUnknown(mx.GetUnknown(), my.GetUnknown())
}

// equalList compares two lists.
func equalList(x, y List) bool {
	if x.Len() != y.Len() {
		return false
	}
	for i := x.Len() - 1; i >= 0; i-- {
		if !equalValue(x.Get(i), y.Get(i)) {
			return false
		}
	}
	return true
}

// equalMap compares two maps.
func equalMap(x, y Map) bool {
	if x.Len() != y.Len() {
		return false
	}
	equal := true
	x.Range(func(k MapKey, vx Value) bool {
		vy := y.Get(k)
		equal = y.Has(k) && equalValue(vx, vy)
		return equal
	})
	return equal
}

// equalUnknown compares unknown fields by direct comparison on the raw bytes
// of each individual field number.
func equalUnknown(x, y RawFields) bool {
	if len(x) != len(y) {
		return false
	}
	if bytes.Equal([]byte(x), []byte(y)) {
		return true
	}

	mx := make(map[FieldNumber]RawFields)
	my := make(map[FieldNumber]RawFields)
	for len(x) > 0 {
		fnum, _, n := protowire.ConsumeField(x)
		mx[fnum] = append(mx[fnum], x[:n]...)
		x = x[n:]
	}
	for len(y) > 0 {
		fnum, _, n := protowire.ConsumeField(y)
		my[fnum] = append(my[fnum], y[:n]...)
		y = y[n:]
	}
	return reflect.DeepEqual(mx, my)
}