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