vendor: update buildkit to master@31c870e82a48

Signed-off-by: Justin Chadwell <me@jedevc.com>
This commit is contained in:
Justin Chadwell
2023-05-15 18:32:31 +01:00
parent 167cd16acb
commit e61a8cf637
269 changed files with 25798 additions and 3371 deletions

View File

@ -0,0 +1,122 @@
// Copyright 2020 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 protopath provides functionality for
// representing a sequence of protobuf reflection operations on a message.
package protopath
import (
"fmt"
"google.golang.org/protobuf/internal/msgfmt"
"google.golang.org/protobuf/reflect/protoreflect"
)
// NOTE: The Path and Values are separate types here since there are use cases
// where you would like to "address" some value in a message with just the path
// and don't have the value information available.
//
// This is different from how "github.com/google/go-cmp/cmp".Path operates,
// which combines both path and value information together.
// Since the cmp package itself is the only one ever constructing a cmp.Path,
// it will always have the value available.
// Path is a sequence of protobuf reflection steps applied to some root
// protobuf message value to arrive at the current value.
// The first step must be a Root step.
type Path []Step
// TODO: Provide a Parse function that parses something similar to or
// perhaps identical to the output of Path.String.
// Index returns the ith step in the path and supports negative indexing.
// A negative index starts counting from the tail of the Path such that -1
// refers to the last step, -2 refers to the second-to-last step, and so on.
// It returns a zero Step value if the index is out-of-bounds.
func (p Path) Index(i int) Step {
if i < 0 {
i = len(p) + i
}
if i < 0 || i >= len(p) {
return Step{}
}
return p[i]
}
// String returns a structured representation of the path
// by concatenating the string representation of every path step.
func (p Path) String() string {
var b []byte
for _, s := range p {
b = s.appendString(b)
}
return string(b)
}
// Values is a Path paired with a sequence of values at each step.
// The lengths of Path and Values must be identical.
// The first step must be a Root step and
// the first value must be a concrete message value.
type Values struct {
Path Path
Values []protoreflect.Value
}
// Len reports the length of the path and values.
// If the path and values have differing length, it returns the minimum length.
func (p Values) Len() int {
n := len(p.Path)
if n > len(p.Values) {
n = len(p.Values)
}
return n
}
// Index returns the ith step and value and supports negative indexing.
// A negative index starts counting from the tail of the Values such that -1
// refers to the last pair, -2 refers to the second-to-last pair, and so on.
func (p Values) Index(i int) (out struct {
Step Step
Value protoreflect.Value
}) {
// NOTE: This returns a single struct instead of two return values so that
// callers can make use of the the value in an expression:
// vs.Index(i).Value.Interface()
n := p.Len()
if i < 0 {
i = n + i
}
if i < 0 || i >= n {
return out
}
out.Step = p.Path[i]
out.Value = p.Values[i]
return out
}
// String returns a humanly readable representation of the path and last value.
// Do not depend on the output being stable.
//
// For example:
//
// (path.to.MyMessage).list_field[5].map_field["hello"] = {hello: "world"}
func (p Values) String() string {
n := p.Len()
if n == 0 {
return ""
}
// Determine the field descriptor associated with the last step.
var fd protoreflect.FieldDescriptor
last := p.Index(-1)
switch last.Step.kind {
case FieldAccessStep:
fd = last.Step.FieldDescriptor()
case MapIndexStep, ListIndexStep:
fd = p.Index(-2).Step.FieldDescriptor()
}
// Format the full path with the last value.
return fmt.Sprintf("%v = %v", p.Path[:n], msgfmt.FormatValue(last.Value, fd))
}

View File

