mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2024-12-27 15:26:51 +00:00
1c28304cc5
Instead of accepting a concrete protoregistry.Types type, accept an interface that provides the necessary functionality to perform the serialization. The advantages of this approach: * There is no need for complex logic to allow a Parent or custom Resolver on the protoregistry.Types type. * Users can pass their own custom resolver implementations directly to the serialization functions. * This is a more principled approach to plumbing custom resolvers than the previous approach of overloading behavior on the concrete Types type. The disadvantages of this approach: * A pointer to a concrete type is 8B, while an interface is 16B. However, the expansion of the {Marshal,Unmarshal}Options structs should be a concern solved separately from how to plumb custom resolvers. * The resolver interfaces as defined today may be insufficient to provide functionality needed in the future if protobuf expands its feature set. For example, let's suppose the Any message permits directly representing a enum by name. This would require the ability to lookup an enum by name. To support that hypothetical need, we can document that the serializers type-assert the provided Resolver to a EnumTypeResolver and use that if possible. There is some loss of type safety with this approach, but provides a clear path forward. Change-Id: I81ca80e59335d36be6b43d57ec8e17abfdfa3bad Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/177044 Reviewed-by: Damien Neil <dneil@google.com>
246 lines
6.9 KiB
Go
246 lines
6.9 KiB
Go
// Copyright 2018 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 proto
|
|
|
|
import (
|
|
"google.golang.org/protobuf/internal/encoding/wire"
|
|
"google.golang.org/protobuf/internal/errors"
|
|
"google.golang.org/protobuf/internal/pragma"
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
|
"google.golang.org/protobuf/reflect/protoregistry"
|
|
"google.golang.org/protobuf/runtime/protoiface"
|
|
)
|
|
|
|
// UnmarshalOptions configures the unmarshaler.
|
|
//
|
|
// Example usage:
|
|
// err := UnmarshalOptions{DiscardUnknown: true}.Unmarshal(b, m)
|
|
type UnmarshalOptions struct {
|
|
// AllowPartial accepts input for messages that will result in missing
|
|
// required fields. If AllowPartial is false (the default), Unmarshal will
|
|
// return an error if there are any missing required fields.
|
|
AllowPartial bool
|
|
|
|
// If DiscardUnknown is set, unknown fields are ignored.
|
|
DiscardUnknown bool
|
|
|
|
// Resolver is used for looking up types when unmarshaling extension fields.
|
|
// If nil, this defaults to using protoregistry.GlobalTypes.
|
|
Resolver interface {
|
|
protoregistry.ExtensionTypeResolver
|
|
}
|
|
|
|
pragma.NoUnkeyedLiterals
|
|
}
|
|
|
|
var _ = protoiface.UnmarshalOptions(UnmarshalOptions{})
|
|
|
|
// Unmarshal parses the wire-format message in b and places the result in m.
|
|
func Unmarshal(b []byte, m Message) error {
|
|
return UnmarshalOptions{}.Unmarshal(b, m)
|
|
}
|
|
|
|
// Unmarshal parses the wire-format message in b and places the result in m.
|
|
func (o UnmarshalOptions) Unmarshal(b []byte, m Message) error {
|
|
if o.Resolver == nil {
|
|
o.Resolver = protoregistry.GlobalTypes
|
|
}
|
|
|
|
// TODO: Reset m?
|
|
err := o.unmarshalMessageFast(b, m)
|
|
if err == errInternalNoFast {
|
|
err = o.unmarshalMessage(b, m.ProtoReflect())
|
|
}
|
|
var nerr errors.NonFatal
|
|
if !nerr.Merge(err) {
|
|
return err
|
|
}
|
|
if !o.AllowPartial {
|
|
nerr.Merge(IsInitialized(m))
|
|
}
|
|
return nerr.E
|
|
}
|
|
|
|
func (o UnmarshalOptions) unmarshalMessageFast(b []byte, m Message) error {
|
|
methods := protoMethods(m)
|
|
if methods == nil || methods.Unmarshal == nil {
|
|
return errInternalNoFast
|
|
}
|
|
return methods.Unmarshal(b, m, protoiface.UnmarshalOptions(o))
|
|
}
|
|
|
|
func (o UnmarshalOptions) unmarshalMessage(b []byte, m protoreflect.Message) error {
|
|
messageDesc := m.Descriptor()
|
|
fieldDescs := messageDesc.Fields()
|
|
knownFields := m.KnownFields()
|
|
unknownFields := m.UnknownFields()
|
|
var nerr errors.NonFatal
|
|
for len(b) > 0 {
|
|
// Parse the tag (field number and wire type).
|
|
num, wtyp, tagLen := wire.ConsumeTag(b)
|
|
if tagLen < 0 {
|
|
return wire.ParseError(tagLen)
|
|
}
|
|
|
|
// Parse the field value.
|
|
fieldDesc := fieldDescs.ByNumber(num)
|
|
if fieldDesc == nil {
|
|
extType := knownFields.ExtensionTypes().ByNumber(num)
|
|
if extType == nil && messageDesc.ExtensionRanges().Has(num) {
|
|
var err error
|
|
extType, err = o.Resolver.FindExtensionByNumber(messageDesc.FullName(), num)
|
|
if err != nil && err != protoregistry.NotFound {
|
|
return err
|
|
}
|
|
if extType != nil {
|
|
knownFields.ExtensionTypes().Register(extType)
|
|
}
|
|
}
|
|
if extType != nil {
|
|
fieldDesc = extType.Descriptor()
|
|
}
|
|
}
|
|
var err error
|
|
var valLen int
|
|
switch {
|
|
case fieldDesc == nil:
|
|
err = errUnknown
|
|
case fieldDesc.IsList():
|
|
valLen, err = o.unmarshalList(b[tagLen:], wtyp, num, knownFields.Get(num).List(), fieldDesc)
|
|
case fieldDesc.IsMap():
|
|
valLen, err = o.unmarshalMap(b[tagLen:], wtyp, num, knownFields.Get(num).Map(), fieldDesc)
|
|
default:
|
|
valLen, err = o.unmarshalScalarField(b[tagLen:], wtyp, num, knownFields, fieldDesc)
|
|
}
|
|
if err == errUnknown {
|
|
valLen = wire.ConsumeFieldValue(num, wtyp, b[tagLen:])
|
|
if valLen < 0 {
|
|
return wire.ParseError(valLen)
|
|
}
|
|
unknownFields.Set(num, append(unknownFields.Get(num), b[:tagLen+valLen]...))
|
|
} else if !nerr.Merge(err) {
|
|
return err
|
|
}
|
|
b = b[tagLen+valLen:]
|
|
}
|
|
return nerr.E
|
|
}
|
|
|
|
func (o UnmarshalOptions) unmarshalScalarField(b []byte, wtyp wire.Type, num wire.Number, knownFields protoreflect.KnownFields, field protoreflect.FieldDescriptor) (n int, err error) {
|
|
var nerr errors.NonFatal
|
|
v, n, err := o.unmarshalScalar(b, wtyp, num, field)
|
|
if !nerr.Merge(err) {
|
|
return 0, err
|
|
}
|
|
switch field.Kind() {
|
|
case protoreflect.GroupKind, protoreflect.MessageKind:
|
|
// Messages are merged with any existing message value,
|
|
// unless the message is part of a oneof.
|
|
//
|
|
// TODO: C++ merges into oneofs, while v1 does not.
|
|
// Evaluate which behavior to pick.
|
|
var m protoreflect.Message
|
|
if knownFields.Has(num) && field.ContainingOneof() == nil {
|
|
m = knownFields.Get(num).Message()
|
|
} else {
|
|
m = knownFields.NewMessage(num)
|
|
knownFields.Set(num, protoreflect.ValueOf(m))
|
|
}
|
|
// Pass up errors (fatal and otherwise).
|
|
if err := o.unmarshalMessage(v.Bytes(), m); !nerr.Merge(err) {
|
|
return n, err
|
|
}
|
|
default:
|
|
// Non-message scalars replace the previous value.
|
|
knownFields.Set(num, v)
|
|
}
|
|
return n, nerr.E
|
|
}
|
|
|
|
func (o UnmarshalOptions) unmarshalMap(b []byte, wtyp wire.Type, num wire.Number, mapv protoreflect.Map, field protoreflect.FieldDescriptor) (n int, err error) {
|
|
if wtyp != wire.BytesType {
|
|
return 0, errUnknown
|
|
}
|
|
b, n = wire.ConsumeBytes(b)
|
|
if n < 0 {
|
|
return 0, wire.ParseError(n)
|
|
}
|
|
var (
|
|
keyField = field.MapKey()
|
|
valField = field.MapValue()
|
|
key protoreflect.Value
|
|
val protoreflect.Value
|
|
haveKey bool
|
|
haveVal bool
|
|
)
|
|
switch valField.Kind() {
|
|
case protoreflect.GroupKind, protoreflect.MessageKind:
|
|
val = protoreflect.ValueOf(mapv.NewMessage())
|
|
}
|
|
// Map entries are represented as a two-element message with fields
|
|
// containing the key and value.
|
|
var nerr errors.NonFatal
|
|
for len(b) > 0 {
|
|
num, wtyp, n := wire.ConsumeTag(b)
|
|
if n < 0 {
|
|
return 0, wire.ParseError(n)
|
|
}
|
|
b = b[n:]
|
|
err = errUnknown
|
|
switch num {
|
|
case 1:
|
|
key, n, err = o.unmarshalScalar(b, wtyp, num, keyField)
|
|
if !nerr.Merge(err) {
|
|
break
|
|
}
|
|
err = nil
|
|
haveKey = true
|
|
case 2:
|
|
var v protoreflect.Value
|
|
v, n, err = o.unmarshalScalar(b, wtyp, num, valField)
|
|
if !nerr.Merge(err) {
|
|
break
|
|
}
|
|
err = nil
|
|
switch valField.Kind() {
|
|
case protoreflect.GroupKind, protoreflect.MessageKind:
|
|
if err := o.unmarshalMessage(v.Bytes(), val.Message()); !nerr.Merge(err) {
|
|
return 0, err
|
|
}
|
|
default:
|
|
val = v
|
|
}
|
|
haveVal = true
|
|
}
|
|
if err == errUnknown {
|
|
n = wire.ConsumeFieldValue(num, wtyp, b)
|
|
if n < 0 {
|
|
return 0, wire.ParseError(n)
|
|
}
|
|
} else if err != nil {
|
|
return 0, err
|
|
}
|
|
b = b[n:]
|
|
}
|
|
// Every map entry should have entries for key and value, but this is not strictly required.
|
|
if !haveKey {
|
|
key = keyField.Default()
|
|
}
|
|
if !haveVal {
|
|
switch valField.Kind() {
|
|
case protoreflect.GroupKind, protoreflect.MessageKind:
|
|
default:
|
|
val = valField.Default()
|
|
}
|
|
}
|
|
mapv.Set(key.MapKey(), val)
|
|
return n, nerr.E
|
|
}
|
|
|
|
// errUnknown is used internally to indicate fields which should be added
|
|
// to the unknown field set of a message. It is never returned from an exported
|
|
// function.
|
|
var errUnknown = errors.New("BUG: internal error (unknown)")
|