mirror of
https://github.com/protocolbuffers/protobuf-go.git
synced 2025-02-19 03:39:48 +00:00
proto, internal/impl: don't create fast path Size for legacy Marshalers
Implementations of the legacy Marshaler type have no way to efficiently compute the size of the message. Rather than generating an inefficient fast-path Size method which marshals the message and examines the length of the result, don't generate a fast-path at all. Drop the requirement that a fast-path MarshalAppend requires a corresponding Size. Avoids O(N^2) behavior when marshaling a legacy Marshaler that recursively calls proto.Marshal. Change-Id: I4793cf32275d08f29c8e1a1a44a193d9a5724058 Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/213443 Reviewed-by: Joe Tsai <joetsai@google.com>
This commit is contained in:
parent
b7695fab0d
commit
b0d217f664
@ -51,7 +51,6 @@ func legacyLoadMessageInfo(t reflect.Type, name pref.FullName) *MessageInfo {
|
||||
v := reflect.Zero(t).Interface()
|
||||
if _, ok := v.(legacyMarshaler); ok {
|
||||
mi.methods.MarshalAppend = legacyMarshalAppend
|
||||
mi.methods.Size = legacySize
|
||||
|
||||
// We have no way to tell whether the type's Marshal method
|
||||
// supports deterministic serialization or not, but this
|
||||
@ -364,7 +363,6 @@ type legacyUnmarshaler interface {
|
||||
}
|
||||
|
||||
var legacyProtoMethods = &piface.Methods{
|
||||
Size: legacySize,
|
||||
MarshalAppend: legacyMarshalAppend,
|
||||
Unmarshal: legacyUnmarshal,
|
||||
|
||||
@ -375,11 +373,6 @@ var legacyProtoMethods = &piface.Methods{
|
||||
Flags: piface.SupportMarshalDeterministic,
|
||||
}
|
||||
|
||||
func legacySize(m protoreflect.Message, opts piface.MarshalOptions) int {
|
||||
b, _ := legacyMarshalAppend(nil, m, opts)
|
||||
return len(b)
|
||||
}
|
||||
|
||||
func legacyMarshalAppend(b []byte, m protoreflect.Message, opts piface.MarshalOptions) ([]byte, error) {
|
||||
v := m.(unwrapper).protoUnwrap()
|
||||
marshaler, ok := v.(legacyMarshaler)
|
||||
|
@ -100,13 +100,15 @@ func (o MarshalOptions) MarshalAppend(b []byte, m Message) ([]byte, error) {
|
||||
func (o MarshalOptions) marshalMessage(b []byte, m protoreflect.Message) ([]byte, error) {
|
||||
if methods := protoMethods(m); methods != nil && methods.MarshalAppend != nil &&
|
||||
!(o.Deterministic && methods.Flags&protoiface.SupportMarshalDeterministic == 0) {
|
||||
sz := methods.Size(m, protoiface.MarshalOptions(o))
|
||||
if cap(b) < len(b)+sz {
|
||||
x := make([]byte, len(b), growcap(cap(b), len(b)+sz))
|
||||
copy(x, b)
|
||||
b = x
|
||||
if methods.Size != nil {
|
||||
sz := methods.Size(m, protoiface.MarshalOptions(o))
|
||||
if cap(b) < len(b)+sz {
|
||||
x := make([]byte, len(b), growcap(cap(b), len(b)+sz))
|
||||
copy(x, b)
|
||||
b = x
|
||||
}
|
||||
o.UseCachedSize = true
|
||||
}
|
||||
o.UseCachedSize = true
|
||||
return methods.MarshalAppend(b, m, protoiface.MarshalOptions(o))
|
||||
}
|
||||
return o.marshalMessageSlow(b, m)
|
||||
|
@ -22,9 +22,16 @@ func (o MarshalOptions) Size(m Message) int {
|
||||
}
|
||||
|
||||
func sizeMessage(m protoreflect.Message) (size int) {
|
||||
if methods := protoMethods(m); methods != nil && methods.Size != nil {
|
||||
methods := protoMethods(m)
|
||||
if methods != nil && methods.Size != nil {
|
||||
return methods.Size(m, protoiface.MarshalOptions{})
|
||||
}
|
||||
if methods != nil && methods.MarshalAppend != nil {
|
||||
// This is not efficient, but we don't have any choice.
|
||||
// This case is mainly used for legacy types with a Marshal method.
|
||||
b, _ := methods.MarshalAppend(nil, m, protoiface.MarshalOptions{})
|
||||
return len(b)
|
||||
}
|
||||
return sizeMessageSlow(m)
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ type Methods struct {
|
||||
Size func(m protoreflect.Message, opts MarshalOptions) int
|
||||
|
||||
// MarshalAppend appends the wire-format encoding of m to b, returning the result.
|
||||
// Size must be provided if a custom MarshalAppend is provided.
|
||||
// Size should be provided if a custom MarshalAppend is provided.
|
||||
// It must not perform required field checks.
|
||||
MarshalAppend func(b []byte, m protoreflect.Message, opts MarshalOptions) ([]byte, error)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user