@ -0,0 +1,241 @@
// Copyright 2020 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 protopath
import (
"fmt"
"strconv"
"strings"
"google.golang.org/protobuf/internal/encoding/text"
"google.golang.org/protobuf/reflect/protoreflect"
)
// StepKind identifies the kind of step operation.
// Each kind of step corresponds with some protobuf reflection operation.
type StepKind int
const (
invalidStep StepKind = iota
// RootStep identifies a step as the Root step operation.
RootStep
// FieldAccessStep identifies a step as the FieldAccess step operation.
FieldAccessStep
// UnknownAccessStep identifies a step as the UnknownAccess step operation.
UnknownAccessStep
// ListIndexStep identifies a step as the ListIndex step operation.
ListIndexStep
// MapIndexStep identifies a step as the MapIndex step operation.
MapIndexStep
// AnyExpandStep identifies a step as the AnyExpand step operation.
AnyExpandStep
)
func (k StepKind) String() string {
switch k {
case invalidStep:
return "<invalid>"
case RootStep:
return "Root"
case FieldAccessStep:
return "FieldAccess"
case UnknownAccessStep:
return "UnknownAccess"
case ListIndexStep:
return "ListIndex"
case MapIndexStep:
return "MapIndex"
case AnyExpandStep:
return "AnyExpand"
default:
return fmt.Sprintf("<unknown:%d>", k)
}
}
// Step is a union where only one step operation may be specified at a time.
// The different kinds of steps are specified by the constants defined for
// the StepKind type.
type Step struct {
kind StepKind
desc protoreflect.Descriptor
key protoreflect.Value
}
// Root indicates the root message that a path is relative to.
// It should always (and only ever) be the first step in a path.
func Root(md protoreflect.MessageDescriptor) Step {
if md == nil {
panic("nil message descriptor")
}
return Step{kind: RootStep, desc: md}
}
// FieldAccess describes access of a field within a message.
// Extension field accesses are also represented using a FieldAccess and
// must be provided with a protoreflect.FieldDescriptor
//
// Within the context of Values,
// the type of the previous step value is always a message, and
// the type of the current step value is determined by the field descriptor.
func FieldAccess(fd protoreflect.FieldDescriptor) Step {
if fd == nil {
panic("nil field descriptor")
} else if _, ok := fd.(protoreflect.ExtensionTypeDescriptor); !ok && fd.IsExtension() {
panic(fmt.Sprintf("extension field %q must implement protoreflect.ExtensionTypeDescriptor", fd.FullName()))
}
return Step{kind: FieldAccessStep, desc: fd}
}
// UnknownAccess describes access to the unknown fields within a message.
//
// Within the context of Values,
// the type of the previous step value is always a message, and
// the type of the current step value is always a bytes type.
func UnknownAccess() Step {
return Step{kind: UnknownAccessStep}
}
// ListIndex describes index of an element within a list.
//
// Within the context of Values,
// the type of the previous, previous step value is always a message,
// the type of the previous step value is always a list, and
// the type of the current step value is determined by the field descriptor.
func ListIndex(i int) Step {
if i < 0 {
panic(fmt.Sprintf("invalid list index: %v", i))
}
return Step{kind: ListIndexStep, key: protoreflect.ValueOfInt64(int64(i))}
}
// MapIndex describes index of an entry within a map.
// The key type is determined by field descriptor that the map belongs to.
//
// Within the context of Values,
// the type of the previous previous step value is always a message,
// the type of the previous step value is always a map, and
// the type of the current step value is determined by the field descriptor.
func MapIndex(k protoreflect.MapKey) Step {
if !k.IsValid() {
panic("invalid map index")
}
return Step{kind: MapIndexStep, key: k.Value()}
}
// AnyExpand describes expansion of a google.protobuf.Any message into
// a structured representation of the underlying message.
//
// Within the context of Values,
// the type of the previous step value is always a google.protobuf.Any message, and
// the type of the current step value is always a message.
func AnyExpand(md protoreflect.MessageDescriptor) Step {
if md == nil {
panic("nil message descriptor")
}
return Step{kind: AnyExpandStep, desc: md}
}
// MessageDescriptor returns the message descriptor for Root or AnyExpand steps,
// otherwise it returns nil.
func (s Step) MessageDescriptor() protoreflect.MessageDescriptor {
switch s.kind {
case RootStep, AnyExpandStep:
return s.desc.(protoreflect.MessageDescriptor)
default:
return nil
}
}
// FieldDescriptor returns the field descriptor for FieldAccess steps,
// otherwise it returns nil.
func (s Step) FieldDescriptor() protoreflect.FieldDescriptor {
switch s.kind {
case FieldAccessStep:
return s.desc.(protoreflect.FieldDescriptor)
default:
return nil
}
}
// ListIndex returns the list index for ListIndex steps,
// otherwise it returns 0.
func (s Step) ListIndex() int {
switch s.kind {
case ListIndexStep:
return int(s.key.Int())
default:
return 0
}
}
// MapIndex returns the map key for MapIndex steps,
// otherwise it returns an invalid map key.
func (s Step) MapIndex() protoreflect.MapKey {
switch s.kind {
case MapIndexStep:
return s.key.MapKey()
default:
return protoreflect.MapKey{}
}
}
// Kind reports which kind of step this is.
func (s Step) Kind() StepKind {
return s.kind
}
func (s Step) String() string {
return string(s.appendString(nil))
}
func (s Step) appendString(b []byte) []byte {
switch s.kind {
case RootStep:
b = append(b, '(')
b = append(b, s.desc.FullName()...)
b = append(b, ')')
case FieldAccessStep:
b = append(b, '.')
if fd := s.desc.(protoreflect.FieldDescriptor); fd.IsExtension() {
b = append(b, '(')
b = append(b, strings.Trim(fd.TextName(), "[]")...)
b = append(b, ')')
} else {
b = append(b, fd.TextName()...)
}
case UnknownAccessStep:
b = append(b, '.')
b = append(b, '?')
case ListIndexStep:
b = append(b, '[')
b = strconv.AppendInt(b, s.key.Int(), 10)
b = append(b, ']')
case MapIndexStep:
b = append(b, '[')
switch k := s.key.Interface().(type) {
case bool:
b = strconv.AppendBool(b, bool(k)) // e.g., "true" or "false"
case int32:
b = strconv.AppendInt(b, int64(k), 10) // e.g., "-32"
case int64:
b = strconv.AppendInt(b, int64(k), 10) // e.g., "-64"
case uint32:
b = strconv.AppendUint(b, uint64(k), 10) // e.g., "32"
case uint64:
b = strconv.AppendUint(b, uint64(k), 10) // e.g., "64"
case string:
b = text.AppendString(b, k) // e.g., `"hello, world"`
}
b = append(b, ']')
case AnyExpandStep:
b = append(b, '.')
b = append(b, '(')
b = append(b, s.desc.FullName()...)
b = append(b, ')')
default:
b = append(b, "<invalid>"...)
}
return b
}

