xref: /aosp_15_r20/external/golang-protobuf/testing/protocmp/util.go (revision 1c12ee1efe575feb122dbf939ff15148a3b3e8f2)
1*1c12ee1eSDan Willemsen// Copyright 2019 The Go Authors. All rights reserved.
2*1c12ee1eSDan Willemsen// Use of this source code is governed by a BSD-style
3*1c12ee1eSDan Willemsen// license that can be found in the LICENSE file.
4*1c12ee1eSDan Willemsen
5*1c12ee1eSDan Willemsenpackage protocmp
6*1c12ee1eSDan Willemsen
7*1c12ee1eSDan Willemsenimport (
8*1c12ee1eSDan Willemsen	"bytes"
9*1c12ee1eSDan Willemsen	"fmt"
10*1c12ee1eSDan Willemsen	"math"
11*1c12ee1eSDan Willemsen	"reflect"
12*1c12ee1eSDan Willemsen	"strings"
13*1c12ee1eSDan Willemsen
14*1c12ee1eSDan Willemsen	"github.com/google/go-cmp/cmp"
15*1c12ee1eSDan Willemsen	"github.com/google/go-cmp/cmp/cmpopts"
16*1c12ee1eSDan Willemsen
17*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/proto"
18*1c12ee1eSDan Willemsen	"google.golang.org/protobuf/reflect/protoreflect"
19*1c12ee1eSDan Willemsen)
20*1c12ee1eSDan Willemsen
21*1c12ee1eSDan Willemsenvar (
22*1c12ee1eSDan Willemsen	enumReflectType    = reflect.TypeOf(Enum{})
23*1c12ee1eSDan Willemsen	messageReflectType = reflect.TypeOf(Message{})
24*1c12ee1eSDan Willemsen)
25*1c12ee1eSDan Willemsen
26*1c12ee1eSDan Willemsen// FilterEnum filters opt to only be applicable on standalone Enums,
27*1c12ee1eSDan Willemsen// singular fields of enums, list fields of enums, or map fields of enum values,
28*1c12ee1eSDan Willemsen// where the enum is the same type as the specified enum.
29*1c12ee1eSDan Willemsen//
30*1c12ee1eSDan Willemsen// The Go type of the last path step may be an:
31*1c12ee1eSDan Willemsen//   - Enum for singular fields, elements of a repeated field,
32*1c12ee1eSDan Willemsen//     values of a map field, or standalone Enums
33*1c12ee1eSDan Willemsen//   - []Enum for list fields
34*1c12ee1eSDan Willemsen//   - map[K]Enum for map fields
35*1c12ee1eSDan Willemsen//   - interface{} for a Message map entry value
36*1c12ee1eSDan Willemsen//
37*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
38*1c12ee1eSDan Willemsenfunc FilterEnum(enum protoreflect.Enum, opt cmp.Option) cmp.Option {
39*1c12ee1eSDan Willemsen	return FilterDescriptor(enum.Descriptor(), opt)
40*1c12ee1eSDan Willemsen}
41*1c12ee1eSDan Willemsen
42*1c12ee1eSDan Willemsen// FilterMessage filters opt to only be applicable on standalone Messages,
43*1c12ee1eSDan Willemsen// singular fields of messages, list fields of messages, or map fields of
44*1c12ee1eSDan Willemsen// message values, where the message is the same type as the specified message.
45*1c12ee1eSDan Willemsen//
46*1c12ee1eSDan Willemsen// The Go type of the last path step may be an:
47*1c12ee1eSDan Willemsen//   - Message for singular fields, elements of a repeated field,
48*1c12ee1eSDan Willemsen//     values of a map field, or standalone Messages
49*1c12ee1eSDan Willemsen//   - []Message for list fields
50*1c12ee1eSDan Willemsen//   - map[K]Message for map fields
51*1c12ee1eSDan Willemsen//   - interface{} for a Message map entry value
52*1c12ee1eSDan Willemsen//
53*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
54*1c12ee1eSDan Willemsenfunc FilterMessage(message proto.Message, opt cmp.Option) cmp.Option {
55*1c12ee1eSDan Willemsen	return FilterDescriptor(message.ProtoReflect().Descriptor(), opt)
56*1c12ee1eSDan Willemsen}
57*1c12ee1eSDan Willemsen
58*1c12ee1eSDan Willemsen// FilterField filters opt to only be applicable on the specified field
59*1c12ee1eSDan Willemsen// in the message. It panics if a field of the given name does not exist.
60*1c12ee1eSDan Willemsen//
61*1c12ee1eSDan Willemsen// The Go type of the last path step may be an:
62*1c12ee1eSDan Willemsen//   - T for singular fields
63*1c12ee1eSDan Willemsen//   - []T for list fields
64*1c12ee1eSDan Willemsen//   - map[K]T for map fields
65*1c12ee1eSDan Willemsen//   - interface{} for a Message map entry value
66*1c12ee1eSDan Willemsen//
67*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
68*1c12ee1eSDan Willemsenfunc FilterField(message proto.Message, name protoreflect.Name, opt cmp.Option) cmp.Option {
69*1c12ee1eSDan Willemsen	md := message.ProtoReflect().Descriptor()
70*1c12ee1eSDan Willemsen	return FilterDescriptor(mustFindFieldDescriptor(md, name), opt)
71*1c12ee1eSDan Willemsen}
72*1c12ee1eSDan Willemsen
73*1c12ee1eSDan Willemsen// FilterOneof filters opt to only be applicable on all fields within the
74*1c12ee1eSDan Willemsen// specified oneof in the message. It panics if a oneof of the given name
75*1c12ee1eSDan Willemsen// does not exist.
76*1c12ee1eSDan Willemsen//
77*1c12ee1eSDan Willemsen// The Go type of the last path step may be an:
78*1c12ee1eSDan Willemsen//   - T for singular fields
79*1c12ee1eSDan Willemsen//   - []T for list fields
80*1c12ee1eSDan Willemsen//   - map[K]T for map fields
81*1c12ee1eSDan Willemsen//   - interface{} for a Message map entry value
82*1c12ee1eSDan Willemsen//
83*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
84*1c12ee1eSDan Willemsenfunc FilterOneof(message proto.Message, name protoreflect.Name, opt cmp.Option) cmp.Option {
85*1c12ee1eSDan Willemsen	md := message.ProtoReflect().Descriptor()
86*1c12ee1eSDan Willemsen	return FilterDescriptor(mustFindOneofDescriptor(md, name), opt)
87*1c12ee1eSDan Willemsen}
88*1c12ee1eSDan Willemsen
89*1c12ee1eSDan Willemsen// FilterDescriptor ignores the specified descriptor.
90*1c12ee1eSDan Willemsen//
91*1c12ee1eSDan Willemsen// The following descriptor types may be specified:
92*1c12ee1eSDan Willemsen//   - protoreflect.EnumDescriptor
93*1c12ee1eSDan Willemsen//   - protoreflect.MessageDescriptor
94*1c12ee1eSDan Willemsen//   - protoreflect.FieldDescriptor
95*1c12ee1eSDan Willemsen//   - protoreflect.OneofDescriptor
96*1c12ee1eSDan Willemsen//
97*1c12ee1eSDan Willemsen// For the behavior of each, see the corresponding filter function.
98*1c12ee1eSDan Willemsen// Since this filter accepts a protoreflect.FieldDescriptor, it can be used
99*1c12ee1eSDan Willemsen// to also filter for extension fields as a protoreflect.ExtensionDescriptor
100*1c12ee1eSDan Willemsen// is just an alias to protoreflect.FieldDescriptor.
101*1c12ee1eSDan Willemsen//
102*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
103*1c12ee1eSDan Willemsenfunc FilterDescriptor(desc protoreflect.Descriptor, opt cmp.Option) cmp.Option {
104*1c12ee1eSDan Willemsen	f := newNameFilters(desc)
105*1c12ee1eSDan Willemsen	return cmp.FilterPath(f.Filter, opt)
106*1c12ee1eSDan Willemsen}
107*1c12ee1eSDan Willemsen
108*1c12ee1eSDan Willemsen// IgnoreEnums ignores all enums of the specified types.
109*1c12ee1eSDan Willemsen// It is equivalent to FilterEnum(enum, cmp.Ignore()) for each enum.
110*1c12ee1eSDan Willemsen//
111*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
112*1c12ee1eSDan Willemsenfunc IgnoreEnums(enums ...protoreflect.Enum) cmp.Option {
113*1c12ee1eSDan Willemsen	var ds []protoreflect.Descriptor
114*1c12ee1eSDan Willemsen	for _, e := range enums {
115*1c12ee1eSDan Willemsen		ds = append(ds, e.Descriptor())
116*1c12ee1eSDan Willemsen	}
117*1c12ee1eSDan Willemsen	return IgnoreDescriptors(ds...)
118*1c12ee1eSDan Willemsen}
119*1c12ee1eSDan Willemsen
120*1c12ee1eSDan Willemsen// IgnoreMessages ignores all messages of the specified types.
121*1c12ee1eSDan Willemsen// It is equivalent to FilterMessage(message, cmp.Ignore()) for each message.
122*1c12ee1eSDan Willemsen//
123*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
124*1c12ee1eSDan Willemsenfunc IgnoreMessages(messages ...proto.Message) cmp.Option {
125*1c12ee1eSDan Willemsen	var ds []protoreflect.Descriptor
126*1c12ee1eSDan Willemsen	for _, m := range messages {
127*1c12ee1eSDan Willemsen		ds = append(ds, m.ProtoReflect().Descriptor())
128*1c12ee1eSDan Willemsen	}
129*1c12ee1eSDan Willemsen	return IgnoreDescriptors(ds...)
130*1c12ee1eSDan Willemsen}
131*1c12ee1eSDan Willemsen
132*1c12ee1eSDan Willemsen// IgnoreFields ignores the specified fields in the specified message.
133*1c12ee1eSDan Willemsen// It is equivalent to FilterField(message, name, cmp.Ignore()) for each field
134*1c12ee1eSDan Willemsen// in the message.
135*1c12ee1eSDan Willemsen//
136*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
137*1c12ee1eSDan Willemsenfunc IgnoreFields(message proto.Message, names ...protoreflect.Name) cmp.Option {
138*1c12ee1eSDan Willemsen	var ds []protoreflect.Descriptor
139*1c12ee1eSDan Willemsen	md := message.ProtoReflect().Descriptor()
140*1c12ee1eSDan Willemsen	for _, s := range names {
141*1c12ee1eSDan Willemsen		ds = append(ds, mustFindFieldDescriptor(md, s))
142*1c12ee1eSDan Willemsen	}
143*1c12ee1eSDan Willemsen	return IgnoreDescriptors(ds...)
144*1c12ee1eSDan Willemsen}
145*1c12ee1eSDan Willemsen
146*1c12ee1eSDan Willemsen// IgnoreOneofs ignores fields of the specified oneofs in the specified message.
147*1c12ee1eSDan Willemsen// It is equivalent to FilterOneof(message, name, cmp.Ignore()) for each oneof
148*1c12ee1eSDan Willemsen// in the message.
149*1c12ee1eSDan Willemsen//
150*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
151*1c12ee1eSDan Willemsenfunc IgnoreOneofs(message proto.Message, names ...protoreflect.Name) cmp.Option {
152*1c12ee1eSDan Willemsen	var ds []protoreflect.Descriptor
153*1c12ee1eSDan Willemsen	md := message.ProtoReflect().Descriptor()
154*1c12ee1eSDan Willemsen	for _, s := range names {
155*1c12ee1eSDan Willemsen		ds = append(ds, mustFindOneofDescriptor(md, s))
156*1c12ee1eSDan Willemsen	}
157*1c12ee1eSDan Willemsen	return IgnoreDescriptors(ds...)
158*1c12ee1eSDan Willemsen}
159*1c12ee1eSDan Willemsen
160*1c12ee1eSDan Willemsen// IgnoreDescriptors ignores the specified set of descriptors.
161*1c12ee1eSDan Willemsen// It is equivalent to FilterDescriptor(desc, cmp.Ignore()) for each descriptor.
162*1c12ee1eSDan Willemsen//
163*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
164*1c12ee1eSDan Willemsenfunc IgnoreDescriptors(descs ...protoreflect.Descriptor) cmp.Option {
165*1c12ee1eSDan Willemsen	return cmp.FilterPath(newNameFilters(descs...).Filter, cmp.Ignore())
166*1c12ee1eSDan Willemsen}
167*1c12ee1eSDan Willemsen
168*1c12ee1eSDan Willemsenfunc mustFindFieldDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.FieldDescriptor {
169*1c12ee1eSDan Willemsen	d := findDescriptor(md, s)
170*1c12ee1eSDan Willemsen	if fd, ok := d.(protoreflect.FieldDescriptor); ok && fd.TextName() == string(s) {
171*1c12ee1eSDan Willemsen		return fd
172*1c12ee1eSDan Willemsen	}
173*1c12ee1eSDan Willemsen
174*1c12ee1eSDan Willemsen	var suggestion string
175*1c12ee1eSDan Willemsen	switch d := d.(type) {
176*1c12ee1eSDan Willemsen	case protoreflect.FieldDescriptor:
177*1c12ee1eSDan Willemsen		suggestion = fmt.Sprintf("; consider specifying field %q instead", d.TextName())
178*1c12ee1eSDan Willemsen	case protoreflect.OneofDescriptor:
179*1c12ee1eSDan Willemsen		suggestion = fmt.Sprintf("; consider specifying oneof %q with IgnoreOneofs instead", d.Name())
180*1c12ee1eSDan Willemsen	}
181*1c12ee1eSDan Willemsen	panic(fmt.Sprintf("message %q has no field %q%s", md.FullName(), s, suggestion))
182*1c12ee1eSDan Willemsen}
183*1c12ee1eSDan Willemsen
184*1c12ee1eSDan Willemsenfunc mustFindOneofDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.OneofDescriptor {
185*1c12ee1eSDan Willemsen	d := findDescriptor(md, s)
186*1c12ee1eSDan Willemsen	if od, ok := d.(protoreflect.OneofDescriptor); ok && d.Name() == s {
187*1c12ee1eSDan Willemsen		return od
188*1c12ee1eSDan Willemsen	}
189*1c12ee1eSDan Willemsen
190*1c12ee1eSDan Willemsen	var suggestion string
191*1c12ee1eSDan Willemsen	switch d := d.(type) {
192*1c12ee1eSDan Willemsen	case protoreflect.OneofDescriptor:
193*1c12ee1eSDan Willemsen		suggestion = fmt.Sprintf("; consider specifying oneof %q instead", d.Name())
194*1c12ee1eSDan Willemsen	case protoreflect.FieldDescriptor:
195*1c12ee1eSDan Willemsen		suggestion = fmt.Sprintf("; consider specifying field %q with IgnoreFields instead", d.TextName())
196*1c12ee1eSDan Willemsen	}
197*1c12ee1eSDan Willemsen	panic(fmt.Sprintf("message %q has no oneof %q%s", md.FullName(), s, suggestion))
198*1c12ee1eSDan Willemsen}
199*1c12ee1eSDan Willemsen
200*1c12ee1eSDan Willemsenfunc findDescriptor(md protoreflect.MessageDescriptor, s protoreflect.Name) protoreflect.Descriptor {
201*1c12ee1eSDan Willemsen	// Exact match.
202*1c12ee1eSDan Willemsen	if fd := md.Fields().ByTextName(string(s)); fd != nil {
203*1c12ee1eSDan Willemsen		return fd
204*1c12ee1eSDan Willemsen	}
205*1c12ee1eSDan Willemsen	if od := md.Oneofs().ByName(s); od != nil && !od.IsSynthetic() {
206*1c12ee1eSDan Willemsen		return od
207*1c12ee1eSDan Willemsen	}
208*1c12ee1eSDan Willemsen
209*1c12ee1eSDan Willemsen	// Best-effort match.
210*1c12ee1eSDan Willemsen	//
211*1c12ee1eSDan Willemsen	// It's a common user mistake to use the CamelCased field name as it appears
212*1c12ee1eSDan Willemsen	// in the generated Go struct. Instead of complaining that it doesn't exist,
213*1c12ee1eSDan Willemsen	// suggest the real protobuf name that the user may have desired.
214*1c12ee1eSDan Willemsen	normalize := func(s protoreflect.Name) string {
215*1c12ee1eSDan Willemsen		return strings.Replace(strings.ToLower(string(s)), "_", "", -1)
216*1c12ee1eSDan Willemsen	}
217*1c12ee1eSDan Willemsen	for i := 0; i < md.Fields().Len(); i++ {
218*1c12ee1eSDan Willemsen		if fd := md.Fields().Get(i); normalize(fd.Name()) == normalize(s) {
219*1c12ee1eSDan Willemsen			return fd
220*1c12ee1eSDan Willemsen		}
221*1c12ee1eSDan Willemsen	}
222*1c12ee1eSDan Willemsen	for i := 0; i < md.Oneofs().Len(); i++ {
223*1c12ee1eSDan Willemsen		if od := md.Oneofs().Get(i); normalize(od.Name()) == normalize(s) {
224*1c12ee1eSDan Willemsen			return od
225*1c12ee1eSDan Willemsen		}
226*1c12ee1eSDan Willemsen	}
227*1c12ee1eSDan Willemsen	return nil
228*1c12ee1eSDan Willemsen}
229*1c12ee1eSDan Willemsen
230*1c12ee1eSDan Willemsentype nameFilters struct {
231*1c12ee1eSDan Willemsen	names map[protoreflect.FullName]bool
232*1c12ee1eSDan Willemsen}
233*1c12ee1eSDan Willemsen
234*1c12ee1eSDan Willemsenfunc newNameFilters(descs ...protoreflect.Descriptor) *nameFilters {
235*1c12ee1eSDan Willemsen	f := &nameFilters{names: make(map[protoreflect.FullName]bool)}
236*1c12ee1eSDan Willemsen	for _, d := range descs {
237*1c12ee1eSDan Willemsen		switch d := d.(type) {
238*1c12ee1eSDan Willemsen		case protoreflect.EnumDescriptor:
239*1c12ee1eSDan Willemsen			f.names[d.FullName()] = true
240*1c12ee1eSDan Willemsen		case protoreflect.MessageDescriptor:
241*1c12ee1eSDan Willemsen			f.names[d.FullName()] = true
242*1c12ee1eSDan Willemsen		case protoreflect.FieldDescriptor:
243*1c12ee1eSDan Willemsen			f.names[d.FullName()] = true
244*1c12ee1eSDan Willemsen		case protoreflect.OneofDescriptor:
245*1c12ee1eSDan Willemsen			for i := 0; i < d.Fields().Len(); i++ {
246*1c12ee1eSDan Willemsen				f.names[d.Fields().Get(i).FullName()] = true
247*1c12ee1eSDan Willemsen			}
248*1c12ee1eSDan Willemsen		default:
249*1c12ee1eSDan Willemsen			panic("invalid descriptor type")
250*1c12ee1eSDan Willemsen		}
251*1c12ee1eSDan Willemsen	}
252*1c12ee1eSDan Willemsen	return f
253*1c12ee1eSDan Willemsen}
254*1c12ee1eSDan Willemsen
255*1c12ee1eSDan Willemsenfunc (f *nameFilters) Filter(p cmp.Path) bool {
256*1c12ee1eSDan Willemsen	vx, vy := p.Last().Values()
257*1c12ee1eSDan Willemsen	return (f.filterValue(vx) && f.filterValue(vy)) || f.filterFields(p)
258*1c12ee1eSDan Willemsen}
259*1c12ee1eSDan Willemsen
260*1c12ee1eSDan Willemsenfunc (f *nameFilters) filterFields(p cmp.Path) bool {
261*1c12ee1eSDan Willemsen	// Trim off trailing type-assertions so that the filter can match on the
262*1c12ee1eSDan Willemsen	// concrete value held within an interface value.
263*1c12ee1eSDan Willemsen	if _, ok := p.Last().(cmp.TypeAssertion); ok {
264*1c12ee1eSDan Willemsen		p = p[:len(p)-1]
265*1c12ee1eSDan Willemsen	}
266*1c12ee1eSDan Willemsen
267*1c12ee1eSDan Willemsen	// Filter for Message maps.
268*1c12ee1eSDan Willemsen	mi, ok := p.Index(-1).(cmp.MapIndex)
269*1c12ee1eSDan Willemsen	if !ok {
270*1c12ee1eSDan Willemsen		return false
271*1c12ee1eSDan Willemsen	}
272*1c12ee1eSDan Willemsen	ps := p.Index(-2)
273*1c12ee1eSDan Willemsen	if ps.Type() != messageReflectType {
274*1c12ee1eSDan Willemsen		return false
275*1c12ee1eSDan Willemsen	}
276*1c12ee1eSDan Willemsen
277*1c12ee1eSDan Willemsen	// Check field name.
278*1c12ee1eSDan Willemsen	vx, vy := ps.Values()
279*1c12ee1eSDan Willemsen	mx := vx.Interface().(Message)
280*1c12ee1eSDan Willemsen	my := vy.Interface().(Message)
281*1c12ee1eSDan Willemsen	k := mi.Key().String()
282*1c12ee1eSDan Willemsen	if f.filterFieldName(mx, k) && f.filterFieldName(my, k) {
283*1c12ee1eSDan Willemsen		return true
284*1c12ee1eSDan Willemsen	}
285*1c12ee1eSDan Willemsen
286*1c12ee1eSDan Willemsen	// Check field value.
287*1c12ee1eSDan Willemsen	vx, vy = mi.Values()
288*1c12ee1eSDan Willemsen	if f.filterFieldValue(vx) && f.filterFieldValue(vy) {
289*1c12ee1eSDan Willemsen		return true
290*1c12ee1eSDan Willemsen	}
291*1c12ee1eSDan Willemsen
292*1c12ee1eSDan Willemsen	return false
293*1c12ee1eSDan Willemsen}
294*1c12ee1eSDan Willemsen
295*1c12ee1eSDan Willemsenfunc (f *nameFilters) filterFieldName(m Message, k string) bool {
296*1c12ee1eSDan Willemsen	if _, ok := m[k]; !ok {
297*1c12ee1eSDan Willemsen		return true // treat missing fields as already filtered
298*1c12ee1eSDan Willemsen	}
299*1c12ee1eSDan Willemsen	var fd protoreflect.FieldDescriptor
300*1c12ee1eSDan Willemsen	switch mm := m[messageTypeKey].(messageMeta); {
301*1c12ee1eSDan Willemsen	case protoreflect.Name(k).IsValid():
302*1c12ee1eSDan Willemsen		fd = mm.md.Fields().ByTextName(k)
303*1c12ee1eSDan Willemsen	default:
304*1c12ee1eSDan Willemsen		fd = mm.xds[k]
305*1c12ee1eSDan Willemsen	}
306*1c12ee1eSDan Willemsen	if fd != nil {
307*1c12ee1eSDan Willemsen		return f.names[fd.FullName()]
308*1c12ee1eSDan Willemsen	}
309*1c12ee1eSDan Willemsen	return false
310*1c12ee1eSDan Willemsen}
311*1c12ee1eSDan Willemsen
312*1c12ee1eSDan Willemsenfunc (f *nameFilters) filterFieldValue(v reflect.Value) bool {
313*1c12ee1eSDan Willemsen	if !v.IsValid() {
314*1c12ee1eSDan Willemsen		return true // implies missing slice element or map entry
315*1c12ee1eSDan Willemsen	}
316*1c12ee1eSDan Willemsen	v = v.Elem() // map entries are always populated values
317*1c12ee1eSDan Willemsen	switch t := v.Type(); {
318*1c12ee1eSDan Willemsen	case t == enumReflectType || t == messageReflectType:
319*1c12ee1eSDan Willemsen		// Check for singular message or enum field.
320*1c12ee1eSDan Willemsen		return f.filterValue(v)
321*1c12ee1eSDan Willemsen	case t.Kind() == reflect.Slice && (t.Elem() == enumReflectType || t.Elem() == messageReflectType):
322*1c12ee1eSDan Willemsen		// Check for list field of enum or message type.
323*1c12ee1eSDan Willemsen		return f.filterValue(v.Index(0))
324*1c12ee1eSDan Willemsen	case t.Kind() == reflect.Map && (t.Elem() == enumReflectType || t.Elem() == messageReflectType):
325*1c12ee1eSDan Willemsen		// Check for map field of enum or message type.
326*1c12ee1eSDan Willemsen		return f.filterValue(v.MapIndex(v.MapKeys()[0]))
327*1c12ee1eSDan Willemsen	}
328*1c12ee1eSDan Willemsen	return false
329*1c12ee1eSDan Willemsen}
330*1c12ee1eSDan Willemsen
331*1c12ee1eSDan Willemsenfunc (f *nameFilters) filterValue(v reflect.Value) bool {
332*1c12ee1eSDan Willemsen	if !v.IsValid() {
333*1c12ee1eSDan Willemsen		return true // implies missing slice element or map entry
334*1c12ee1eSDan Willemsen	}
335*1c12ee1eSDan Willemsen	if !v.CanInterface() {
336*1c12ee1eSDan Willemsen		return false // implies unexported struct field
337*1c12ee1eSDan Willemsen	}
338*1c12ee1eSDan Willemsen	switch v := v.Interface().(type) {
339*1c12ee1eSDan Willemsen	case Enum:
340*1c12ee1eSDan Willemsen		return v.Descriptor() != nil && f.names[v.Descriptor().FullName()]
341*1c12ee1eSDan Willemsen	case Message:
342*1c12ee1eSDan Willemsen		return v.Descriptor() != nil && f.names[v.Descriptor().FullName()]
343*1c12ee1eSDan Willemsen	}
344*1c12ee1eSDan Willemsen	return false
345*1c12ee1eSDan Willemsen}
346*1c12ee1eSDan Willemsen
347*1c12ee1eSDan Willemsen// IgnoreDefaultScalars ignores singular scalars that are unpopulated or
348*1c12ee1eSDan Willemsen// explicitly set to the default value.
349*1c12ee1eSDan Willemsen// This option does not effect elements in a list or entries in a map.
350*1c12ee1eSDan Willemsen//
351*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
352*1c12ee1eSDan Willemsenfunc IgnoreDefaultScalars() cmp.Option {
353*1c12ee1eSDan Willemsen	return cmp.FilterPath(func(p cmp.Path) bool {
354*1c12ee1eSDan Willemsen		// Filter for Message maps.
355*1c12ee1eSDan Willemsen		mi, ok := p.Index(-1).(cmp.MapIndex)
356*1c12ee1eSDan Willemsen		if !ok {
357*1c12ee1eSDan Willemsen			return false
358*1c12ee1eSDan Willemsen		}
359*1c12ee1eSDan Willemsen		ps := p.Index(-2)
360*1c12ee1eSDan Willemsen		if ps.Type() != messageReflectType {
361*1c12ee1eSDan Willemsen			return false
362*1c12ee1eSDan Willemsen		}
363*1c12ee1eSDan Willemsen
364*1c12ee1eSDan Willemsen		// Check whether both fields are default or unpopulated scalars.
365*1c12ee1eSDan Willemsen		vx, vy := ps.Values()
366*1c12ee1eSDan Willemsen		mx := vx.Interface().(Message)
367*1c12ee1eSDan Willemsen		my := vy.Interface().(Message)
368*1c12ee1eSDan Willemsen		k := mi.Key().String()
369*1c12ee1eSDan Willemsen		return isDefaultScalar(mx, k) && isDefaultScalar(my, k)
370*1c12ee1eSDan Willemsen	}, cmp.Ignore())
371*1c12ee1eSDan Willemsen}
372*1c12ee1eSDan Willemsen
373*1c12ee1eSDan Willemsenfunc isDefaultScalar(m Message, k string) bool {
374*1c12ee1eSDan Willemsen	if _, ok := m[k]; !ok {
375*1c12ee1eSDan Willemsen		return true
376*1c12ee1eSDan Willemsen	}
377*1c12ee1eSDan Willemsen
378*1c12ee1eSDan Willemsen	var fd protoreflect.FieldDescriptor
379*1c12ee1eSDan Willemsen	switch mm := m[messageTypeKey].(messageMeta); {
380*1c12ee1eSDan Willemsen	case protoreflect.Name(k).IsValid():
381*1c12ee1eSDan Willemsen		fd = mm.md.Fields().ByTextName(k)
382*1c12ee1eSDan Willemsen	default:
383*1c12ee1eSDan Willemsen		fd = mm.xds[k]
384*1c12ee1eSDan Willemsen	}
385*1c12ee1eSDan Willemsen	if fd == nil || !fd.Default().IsValid() {
386*1c12ee1eSDan Willemsen		return false
387*1c12ee1eSDan Willemsen	}
388*1c12ee1eSDan Willemsen	switch fd.Kind() {
389*1c12ee1eSDan Willemsen	case protoreflect.BytesKind:
390*1c12ee1eSDan Willemsen		v, ok := m[k].([]byte)
391*1c12ee1eSDan Willemsen		return ok && bytes.Equal(fd.Default().Bytes(), v)
392*1c12ee1eSDan Willemsen	case protoreflect.FloatKind:
393*1c12ee1eSDan Willemsen		v, ok := m[k].(float32)
394*1c12ee1eSDan Willemsen		return ok && equalFloat64(fd.Default().Float(), float64(v))
395*1c12ee1eSDan Willemsen	case protoreflect.DoubleKind:
396*1c12ee1eSDan Willemsen		v, ok := m[k].(float64)
397*1c12ee1eSDan Willemsen		return ok && equalFloat64(fd.Default().Float(), float64(v))
398*1c12ee1eSDan Willemsen	case protoreflect.EnumKind:
399*1c12ee1eSDan Willemsen		v, ok := m[k].(Enum)
400*1c12ee1eSDan Willemsen		return ok && fd.Default().Enum() == v.Number()
401*1c12ee1eSDan Willemsen	default:
402*1c12ee1eSDan Willemsen		return reflect.DeepEqual(fd.Default().Interface(), m[k])
403*1c12ee1eSDan Willemsen	}
404*1c12ee1eSDan Willemsen}
405*1c12ee1eSDan Willemsen
406*1c12ee1eSDan Willemsenfunc equalFloat64(x, y float64) bool {
407*1c12ee1eSDan Willemsen	return x == y || (math.IsNaN(x) && math.IsNaN(y))
408*1c12ee1eSDan Willemsen}
409*1c12ee1eSDan Willemsen
410*1c12ee1eSDan Willemsen// IgnoreEmptyMessages ignores messages that are empty or unpopulated.
411*1c12ee1eSDan Willemsen// It applies to standalone Messages, singular message fields,
412*1c12ee1eSDan Willemsen// list fields of messages, and map fields of message values.
413*1c12ee1eSDan Willemsen//
414*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
415*1c12ee1eSDan Willemsenfunc IgnoreEmptyMessages() cmp.Option {
416*1c12ee1eSDan Willemsen	return cmp.FilterPath(func(p cmp.Path) bool {
417*1c12ee1eSDan Willemsen		vx, vy := p.Last().Values()
418*1c12ee1eSDan Willemsen		return (isEmptyMessage(vx) && isEmptyMessage(vy)) || isEmptyMessageFields(p)
419*1c12ee1eSDan Willemsen	}, cmp.Ignore())
420*1c12ee1eSDan Willemsen}
421*1c12ee1eSDan Willemsen
422*1c12ee1eSDan Willemsenfunc isEmptyMessageFields(p cmp.Path) bool {
423*1c12ee1eSDan Willemsen	// Filter for Message maps.
424*1c12ee1eSDan Willemsen	mi, ok := p.Index(-1).(cmp.MapIndex)
425*1c12ee1eSDan Willemsen	if !ok {
426*1c12ee1eSDan Willemsen		return false
427*1c12ee1eSDan Willemsen	}
428*1c12ee1eSDan Willemsen	ps := p.Index(-2)
429*1c12ee1eSDan Willemsen	if ps.Type() != messageReflectType {
430*1c12ee1eSDan Willemsen		return false
431*1c12ee1eSDan Willemsen	}
432*1c12ee1eSDan Willemsen
433*1c12ee1eSDan Willemsen	// Check field value.
434*1c12ee1eSDan Willemsen	vx, vy := mi.Values()
435*1c12ee1eSDan Willemsen	if isEmptyMessageFieldValue(vx) && isEmptyMessageFieldValue(vy) {
436*1c12ee1eSDan Willemsen		return true
437*1c12ee1eSDan Willemsen	}
438*1c12ee1eSDan Willemsen
439*1c12ee1eSDan Willemsen	return false
440*1c12ee1eSDan Willemsen}
441*1c12ee1eSDan Willemsen
442*1c12ee1eSDan Willemsenfunc isEmptyMessageFieldValue(v reflect.Value) bool {
443*1c12ee1eSDan Willemsen	if !v.IsValid() {
444*1c12ee1eSDan Willemsen		return true // implies missing slice element or map entry
445*1c12ee1eSDan Willemsen	}
446*1c12ee1eSDan Willemsen	v = v.Elem() // map entries are always populated values
447*1c12ee1eSDan Willemsen	switch t := v.Type(); {
448*1c12ee1eSDan Willemsen	case t == messageReflectType:
449*1c12ee1eSDan Willemsen		// Check singular field for empty message.
450*1c12ee1eSDan Willemsen		if !isEmptyMessage(v) {
451*1c12ee1eSDan Willemsen			return false
452*1c12ee1eSDan Willemsen		}
453*1c12ee1eSDan Willemsen	case t.Kind() == reflect.Slice && t.Elem() == messageReflectType:
454*1c12ee1eSDan Willemsen		// Check list field for all empty message elements.
455*1c12ee1eSDan Willemsen		for i := 0; i < v.Len(); i++ {
456*1c12ee1eSDan Willemsen			if !isEmptyMessage(v.Index(i)) {
457*1c12ee1eSDan Willemsen				return false
458*1c12ee1eSDan Willemsen			}
459*1c12ee1eSDan Willemsen		}
460*1c12ee1eSDan Willemsen	case t.Kind() == reflect.Map && t.Elem() == messageReflectType:
461*1c12ee1eSDan Willemsen		// Check map field for all empty message values.
462*1c12ee1eSDan Willemsen		for _, k := range v.MapKeys() {
463*1c12ee1eSDan Willemsen			if !isEmptyMessage(v.MapIndex(k)) {
464*1c12ee1eSDan Willemsen				return false
465*1c12ee1eSDan Willemsen			}
466*1c12ee1eSDan Willemsen		}
467*1c12ee1eSDan Willemsen	default:
468*1c12ee1eSDan Willemsen		return false
469*1c12ee1eSDan Willemsen	}
470*1c12ee1eSDan Willemsen	return true
471*1c12ee1eSDan Willemsen}
472*1c12ee1eSDan Willemsen
473*1c12ee1eSDan Willemsenfunc isEmptyMessage(v reflect.Value) bool {
474*1c12ee1eSDan Willemsen	if !v.IsValid() {
475*1c12ee1eSDan Willemsen		return true // implies missing slice element or map entry
476*1c12ee1eSDan Willemsen	}
477*1c12ee1eSDan Willemsen	if !v.CanInterface() {
478*1c12ee1eSDan Willemsen		return false // implies unexported struct field
479*1c12ee1eSDan Willemsen	}
480*1c12ee1eSDan Willemsen	if m, ok := v.Interface().(Message); ok {
481*1c12ee1eSDan Willemsen		for k := range m {
482*1c12ee1eSDan Willemsen			if k != messageTypeKey && k != messageInvalidKey {
483*1c12ee1eSDan Willemsen				return false
484*1c12ee1eSDan Willemsen			}
485*1c12ee1eSDan Willemsen		}
486*1c12ee1eSDan Willemsen		return true
487*1c12ee1eSDan Willemsen	}
488*1c12ee1eSDan Willemsen	return false
489*1c12ee1eSDan Willemsen}
490*1c12ee1eSDan Willemsen
491*1c12ee1eSDan Willemsen// IgnoreUnknown ignores unknown fields in all messages.
492*1c12ee1eSDan Willemsen//
493*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
494*1c12ee1eSDan Willemsenfunc IgnoreUnknown() cmp.Option {
495*1c12ee1eSDan Willemsen	return cmp.FilterPath(func(p cmp.Path) bool {
496*1c12ee1eSDan Willemsen		// Filter for Message maps.
497*1c12ee1eSDan Willemsen		mi, ok := p.Index(-1).(cmp.MapIndex)
498*1c12ee1eSDan Willemsen		if !ok {
499*1c12ee1eSDan Willemsen			return false
500*1c12ee1eSDan Willemsen		}
501*1c12ee1eSDan Willemsen		ps := p.Index(-2)
502*1c12ee1eSDan Willemsen		if ps.Type() != messageReflectType {
503*1c12ee1eSDan Willemsen			return false
504*1c12ee1eSDan Willemsen		}
505*1c12ee1eSDan Willemsen
506*1c12ee1eSDan Willemsen		// Filter for unknown fields (which always have a numeric map key).
507*1c12ee1eSDan Willemsen		return strings.Trim(mi.Key().String(), "0123456789") == ""
508*1c12ee1eSDan Willemsen	}, cmp.Ignore())
509*1c12ee1eSDan Willemsen}
510*1c12ee1eSDan Willemsen
511*1c12ee1eSDan Willemsen// SortRepeated sorts repeated fields of the specified element type.
512*1c12ee1eSDan Willemsen// The less function must be of the form "func(T, T) bool" where T is the
513*1c12ee1eSDan Willemsen// Go element type for the repeated field kind.
514*1c12ee1eSDan Willemsen//
515*1c12ee1eSDan Willemsen// The element type T can be one of the following:
516*1c12ee1eSDan Willemsen//   - Go type for a protobuf scalar kind except for an enum
517*1c12ee1eSDan Willemsen//     (i.e., bool, int32, int64, uint32, uint64, float32, float64, string, and []byte)
518*1c12ee1eSDan Willemsen//   - E where E is a concrete enum type that implements protoreflect.Enum
519*1c12ee1eSDan Willemsen//   - M where M is a concrete message type that implement proto.Message
520*1c12ee1eSDan Willemsen//
521*1c12ee1eSDan Willemsen// This option only applies to repeated fields within a protobuf message.
522*1c12ee1eSDan Willemsen// It does not operate on higher-order Go types that seem like a repeated field.
523*1c12ee1eSDan Willemsen// For example, a []T outside the context of a protobuf message will not be
524*1c12ee1eSDan Willemsen// handled by this option. To sort Go slices that are not repeated fields,
525*1c12ee1eSDan Willemsen// consider using "github.com/google/go-cmp/cmp/cmpopts".SortSlices instead.
526*1c12ee1eSDan Willemsen//
527*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
528*1c12ee1eSDan Willemsenfunc SortRepeated(lessFunc interface{}) cmp.Option {
529*1c12ee1eSDan Willemsen	t, ok := checkTTBFunc(lessFunc)
530*1c12ee1eSDan Willemsen	if !ok {
531*1c12ee1eSDan Willemsen		panic(fmt.Sprintf("invalid less function: %T", lessFunc))
532*1c12ee1eSDan Willemsen	}
533*1c12ee1eSDan Willemsen
534*1c12ee1eSDan Willemsen	var opt cmp.Option
535*1c12ee1eSDan Willemsen	var sliceType reflect.Type
536*1c12ee1eSDan Willemsen	switch vf := reflect.ValueOf(lessFunc); {
537*1c12ee1eSDan Willemsen	case t.Implements(enumV2Type):
538*1c12ee1eSDan Willemsen		et := reflect.Zero(t).Interface().(protoreflect.Enum).Type()
539*1c12ee1eSDan Willemsen		lessFunc = func(x, y Enum) bool {
540*1c12ee1eSDan Willemsen			vx := reflect.ValueOf(et.New(x.Number()))
541*1c12ee1eSDan Willemsen			vy := reflect.ValueOf(et.New(y.Number()))
542*1c12ee1eSDan Willemsen			return vf.Call([]reflect.Value{vx, vy})[0].Bool()
543*1c12ee1eSDan Willemsen		}
544*1c12ee1eSDan Willemsen		opt = FilterDescriptor(et.Descriptor(), cmpopts.SortSlices(lessFunc))
545*1c12ee1eSDan Willemsen		sliceType = reflect.SliceOf(enumReflectType)
546*1c12ee1eSDan Willemsen	case t.Implements(messageV2Type):
547*1c12ee1eSDan Willemsen		mt := reflect.Zero(t).Interface().(protoreflect.ProtoMessage).ProtoReflect().Type()
548*1c12ee1eSDan Willemsen		lessFunc = func(x, y Message) bool {
549*1c12ee1eSDan Willemsen			mx := mt.New().Interface()
550*1c12ee1eSDan Willemsen			my := mt.New().Interface()
551*1c12ee1eSDan Willemsen			proto.Merge(mx, x)
552*1c12ee1eSDan Willemsen			proto.Merge(my, y)
553*1c12ee1eSDan Willemsen			vx := reflect.ValueOf(mx)
554*1c12ee1eSDan Willemsen			vy := reflect.ValueOf(my)
555*1c12ee1eSDan Willemsen			return vf.Call([]reflect.Value{vx, vy})[0].Bool()
556*1c12ee1eSDan Willemsen		}
557*1c12ee1eSDan Willemsen		opt = FilterDescriptor(mt.Descriptor(), cmpopts.SortSlices(lessFunc))
558*1c12ee1eSDan Willemsen		sliceType = reflect.SliceOf(messageReflectType)
559*1c12ee1eSDan Willemsen	default:
560*1c12ee1eSDan Willemsen		switch t {
561*1c12ee1eSDan Willemsen		case reflect.TypeOf(bool(false)):
562*1c12ee1eSDan Willemsen		case reflect.TypeOf(int32(0)):
563*1c12ee1eSDan Willemsen		case reflect.TypeOf(int64(0)):
564*1c12ee1eSDan Willemsen		case reflect.TypeOf(uint32(0)):
565*1c12ee1eSDan Willemsen		case reflect.TypeOf(uint64(0)):
566*1c12ee1eSDan Willemsen		case reflect.TypeOf(float32(0)):
567*1c12ee1eSDan Willemsen		case reflect.TypeOf(float64(0)):
568*1c12ee1eSDan Willemsen		case reflect.TypeOf(string("")):
569*1c12ee1eSDan Willemsen		case reflect.TypeOf([]byte(nil)):
570*1c12ee1eSDan Willemsen		default:
571*1c12ee1eSDan Willemsen			panic(fmt.Sprintf("invalid element type: %v", t))
572*1c12ee1eSDan Willemsen		}
573*1c12ee1eSDan Willemsen		opt = cmpopts.SortSlices(lessFunc)
574*1c12ee1eSDan Willemsen		sliceType = reflect.SliceOf(t)
575*1c12ee1eSDan Willemsen	}
576*1c12ee1eSDan Willemsen
577*1c12ee1eSDan Willemsen	return cmp.FilterPath(func(p cmp.Path) bool {
578*1c12ee1eSDan Willemsen		// Filter to only apply to repeated fields within a message.
579*1c12ee1eSDan Willemsen		if t := p.Index(-1).Type(); t == nil || t != sliceType {
580*1c12ee1eSDan Willemsen			return false
581*1c12ee1eSDan Willemsen		}
582*1c12ee1eSDan Willemsen		if t := p.Index(-2).Type(); t == nil || t.Kind() != reflect.Interface {
583*1c12ee1eSDan Willemsen			return false
584*1c12ee1eSDan Willemsen		}
585*1c12ee1eSDan Willemsen		if t := p.Index(-3).Type(); t == nil || t != messageReflectType {
586*1c12ee1eSDan Willemsen			return false
587*1c12ee1eSDan Willemsen		}
588*1c12ee1eSDan Willemsen		return true
589*1c12ee1eSDan Willemsen	}, opt)
590*1c12ee1eSDan Willemsen}
591*1c12ee1eSDan Willemsen
592*1c12ee1eSDan Willemsenfunc checkTTBFunc(lessFunc interface{}) (reflect.Type, bool) {
593*1c12ee1eSDan Willemsen	switch t := reflect.TypeOf(lessFunc); {
594*1c12ee1eSDan Willemsen	case t == nil:
595*1c12ee1eSDan Willemsen		return nil, false
596*1c12ee1eSDan Willemsen	case t.NumIn() != 2 || t.In(0) != t.In(1) || t.IsVariadic():
597*1c12ee1eSDan Willemsen		return nil, false
598*1c12ee1eSDan Willemsen	case t.NumOut() != 1 || t.Out(0) != reflect.TypeOf(false):
599*1c12ee1eSDan Willemsen		return nil, false
600*1c12ee1eSDan Willemsen	default:
601*1c12ee1eSDan Willemsen		return t.In(0), true
602*1c12ee1eSDan Willemsen	}
603*1c12ee1eSDan Willemsen}
604*1c12ee1eSDan Willemsen
605*1c12ee1eSDan Willemsen// SortRepeatedFields sorts the specified repeated fields.
606*1c12ee1eSDan Willemsen// Sorting a repeated field is useful for treating the list as a multiset
607*1c12ee1eSDan Willemsen// (i.e., a set where each value can appear multiple times).
608*1c12ee1eSDan Willemsen// It panics if the field does not exist or is not a repeated field.
609*1c12ee1eSDan Willemsen//
610*1c12ee1eSDan Willemsen// The sort ordering is as follows:
611*1c12ee1eSDan Willemsen//   - Booleans are sorted where false is sorted before true.
612*1c12ee1eSDan Willemsen//   - Integers are sorted in ascending order.
613*1c12ee1eSDan Willemsen//   - Floating-point numbers are sorted in ascending order according to
614*1c12ee1eSDan Willemsen//     the total ordering defined by IEEE-754 (section 5.10).
615*1c12ee1eSDan Willemsen//   - Strings and bytes are sorted lexicographically in ascending order.
616*1c12ee1eSDan Willemsen//   - Enums are sorted in ascending order based on its numeric value.
617*1c12ee1eSDan Willemsen//   - Messages are sorted according to some arbitrary ordering
618*1c12ee1eSDan Willemsen//     which is undefined and may change in future implementations.
619*1c12ee1eSDan Willemsen//
620*1c12ee1eSDan Willemsen// The ordering chosen for repeated messages is unlikely to be aesthetically
621*1c12ee1eSDan Willemsen// preferred by humans. Consider using a custom sort function:
622*1c12ee1eSDan Willemsen//
623*1c12ee1eSDan Willemsen//	FilterField(m, "foo_field", SortRepeated(func(x, y *foopb.MyMessage) bool {
624*1c12ee1eSDan Willemsen//	    ... // user-provided definition for less
625*1c12ee1eSDan Willemsen//	}))
626*1c12ee1eSDan Willemsen//
627*1c12ee1eSDan Willemsen// This must be used in conjunction with Transform.
628*1c12ee1eSDan Willemsenfunc SortRepeatedFields(message proto.Message, names ...protoreflect.Name) cmp.Option {
629*1c12ee1eSDan Willemsen	var opts cmp.Options
630*1c12ee1eSDan Willemsen	md := message.ProtoReflect().Descriptor()
631*1c12ee1eSDan Willemsen	for _, name := range names {
632*1c12ee1eSDan Willemsen		fd := mustFindFieldDescriptor(md, name)
633*1c12ee1eSDan Willemsen		if !fd.IsList() {
634*1c12ee1eSDan Willemsen			panic(fmt.Sprintf("message field %q is not repeated", fd.FullName()))
635*1c12ee1eSDan Willemsen		}
636*1c12ee1eSDan Willemsen
637*1c12ee1eSDan Willemsen		var lessFunc interface{}
638*1c12ee1eSDan Willemsen		switch fd.Kind() {
639*1c12ee1eSDan Willemsen		case protoreflect.BoolKind:
640*1c12ee1eSDan Willemsen			lessFunc = func(x, y bool) bool { return !x && y }
641*1c12ee1eSDan Willemsen		case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
642*1c12ee1eSDan Willemsen			lessFunc = func(x, y int32) bool { return x < y }
643*1c12ee1eSDan Willemsen		case protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
644*1c12ee1eSDan Willemsen			lessFunc = func(x, y int64) bool { return x < y }
645*1c12ee1eSDan Willemsen		case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
646*1c12ee1eSDan Willemsen			lessFunc = func(x, y uint32) bool { return x < y }
647*1c12ee1eSDan Willemsen		case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
648*1c12ee1eSDan Willemsen			lessFunc = func(x, y uint64) bool { return x < y }
649*1c12ee1eSDan Willemsen		case protoreflect.FloatKind:
650*1c12ee1eSDan Willemsen			lessFunc = lessF32
651*1c12ee1eSDan Willemsen		case protoreflect.DoubleKind:
652*1c12ee1eSDan Willemsen			lessFunc = lessF64
653*1c12ee1eSDan Willemsen		case protoreflect.StringKind:
654*1c12ee1eSDan Willemsen			lessFunc = func(x, y string) bool { return x < y }
655*1c12ee1eSDan Willemsen		case protoreflect.BytesKind:
656*1c12ee1eSDan Willemsen			lessFunc = func(x, y []byte) bool { return bytes.Compare(x, y) < 0 }
657*1c12ee1eSDan Willemsen		case protoreflect.EnumKind:
658*1c12ee1eSDan Willemsen			lessFunc = func(x, y Enum) bool { return x.Number() < y.Number() }
659*1c12ee1eSDan Willemsen		case protoreflect.MessageKind, protoreflect.GroupKind:
660*1c12ee1eSDan Willemsen			lessFunc = func(x, y Message) bool { return x.String() < y.String() }
661*1c12ee1eSDan Willemsen		default:
662*1c12ee1eSDan Willemsen			panic(fmt.Sprintf("invalid kind: %v", fd.Kind()))
663*1c12ee1eSDan Willemsen		}
664*1c12ee1eSDan Willemsen		opts = append(opts, FilterDescriptor(fd, cmpopts.SortSlices(lessFunc)))
665*1c12ee1eSDan Willemsen	}
666*1c12ee1eSDan Willemsen	return opts
667*1c12ee1eSDan Willemsen}
668*1c12ee1eSDan Willemsen
669*1c12ee1eSDan Willemsenfunc lessF32(x, y float32) bool {
670*1c12ee1eSDan Willemsen	// Bit-wise implementation of IEEE-754, section 5.10.
671*1c12ee1eSDan Willemsen	xi := int32(math.Float32bits(x))
672*1c12ee1eSDan Willemsen	yi := int32(math.Float32bits(y))
673*1c12ee1eSDan Willemsen	xi ^= int32(uint32(xi>>31) >> 1)
674*1c12ee1eSDan Willemsen	yi ^= int32(uint32(yi>>31) >> 1)
675*1c12ee1eSDan Willemsen	return xi < yi
676*1c12ee1eSDan Willemsen}
677*1c12ee1eSDan Willemsenfunc lessF64(x, y float64) bool {
678*1c12ee1eSDan Willemsen	// Bit-wise implementation of IEEE-754, section 5.10.
679*1c12ee1eSDan Willemsen	xi := int64(math.Float64bits(x))
680*1c12ee1eSDan Willemsen	yi := int64(math.Float64bits(y))
681*1c12ee1eSDan Willemsen	xi ^= int64(uint64(xi>>63) >> 1)
682*1c12ee1eSDan Willemsen	yi ^= int64(uint64(yi>>63) >> 1)
683*1c12ee1eSDan Willemsen	return xi < yi
684*1c12ee1eSDan Willemsen}
685