protobuf-go/proto/decode.go

295 lines
8.5 KiB
Go
Raw Normal View History

// 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/encoding/protowire"
"google.golang.org/protobuf/internal/encoding/messageset"
"google.golang.org/protobuf/internal/errors"
"google.golang.org/protobuf/internal/flags"
"google.golang.org/protobuf/internal/genid"
"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 {
pragma.NoUnkeyedLiterals
// Merge merges the input into the destination message.
// The default behavior is to always reset the message before unmarshaling,
// unless Merge is specified.
Merge bool
// 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.
proto, encoding/protojson, encoding/prototext: use Resolver interface 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>
2019-05-14 14:28:19 -07:00
Resolver interface {
FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error)
FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error)
proto, encoding/protojson, encoding/prototext: use Resolver interface 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>
2019-05-14 14:28:19 -07:00
}
all: implement depth limit for unmarshaling + This change introduce a default and configurable depth limit for proto.Unmarshal. If a message is nested deeper than the limit, unmarshaling will fail. There are two ways to nest messages. Either by having fields which are message types itself or by using groups. + The default limit is 10,000 for now. This might change in the future to align it with other language implementation (C++ and Java use 100 as limit). + If pure groups (groups that don't contain message fields) are nested deeper than the default limit the unmarshaling fails with: proto: cannot parse invalid wire-format data + Note: the configured limit does not apply to pure groups. + This change is introduced to improve security and robustness. Because unmarshaling is implemented using recursion it can lead to stack overflows for certain inputs. The introduced limit protects against this. + A secondary motivation for this limit is the alignment with other languages. Protocol buffers are a language interoperability mechanism and thus either all implementations should accept the input or all implementation should reject the input. Change-Id: I14bdb44d06e4bd1aa90d6336c2cf6446003b2037 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/385854 Trust: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Damien Neil <dneil@google.com> Trust: Damien Neil <dneil@google.com> Reviewed-by: Nicolas Hillegeer <aktau@google.com> Reviewed-by: Chressie Himpel <chressie@google.com>
2022-02-15 10:03:20 +01:00
// RecursionLimit limits how deeply messages may be nested.
// If zero, a default limit is applied.
RecursionLimit int
}
// Unmarshal parses the wire-format message in b and places the result in m.
// The provided message must be mutable (e.g., a non-nil pointer to a message).
func Unmarshal(b []byte, m Message) error {
all: implement depth limit for unmarshaling + This change introduce a default and configurable depth limit for proto.Unmarshal. If a message is nested deeper than the limit, unmarshaling will fail. There are two ways to nest messages. Either by having fields which are message types itself or by using groups. + The default limit is 10,000 for now. This might change in the future to align it with other language implementation (C++ and Java use 100 as limit). + If pure groups (groups that don't contain message fields) are nested deeper than the default limit the unmarshaling fails with: proto: cannot parse invalid wire-format data + Note: the configured limit does not apply to pure groups. + This change is introduced to improve security and robustness. Because unmarshaling is implemented using recursion it can lead to stack overflows for certain inputs. The introduced limit protects against this. + A secondary motivation for this limit is the alignment with other languages. Protocol buffers are a language interoperability mechanism and thus either all implementations should accept the input or all implementation should reject the input. Change-Id: I14bdb44d06e4bd1aa90d6336c2cf6446003b2037 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/385854 Trust: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Damien Neil <dneil@google.com> Trust: Damien Neil <dneil@google.com> Reviewed-by: Nicolas Hillegeer <aktau@google.com> Reviewed-by: Chressie Himpel <chressie@google.com>
2022-02-15 10:03:20 +01:00
_, err := UnmarshalOptions{RecursionLimit: protowire.DefaultRecursionLimit}.unmarshal(b, m.ProtoReflect())
return err
}
// Unmarshal parses the wire-format message in b and places the result in m.
// The provided message must be mutable (e.g., a non-nil pointer to a message).
func (o UnmarshalOptions) Unmarshal(b []byte, m Message) error {
all: implement depth limit for unmarshaling + This change introduce a default and configurable depth limit for proto.Unmarshal. If a message is nested deeper than the limit, unmarshaling will fail. There are two ways to nest messages. Either by having fields which are message types itself or by using groups. + The default limit is 10,000 for now. This might change in the future to align it with other language implementation (C++ and Java use 100 as limit). + If pure groups (groups that don't contain message fields) are nested deeper than the default limit the unmarshaling fails with: proto: cannot parse invalid wire-format data + Note: the configured limit does not apply to pure groups. + This change is introduced to improve security and robustness. Because unmarshaling is implemented using recursion it can lead to stack overflows for certain inputs. The introduced limit protects against this. + A secondary motivation for this limit is the alignment with other languages. Protocol buffers are a language interoperability mechanism and thus either all implementations should accept the input or all implementation should reject the input. Change-Id: I14bdb44d06e4bd1aa90d6336c2cf6446003b2037 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/385854 Trust: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Damien Neil <dneil@google.com> Trust: Damien Neil <dneil@google.com> Reviewed-by: Nicolas Hillegeer <aktau@google.com> Reviewed-by: Chressie Himpel <chressie@google.com>
2022-02-15 10:03:20 +01:00
if o.RecursionLimit == 0 {
o.RecursionLimit = protowire.DefaultRecursionLimit
}
_, err := o.unmarshal(b, m.ProtoReflect())
return err
}
// UnmarshalState parses a wire-format message and places the result in m.
//
// This method permits fine-grained control over the unmarshaler.
// Most users should use [Unmarshal] instead.
func (o UnmarshalOptions) UnmarshalState(in protoiface.UnmarshalInput) (protoiface.UnmarshalOutput, error) {
all: implement depth limit for unmarshaling + This change introduce a default and configurable depth limit for proto.Unmarshal. If a message is nested deeper than the limit, unmarshaling will fail. There are two ways to nest messages. Either by having fields which are message types itself or by using groups. + The default limit is 10,000 for now. This might change in the future to align it with other language implementation (C++ and Java use 100 as limit). + If pure groups (groups that don't contain message fields) are nested deeper than the default limit the unmarshaling fails with: proto: cannot parse invalid wire-format data + Note: the configured limit does not apply to pure groups. + This change is introduced to improve security and robustness. Because unmarshaling is implemented using recursion it can lead to stack overflows for certain inputs. The introduced limit protects against this. + A secondary motivation for this limit is the alignment with other languages. Protocol buffers are a language interoperability mechanism and thus either all implementations should accept the input or all implementation should reject the input. Change-Id: I14bdb44d06e4bd1aa90d6336c2cf6446003b2037 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/385854 Trust: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Damien Neil <dneil@google.com> Trust: Damien Neil <dneil@google.com> Reviewed-by: Nicolas Hillegeer <aktau@google.com> Reviewed-by: Chressie Himpel <chressie@google.com>
2022-02-15 10:03:20 +01:00
if o.RecursionLimit == 0 {
o.RecursionLimit = protowire.DefaultRecursionLimit
}
return o.unmarshal(in.Buf, in.Message)
}
// unmarshal is a centralized function that all unmarshal operations go through.
// For profiling purposes, avoid changing the name of this function or
// introducing other code paths for unmarshal that do not go through this.
func (o UnmarshalOptions) unmarshal(b []byte, m protoreflect.Message) (out protoiface.UnmarshalOutput, err error) {
if o.Resolver == nil {
o.Resolver = protoregistry.GlobalTypes
}
if !o.Merge {
Reset(m.Interface())
}
allowPartial := o.AllowPartial
o.Merge = true
o.AllowPartial = true
methods := protoMethods(m)
if methods != nil && methods.Unmarshal != nil &&
!(o.DiscardUnknown && methods.Flags&protoiface.SupportUnmarshalDiscardUnknown == 0) {
in := protoiface.UnmarshalInput{
Message: m,
Buf: b,
runtime/protoiface: use more efficient options representation Change the representation of option flags in protoiface from bools to a bitfield. This brings the representation of options in protoiface in sync with that in internal/impl. This change has several benefits: 1. We will probably find that we need to add more option flags over time. Converting to the more efficient representation of these flags as high in the call stack as possible minimizes the performance implication of the struct growing. 2. On a similar note, this avoids the need to convert from the compact representation to the larger one when passing from internal/impl to proto, since the {Marshal,Unmarshal}State methods take the compact form. 3. This removes unused options from protoiface. Instead of documenting that AllowPartial is always set, we can just not include an AllowPartial flag in the protoiface options. 4. Conversely, this provides a way to add option flags to protoiface that we don't want to expose in the proto package. name old time/op new time/op delta EmptyMessage/Wire/Marshal-12 11.1ns ± 7% 10.1ns ± 1% -9.35% (p=0.000 n=8+8) EmptyMessage/Wire/Unmarshal-12 7.07ns ± 0% 6.74ns ± 1% -4.58% (p=0.000 n=8+8) EmptyMessage/Wire/Validate-12 4.30ns ± 1% 3.80ns ± 8% -11.45% (p=0.000 n=7+8) RepeatedInt32/Wire/Marshal-12 1.17µs ± 1% 1.21µs ± 7% +4.09% (p=0.000 n=8+8) RepeatedInt32/Wire/Unmarshal-12 938ns ± 0% 942ns ± 3% ~ (p=0.178 n=7+8) RepeatedInt32/Wire/Validate-12 521ns ± 4% 543ns ± 7% ~ (p=0.157 n=7+8) Required/Wire/Marshal-12 97.2ns ± 1% 95.3ns ± 1% -1.98% (p=0.001 n=7+7) Required/Wire/Unmarshal-12 41.0ns ± 9% 38.6ns ± 3% -5.73% (p=0.048 n=8+8) Required/Wire/Validate-12 25.4ns ±11% 21.4ns ± 3% -15.62% (p=0.000 n=8+7) Change-Id: I3ac1b00ab36cfdf61316ec087a5dd20d9248e4f6 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/216760 Reviewed-by: Joe Tsai <joetsai@google.com>
2020-01-28 13:32:01 -08:00
Resolver: o.Resolver,
all: implement depth limit for unmarshaling + This change introduce a default and configurable depth limit for proto.Unmarshal. If a message is nested deeper than the limit, unmarshaling will fail. There are two ways to nest messages. Either by having fields which are message types itself or by using groups. + The default limit is 10,000 for now. This might change in the future to align it with other language implementation (C++ and Java use 100 as limit). + If pure groups (groups that don't contain message fields) are nested deeper than the default limit the unmarshaling fails with: proto: cannot parse invalid wire-format data + Note: the configured limit does not apply to pure groups. + This change is introduced to improve security and robustness. Because unmarshaling is implemented using recursion it can lead to stack overflows for certain inputs. The introduced limit protects against this. + A secondary motivation for this limit is the alignment with other languages. Protocol buffers are a language interoperability mechanism and thus either all implementations should accept the input or all implementation should reject the input. Change-Id: I14bdb44d06e4bd1aa90d6336c2cf6446003b2037 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/385854 Trust: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Damien Neil <dneil@google.com> Trust: Damien Neil <dneil@google.com> Reviewed-by: Nicolas Hillegeer <aktau@google.com> Reviewed-by: Chressie Himpel <chressie@google.com>
2022-02-15 10:03:20 +01:00
Depth: o.RecursionLimit,
runtime/protoiface: use more efficient options representation Change the representation of option flags in protoiface from bools to a bitfield. This brings the representation of options in protoiface in sync with that in internal/impl. This change has several benefits: 1. We will probably find that we need to add more option flags over time. Converting to the more efficient representation of these flags as high in the call stack as possible minimizes the performance implication of the struct growing. 2. On a similar note, this avoids the need to convert from the compact representation to the larger one when passing from internal/impl to proto, since the {Marshal,Unmarshal}State methods take the compact form. 3. This removes unused options from protoiface. Instead of documenting that AllowPartial is always set, we can just not include an AllowPartial flag in the protoiface options. 4. Conversely, this provides a way to add option flags to protoiface that we don't want to expose in the proto package. name old time/op new time/op delta EmptyMessage/Wire/Marshal-12 11.1ns ± 7% 10.1ns ± 1% -9.35% (p=0.000 n=8+8) EmptyMessage/Wire/Unmarshal-12 7.07ns ± 0% 6.74ns ± 1% -4.58% (p=0.000 n=8+8) EmptyMessage/Wire/Validate-12 4.30ns ± 1% 3.80ns ± 8% -11.45% (p=0.000 n=7+8) RepeatedInt32/Wire/Marshal-12 1.17µs ± 1% 1.21µs ± 7% +4.09% (p=0.000 n=8+8) RepeatedInt32/Wire/Unmarshal-12 938ns ± 0% 942ns ± 3% ~ (p=0.178 n=7+8) RepeatedInt32/Wire/Validate-12 521ns ± 4% 543ns ± 7% ~ (p=0.157 n=7+8) Required/Wire/Marshal-12 97.2ns ± 1% 95.3ns ± 1% -1.98% (p=0.001 n=7+7) Required/Wire/Unmarshal-12 41.0ns ± 9% 38.6ns ± 3% -5.73% (p=0.048 n=8+8) Required/Wire/Validate-12 25.4ns ±11% 21.4ns ± 3% -15.62% (p=0.000 n=8+7) Change-Id: I3ac1b00ab36cfdf61316ec087a5dd20d9248e4f6 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/216760 Reviewed-by: Joe Tsai <joetsai@google.com>
2020-01-28 13:32:01 -08:00
}
if o.DiscardUnknown {
in.Flags |= protoiface.UnmarshalDiscardUnknown
runtime/protoiface: use more efficient options representation Change the representation of option flags in protoiface from bools to a bitfield. This brings the representation of options in protoiface in sync with that in internal/impl. This change has several benefits: 1. We will probably find that we need to add more option flags over time. Converting to the more efficient representation of these flags as high in the call stack as possible minimizes the performance implication of the struct growing. 2. On a similar note, this avoids the need to convert from the compact representation to the larger one when passing from internal/impl to proto, since the {Marshal,Unmarshal}State methods take the compact form. 3. This removes unused options from protoiface. Instead of documenting that AllowPartial is always set, we can just not include an AllowPartial flag in the protoiface options. 4. Conversely, this provides a way to add option flags to protoiface that we don't want to expose in the proto package. name old time/op new time/op delta EmptyMessage/Wire/Marshal-12 11.1ns ± 7% 10.1ns ± 1% -9.35% (p=0.000 n=8+8) EmptyMessage/Wire/Unmarshal-12 7.07ns ± 0% 6.74ns ± 1% -4.58% (p=0.000 n=8+8) EmptyMessage/Wire/Validate-12 4.30ns ± 1% 3.80ns ± 8% -11.45% (p=0.000 n=7+8) RepeatedInt32/Wire/Marshal-12 1.17µs ± 1% 1.21µs ± 7% +4.09% (p=0.000 n=8+8) RepeatedInt32/Wire/Unmarshal-12 938ns ± 0% 942ns ± 3% ~ (p=0.178 n=7+8) RepeatedInt32/Wire/Validate-12 521ns ± 4% 543ns ± 7% ~ (p=0.157 n=7+8) Required/Wire/Marshal-12 97.2ns ± 1% 95.3ns ± 1% -1.98% (p=0.001 n=7+7) Required/Wire/Unmarshal-12 41.0ns ± 9% 38.6ns ± 3% -5.73% (p=0.048 n=8+8) Required/Wire/Validate-12 25.4ns ±11% 21.4ns ± 3% -15.62% (p=0.000 n=8+7) Change-Id: I3ac1b00ab36cfdf61316ec087a5dd20d9248e4f6 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/216760 Reviewed-by: Joe Tsai <joetsai@google.com>
2020-01-28 13:32:01 -08:00
}
out, err = methods.Unmarshal(in)
} else {
all: implement depth limit for unmarshaling + This change introduce a default and configurable depth limit for proto.Unmarshal. If a message is nested deeper than the limit, unmarshaling will fail. There are two ways to nest messages. Either by having fields which are message types itself or by using groups. + The default limit is 10,000 for now. This might change in the future to align it with other language implementation (C++ and Java use 100 as limit). + If pure groups (groups that don't contain message fields) are nested deeper than the default limit the unmarshaling fails with: proto: cannot parse invalid wire-format data + Note: the configured limit does not apply to pure groups. + This change is introduced to improve security and robustness. Because unmarshaling is implemented using recursion it can lead to stack overflows for certain inputs. The introduced limit protects against this. + A secondary motivation for this limit is the alignment with other languages. Protocol buffers are a language interoperability mechanism and thus either all implementations should accept the input or all implementation should reject the input. Change-Id: I14bdb44d06e4bd1aa90d6336c2cf6446003b2037 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/385854 Trust: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Damien Neil <dneil@google.com> Trust: Damien Neil <dneil@google.com> Reviewed-by: Nicolas Hillegeer <aktau@google.com> Reviewed-by: Chressie Himpel <chressie@google.com>
2022-02-15 10:03:20 +01:00
o.RecursionLimit--
if o.RecursionLimit < 0 {
return out, errors.New("exceeded max recursion depth")
}
err = o.unmarshalMessageSlow(b, m)
}
if err != nil {
return out, err
}
if allowPartial || (out.Flags&protoiface.UnmarshalInitialized != 0) {
return out, nil
}
return out, checkInitialized(m)
}
runtime/protoiface: move and rename XXX_Methods This CL moves and renames the protoreflect.ProtoMessage.XXX_Methods to protoreflect.Message.ProtoMethods. Since one needs to obtain a protoreflect.Message now to get at the fast-path methods, we modify the method signatures to take in a protoreflect.Message instead of protoreflect.ProtoMessage. Doing so also avoids the wrapper hack that was formerly done on impl.messageReflectWrapper. After this change the new protoc-gen-go no longer generates any XXX fields or methods. All internal fields and methods are truly hidden from the end-user. name old time/op new time/op delta Wire/Unmarshal/google_message1_proto2-4 1.50µs ±10% 1.50µs ± 2% ~ (p=0.483 n=10+9) Wire/Unmarshal/google_message1_proto3-4 1.06µs ± 6% 1.06µs ± 4% ~ (p=0.814 n=9+9) Wire/Unmarshal/google_message2-4 734µs ±22% 689µs ±13% ~ (p=0.133 n=10+9) Wire/Marshal/google_message1_proto2-4 790ns ±46% 652ns ± 8% ~ (p=0.590 n=10+9) Wire/Marshal/google_message1_proto3-4 872ns ± 4% 857ns ± 3% ~ (p=0.168 n=9+9) Wire/Marshal/google_message2-4 232µs ±16% 221µs ± 3% -4.75% (p=0.014 n=9+9) Wire/Size/google_message1_proto2-4 164ns ± 2% 167ns ± 4% +1.87% (p=0.046 n=9+10) Wire/Size/google_message1_proto3-4 240ns ± 9% 229ns ± 1% -4.81% (p=0.000 n=9+8) Wire/Size/google_message2-4 58.9µs ± 9% 59.6µs ± 2% +1.23% (p=0.040 n=9+9) name old alloc/op new alloc/op delta Wire/Unmarshal/google_message1_proto2-4 912B ± 0% 912B ± 0% ~ (all equal) Wire/Unmarshal/google_message1_proto3-4 688B ± 0% 688B ± 0% ~ (all equal) Wire/Unmarshal/google_message2-4 470kB ± 0% 470kB ± 0% ~ (p=0.215 n=10+10) Wire/Marshal/google_message1_proto2-4 240B ± 0% 240B ± 0% ~ (all equal) Wire/Marshal/google_message1_proto3-4 224B ± 0% 224B ± 0% ~ (all equal) Wire/Marshal/google_message2-4 90.1kB ± 0% 90.1kB ± 0% ~ (all equal) Wire/Size/google_message1_proto2-4 0.00B 0.00B ~ (all equal) Wire/Size/google_message1_proto3-4 0.00B 0.00B ~ (all equal) Wire/Size/google_message2-4 0.00B 0.00B ~ (all equal) name old allocs/op new allocs/op delta Wire/Unmarshal/google_message1_proto2-4 24.0 ± 0% 24.0 ± 0% ~ (all equal) Wire/Unmarshal/google_message1_proto3-4 6.00 ± 0% 6.00 ± 0% ~ (all equal) Wire/Unmarshal/google_message2-4 8.49k ± 0% 8.49k ± 0% ~ (all equal) Wire/Marshal/google_message1_proto2-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) Wire/Marshal/google_message1_proto3-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) Wire/Marshal/google_message2-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) Wire/Size/google_message1_proto2-4 0.00 0.00 ~ (all equal) Wire/Size/google_message1_proto3-4 0.00 0.00 ~ (all equal) Wire/Size/google_message2-4 0.00 0.00 ~ (all equal) Change-Id: Ibf3263ad0f293326695c22020a92a6b938ef4f65 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185697 Reviewed-by: Damien Neil <dneil@google.com>
2019-07-10 23:14:31 -07:00
func (o UnmarshalOptions) unmarshalMessage(b []byte, m protoreflect.Message) error {
_, err := o.unmarshal(b, m)
return err
}
runtime/protoiface: move and rename XXX_Methods This CL moves and renames the protoreflect.ProtoMessage.XXX_Methods to protoreflect.Message.ProtoMethods. Since one needs to obtain a protoreflect.Message now to get at the fast-path methods, we modify the method signatures to take in a protoreflect.Message instead of protoreflect.ProtoMessage. Doing so also avoids the wrapper hack that was formerly done on impl.messageReflectWrapper. After this change the new protoc-gen-go no longer generates any XXX fields or methods. All internal fields and methods are truly hidden from the end-user. name old time/op new time/op delta Wire/Unmarshal/google_message1_proto2-4 1.50µs ±10% 1.50µs ± 2% ~ (p=0.483 n=10+9) Wire/Unmarshal/google_message1_proto3-4 1.06µs ± 6% 1.06µs ± 4% ~ (p=0.814 n=9+9) Wire/Unmarshal/google_message2-4 734µs ±22% 689µs ±13% ~ (p=0.133 n=10+9) Wire/Marshal/google_message1_proto2-4 790ns ±46% 652ns ± 8% ~ (p=0.590 n=10+9) Wire/Marshal/google_message1_proto3-4 872ns ± 4% 857ns ± 3% ~ (p=0.168 n=9+9) Wire/Marshal/google_message2-4 232µs ±16% 221µs ± 3% -4.75% (p=0.014 n=9+9) Wire/Size/google_message1_proto2-4 164ns ± 2% 167ns ± 4% +1.87% (p=0.046 n=9+10) Wire/Size/google_message1_proto3-4 240ns ± 9% 229ns ± 1% -4.81% (p=0.000 n=9+8) Wire/Size/google_message2-4 58.9µs ± 9% 59.6µs ± 2% +1.23% (p=0.040 n=9+9) name old alloc/op new alloc/op delta Wire/Unmarshal/google_message1_proto2-4 912B ± 0% 912B ± 0% ~ (all equal) Wire/Unmarshal/google_message1_proto3-4 688B ± 0% 688B ± 0% ~ (all equal) Wire/Unmarshal/google_message2-4 470kB ± 0% 470kB ± 0% ~ (p=0.215 n=10+10) Wire/Marshal/google_message1_proto2-4 240B ± 0% 240B ± 0% ~ (all equal) Wire/Marshal/google_message1_proto3-4 224B ± 0% 224B ± 0% ~ (all equal) Wire/Marshal/google_message2-4 90.1kB ± 0% 90.1kB ± 0% ~ (all equal) Wire/Size/google_message1_proto2-4 0.00B 0.00B ~ (all equal) Wire/Size/google_message1_proto3-4 0.00B 0.00B ~ (all equal) Wire/Size/google_message2-4 0.00B 0.00B ~ (all equal) name old allocs/op new allocs/op delta Wire/Unmarshal/google_message1_proto2-4 24.0 ± 0% 24.0 ± 0% ~ (all equal) Wire/Unmarshal/google_message1_proto3-4 6.00 ± 0% 6.00 ± 0% ~ (all equal) Wire/Unmarshal/google_message2-4 8.49k ± 0% 8.49k ± 0% ~ (all equal) Wire/Marshal/google_message1_proto2-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) Wire/Marshal/google_message1_proto3-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) Wire/Marshal/google_message2-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) Wire/Size/google_message1_proto2-4 0.00 0.00 ~ (all equal) Wire/Size/google_message1_proto3-4 0.00 0.00 ~ (all equal) Wire/Size/google_message2-4 0.00 0.00 ~ (all equal) Change-Id: Ibf3263ad0f293326695c22020a92a6b938ef4f65 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/185697 Reviewed-by: Damien Neil <dneil@google.com>
2019-07-10 23:14:31 -07:00
func (o UnmarshalOptions) unmarshalMessageSlow(b []byte, m protoreflect.Message) error {
md := m.Descriptor()
if messageset.IsMessageSet(md) {
return o.unmarshalMessageSet(b, m)
}
fields := md.Fields()
for len(b) > 0 {
// Parse the tag (field number and wire type).
num, wtyp, tagLen := protowire.ConsumeTag(b)
if tagLen < 0 {
return errDecode
}
if num > protowire.MaxValidNumber {
return errDecode
}
// Find the field descriptor for this field number.
fd := fields.ByNumber(num)
if fd == nil && md.ExtensionRanges().Has(num) {
extType, err := o.Resolver.FindExtensionByNumber(md.FullName(), num)
if err != nil && err != protoregistry.NotFound {
return errors.New("%v: unable to resolve extension %v: %v", md.FullName(), num, err)
}
if extType != nil {
fd = extType.TypeDescriptor()
}
}
var err error
if fd == nil {
err = errUnknown
} else if flags.ProtoLegacy {
if fd.IsWeak() && fd.Message().IsPlaceholder() {
err = errUnknown // weak referent is not linked in
}
}
// Parse the field value.
var valLen int
switch {
case err != nil:
case fd.IsList():
valLen, err = o.unmarshalList(b[tagLen:], wtyp, m.Mutable(fd).List(), fd)
case fd.IsMap():
valLen, err = o.unmarshalMap(b[tagLen:], wtyp, m.Mutable(fd).Map(), fd)
default:
valLen, err = o.unmarshalSingular(b[tagLen:], wtyp, m, fd)
}
if err != nil {
if err != errUnknown {
return err
}
valLen = protowire.ConsumeFieldValue(num, wtyp, b[tagLen:])
if valLen < 0 {
return errDecode
}
if !o.DiscardUnknown {
m.SetUnknown(append(m.GetUnknown(), b[:tagLen+valLen]...))
}
}
b = b[tagLen+valLen:]
}
return nil
}
func (o UnmarshalOptions) unmarshalSingular(b []byte, wtyp protowire.Type, m protoreflect.Message, fd protoreflect.FieldDescriptor) (n int, err error) {
v, n, err := o.unmarshalScalar(b, wtyp, fd)
if err != nil {
return 0, err
}
switch fd.Kind() {
case protoreflect.GroupKind, protoreflect.MessageKind:
m2 := m.Mutable(fd).Message()
if err := o.unmarshalMessage(v.Bytes(), m2); err != nil {
return n, err
}
default:
// Non-message scalars replace the previous value.
m.Set(fd, v)
}
return n, nil
}
func (o UnmarshalOptions) unmarshalMap(b []byte, wtyp protowire.Type, mapv protoreflect.Map, fd protoreflect.FieldDescriptor) (n int, err error) {
if wtyp != protowire.BytesType {
return 0, errUnknown
}
b, n = protowire.ConsumeBytes(b)
if n < 0 {
return 0, errDecode
}
var (
keyField = fd.MapKey()
valField = fd.MapValue()
key protoreflect.Value
val protoreflect.Value
haveKey bool
haveVal bool
)
switch valField.Kind() {
case protoreflect.GroupKind, protoreflect.MessageKind:
val = mapv.NewValue()
}
// Map entries are represented as a two-element message with fields
// containing the key and value.
for len(b) > 0 {
num, wtyp, n := protowire.ConsumeTag(b)
if n < 0 {
return 0, errDecode
}
if num > protowire.MaxValidNumber {
return 0, errDecode
}
b = b[n:]
err = errUnknown
switch num {
case genid.MapEntry_Key_field_number:
key, n, err = o.unmarshalScalar(b, wtyp, keyField)
if err != nil {
break
}
haveKey = true
case genid.MapEntry_Value_field_number:
var v protoreflect.Value
v, n, err = o.unmarshalScalar(b, wtyp, valField)
if err != nil {
break
}
switch valField.Kind() {
case protoreflect.GroupKind, protoreflect.MessageKind:
if err := o.unmarshalMessage(v.Bytes(), val.Message()); err != nil {
return 0, err
}
default:
val = v
}
haveVal = true
}
if err == errUnknown {
n = protowire.ConsumeFieldValue(num, wtyp, b)
if n < 0 {
return 0, errDecode
}
} 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, nil
}
// 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)")
var errDecode = errors.New("cannot parse invalid wire-format data")