View File

@ -0,0 +1,316 @@
// Copyright 2020 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 protorange provides functionality to traverse a message value.
package protorange
import (
"bytes"
"errors"
"google.golang.org/protobuf/internal/genid"
"google.golang.org/protobuf/internal/order"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protopath"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/reflect/protoregistry"
)
var (
// Break breaks traversal of children in the current value.
// It has no effect when traversing values that are not composite types
// (e.g., messages, lists, and maps).
Break = errors.New("break traversal of children in current value")
// Terminate terminates the entire range operation.
// All necessary Pop operations continue to be called.
Terminate = errors.New("terminate range operation")
)
// Range performs a depth-first traversal over reachable values in a message.
//
// See Options.Range for details.
func Range(m protoreflect.Message, f func(protopath.Values) error) error {
return Options{}.Range(m, f, nil)
}
// Options configures traversal of a message value tree.
type Options struct {
// Stable specifies whether to visit message fields and map entries
// in a stable ordering. If false, then the ordering is undefined and
// may be non-deterministic.
//
// Message fields are visited in ascending order by field number.
// Map entries are visited in ascending order, where
// boolean keys are ordered such that false sorts before true,
// numeric keys are ordered based on the numeric value, and
// string keys are lexicographically ordered by Unicode codepoints.
Stable bool
// Resolver is used for looking up types when expanding google.protobuf.Any
// messages. If nil, this defaults to using protoregistry.GlobalTypes.
// To prevent expansion of Any messages, pass an empty protoregistry.Types:
//
// Options{Resolver: (*protoregistry.Types)(nil)}
//
Resolver interface {
protoregistry.ExtensionTypeResolver
protoregistry.MessageTypeResolver
}
}
// Range performs a depth-first traversal over reachable values in a message.
// The first push and the last pop are to push/pop a protopath.Root step.
// If push or pop return any non-nil error (other than Break or Terminate),
// it terminates the traversal and is returned by Range.
//
// The rules for traversing a message is as follows:
//
// • For messages, iterate over every populated known and extension field.
// Each field is preceded by a push of a protopath.FieldAccess step,
// followed by recursive application of the rules on the field value,
// and succeeded by a pop of that step.
// If the message has unknown fields, then push an protopath.UnknownAccess step
// followed immediately by pop of that step.
//
// • As an exception to the above rule, if the current message is a
// google.protobuf.Any message, expand the underlying message (if resolvable).
// The expanded message is preceded by a push of a protopath.AnyExpand step,
// followed by recursive application of the rules on the underlying message,
// and succeeded by a pop of that step. Mutations to the expanded message
// are written back to the Any message when popping back out.
//
// • For lists, iterate over every element. Each element is preceded by a push
// of a protopath.ListIndex step, followed by recursive application of the rules
// on the list element, and succeeded by a pop of that step.
//
// • For maps, iterate over every entry. Each entry is preceded by a push
// of a protopath.MapIndex step, followed by recursive application of the rules
// on the map entry value, and succeeded by a pop of that step.
//
// Mutations should only be made to the last value, otherwise the effects on
// traversal will be undefined. If the mutation is made to the last value
// during to a push, then the effects of the mutation will affect traversal.
// For example, if the last value is currently a message, and the push function
// populates a few fields in that message, then the newly modified fields
// will be traversed.
//
// The protopath.Values provided to push functions is only valid until the
// corresponding pop call and the values provided to a pop call is only valid
// for the duration of the pop call itself.
func (o Options) Range(m protoreflect.Message, push, pop func(protopath.Values) error) error {
var err error
p := new(protopath.Values)
if o.Resolver == nil {
o.Resolver = protoregistry.GlobalTypes
}
pushStep(p, protopath.Root(m.Descriptor()), protoreflect.ValueOfMessage(m))
if push != nil {
err = amendError(err, push(*p))
}
if err == nil {
err = o.rangeMessage(p, m, push, pop)
}
if pop != nil {
err = amendError(err, pop(*p))
}
popStep(p)
if err == Break || err == Terminate {
err = nil
}
return err
}
func (o Options) rangeMessage(p *protopath.Values, m protoreflect.Message, push, pop func(protopath.Values) error) (err error) {
if ok, err := o.rangeAnyMessage(p, m, push, pop); ok {
return err
}
fieldOrder := order.AnyFieldOrder
if o.Stable {
fieldOrder = order.NumberFieldOrder
}
order.RangeFields(m, fieldOrder, func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
pushStep(p, protopath.FieldAccess(fd), v)
if push != nil {
err = amendError(err, push(*p))
}
if err == nil {
switch {
case fd.IsMap():
err = o.rangeMap(p, fd, v.Map(), push, pop)
case fd.IsList():
err = o.rangeList(p, fd, v.List(), push, pop)
case fd.Message() != nil:
err = o.rangeMessage(p, v.Message(), push, pop)
}
}
if pop != nil {
err = amendError(err, pop(*p))
}
popStep(p)
return err == nil
})
if b := m.GetUnknown(); len(b) > 0 && err == nil {
pushStep(p, protopath.UnknownAccess(), protoreflect.ValueOfBytes(b))
if push != nil {
err = amendError(err, push(*p))
}
if pop != nil {
err = amendError(err, pop(*p))
}
popStep(p)
}
if err == Break {
err = nil
}
return err
}
func (o Options) rangeAnyMessage(p *protopath.Values, m protoreflect.Message, push, pop func(protopath.Values) error) (ok bool, err error) {
md := m.Descriptor()
if md.FullName() != "google.protobuf.Any" {
return false, nil
}
fds := md.Fields()
url := m.Get(fds.ByNumber(genid.Any_TypeUrl_field_number)).String()
val := m.Get(fds.ByNumber(genid.Any_Value_field_number)).Bytes()
mt, errFind := o.Resolver.FindMessageByURL(url)
if errFind != nil {
return false, nil
}
// Unmarshal the raw encoded message value into a structured message value.
m2 := mt.New()
errUnmarshal := proto.UnmarshalOptions{
Merge: true,
AllowPartial: true,
Resolver: o.Resolver,
}.Unmarshal(val, m2.Interface())
if errUnmarshal != nil {
// If the the underlying message cannot be unmarshaled,
// then just treat this as an normal message type.
return false, nil
}
// Marshal Any before ranging to detect possible mutations.
b1, errMarshal := proto.MarshalOptions{
AllowPartial: true,
Deterministic: true,
}.Marshal(m2.Interface())
if errMarshal != nil {
return true, errMarshal
}
pushStep(p, protopath.AnyExpand(m2.Descriptor()), protoreflect.ValueOfMessage(m2))
if push != nil {
err = amendError(err, push(*p))
}
if err == nil {
err = o.rangeMessage(p, m2, push, pop)
}
if pop != nil {
err = amendError(err, pop(*p))
}
popStep(p)
// Marshal Any after ranging to detect possible mutations.
b2, errMarshal := proto.MarshalOptions{
AllowPartial: true,
Deterministic: true,
}.Marshal(m2.Interface())
if errMarshal != nil {
return true, errMarshal
}
// Mutations detected, write the new sequence of bytes to the Any message.
if !bytes.Equal(b1, b2) {
m.Set(fds.ByNumber(genid.Any_Value_field_number), protoreflect.ValueOfBytes(b2))
}
if err == Break {
err = nil
}
return true, err
}
func (o Options) rangeList(p *protopath.Values, fd protoreflect.FieldDescriptor, ls protoreflect.List, push, pop func(protopath.Values) error) (err error) {
for i := 0; i < ls.Len() && err == nil; i++ {
v := ls.Get(i)
pushStep(p, protopath.ListIndex(i), v)
if push != nil {
err = amendError(err, push(*p))
}
if err == nil && fd.Message() != nil {
err = o.rangeMessage(p, v.Message(), push, pop)
}
if pop != nil {
err = amendError(err, pop(*p))
}
popStep(p)
}
if err == Break {
err = nil
}
return err
}
func (o Options) rangeMap(p *protopath.Values, fd protoreflect.FieldDescriptor, ms protoreflect.Map, push, pop func(protopath.Values) error) (err error) {
keyOrder := order.AnyKeyOrder
if o.Stable {
keyOrder = order.GenericKeyOrder
}
order.RangeEntries(ms, keyOrder, func(k protoreflect.MapKey, v protoreflect.Value) bool {
pushStep(p, protopath.MapIndex(k), v)
if push != nil {
err = amendError(err, push(*p))
}
if err == nil && fd.MapValue().Message() != nil {
err = o.rangeMessage(p, v.Message(), push, pop)
}
if pop != nil {
err = amendError(err, pop(*p))
}
popStep(p)
return err == nil
})
if err == Break {
err = nil
}
return err
}
func pushStep(p *protopath.Values, s protopath.Step, v protoreflect.Value) {
p.Path = append(p.Path, s)
p.Values = append(p.Values, v)
}
func popStep(p *protopath.Values) {
p.Path = p.Path[:len(p.Path)-1]
p.Values = p.Values[:len(p.Values)-1]
}
// amendError amends the previous error with the current error if it is
// considered more serious. The precedence order for errors is:
//
// nil < Break < Terminate < previous non-nil < current non-nil
func amendError(prev, curr error) error {
switch {
case curr == nil:
return prev
case curr == Break && prev != nil:
return prev
case curr == Terminate && prev != nil && prev != Break:
return prev
default:
return curr
}
}

View File

@ -35,6 +35,8 @@ func (p *SourcePath) appendFileDescriptorProto(b []byte) []byte {
b = p.appendSingularField(b, "source_code_info", (*SourcePath).appendSourceCodeInfo)
case 12:
b = p.appendSingularField(b, "syntax", nil)
case 13:
b = p.appendSingularField(b, "edition", nil)
}
return b
}
@ -236,6 +238,8 @@ func (p *SourcePath) appendMessageOptions(b []byte) []byte {
b = p.appendSingularField(b, "deprecated", nil)
case 7:
b = p.appendSingularField(b, "map_entry", nil)
case 11:
b = p.appendSingularField(b, "deprecated_legacy_json_field_conflicts", nil)
case 999:
b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
}
@ -279,6 +283,8 @@ func (p *SourcePath) appendEnumOptions(b []byte) []byte {
b = p.appendSingularField(b, "allow_alias", nil)
case 3:
b = p.appendSingularField(b, "deprecated", nil)
case 6:
b = p.appendSingularField(b, "deprecated_legacy_json_field_conflicts", nil)
case 999:
b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
}
@ -345,10 +351,18 @@ func (p *SourcePath) appendFieldOptions(b []byte) []byte {
b = p.appendSingularField(b, "jstype", nil)
case 5:
b = p.appendSingularField(b, "lazy", nil)
case 15:
b = p.appendSingularField(b, "unverified_lazy", nil)
case 3:
b = p.appendSingularField(b, "deprecated", nil)
case 10:
b = p.appendSingularField(b, "weak", nil)
case 16:
b = p.appendSingularField(b, "debug_redact", nil)
case 17:
b = p.appendSingularField(b, "retention", nil)
case 18:
b = p.appendSingularField(b, "target", nil)
case 999:
b = p.appendRepeatedField(b, "uninterpreted_option", (*SourcePath).appendUninterpretedOption)
}

View File

@ -148,7 +148,7 @@ type Message interface {
// be preserved in marshaling or other operations.
IsValid() bool
// ProtoMethods returns optional fast-path implementions of various operations.
// ProtoMethods returns optional fast-path implementations of various operations.
// This method may return nil.
//
// The returned methods type is identical to

View File

@ -0,0 +1,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)
}

View File

@ -54,11 +54,11 @@ import (
// // Append a 0 to a "repeated int32" field.
// // Since the Value returned by Mutable is guaranteed to alias
// // the source message, modifying the Value modifies the message.
// message.Mutable(fieldDesc).(List).Append(protoreflect.ValueOfInt32(0))
// message.Mutable(fieldDesc).List().Append(protoreflect.ValueOfInt32(0))
//
// // Assign [0] to a "repeated int32" field by creating a new Value,
// // modifying it, and assigning it.
// list := message.NewField(fieldDesc).(List)
// list := message.NewField(fieldDesc).List()
// list.Append(protoreflect.ValueOfInt32(0))
// message.Set(fieldDesc, list)
// // ERROR: Since it is not defined whether Set aliases the source,

View File

@ -46,7 +46,7 @@ var conflictPolicy = "panic" // "panic" | "warn" | "ignore"
// It is a variable so that the behavior is easily overridden in another file.
var ignoreConflict = func(d protoreflect.Descriptor, err error) bool {
const env = "GOLANG_PROTOBUF_REGISTRATION_CONFLICT"
const faq = "https://developers.google.com/protocol-buffers/docs/reference/go/faq#namespace-conflict"
const faq = "https://protobuf.dev/reference/go/faq#namespace-conflict"
policy := conflictPolicy
if v := os.Getenv(env); v != "" {
policy = v