all: abstract fast-path marshal and unmarshal inputs and outputs

We may want to make changes to the inputs and outputs of the fast-path
functions in the future. For example, we likely want to add the ability
for the fast-path unmarshal to report back whether the unmarshaled
message is known to be initialized.

Change the signatures of these functions to take in and return struct
types which can be extended with whatever fields we want in the future.

Change-Id: Idead360785df730283a4630ea405265b72482e62
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/215719
Reviewed-by: Joe Tsai <joetsai@google.com>
This commit is contained in:
Damien Neil 2020-01-21 13:29:51 -08:00
parent f12fb45fd6
commit 61781dd92f
9 changed files with 90 additions and 33 deletions

View File

@ -134,9 +134,9 @@ func (mi *MessageInfo) makeCoderMethods(t reflect.Type, si structInfo) {
}
mi.needsInitCheck = needsInitCheck(mi.Desc)
if mi.methods.MarshalAppend == nil && mi.methods.Size == nil {
if mi.methods.Marshal == nil && mi.methods.Size == nil {
mi.methods.Flags |= piface.SupportMarshalDeterministic
mi.methods.MarshalAppend = mi.marshalAppend
mi.methods.Marshal = mi.marshal
mi.methods.Size = mi.size
}
if mi.methods.Unmarshal == nil {

View File

@ -58,15 +58,15 @@ func (o unmarshalOptions) DiscardUnknown() bool { return o.flags
func (o unmarshalOptions) Resolver() preg.ExtensionTypeResolver { return o.resolver }
// unmarshal is protoreflect.Methods.Unmarshal.
func (mi *MessageInfo) unmarshal(b []byte, m pref.Message, opts piface.UnmarshalOptions) error {
func (mi *MessageInfo) unmarshal(m pref.Message, in piface.UnmarshalInput) (piface.UnmarshalOutput, error) {
var p pointer
if ms, ok := m.(*messageState); ok {
p = ms.pointer()
} else {
p = m.(*messageReflectWrapper).pointer()
}
_, err := mi.unmarshalPointer(b, p, 0, newUnmarshalOptions(opts))
return err
_, err := mi.unmarshalPointer(in.Buf, p, 0, newUnmarshalOptions(in.Options))
return piface.UnmarshalOutput{}, err
}
// errUnknown is returned during unmarshaling to indicate a parse error that

View File

@ -101,15 +101,16 @@ func (mi *MessageInfo) sizePointerSlow(p pointer, opts marshalOptions) (size int
return size
}
// marshalAppend is protoreflect.Methods.MarshalAppend.
func (mi *MessageInfo) marshalAppend(b []byte, m pref.Message, opts piface.MarshalOptions) ([]byte, error) {
// marshal is protoreflect.Methods.Marshal.
func (mi *MessageInfo) marshal(m pref.Message, in piface.MarshalInput) (piface.MarshalOutput, error) {
var p pointer
if ms, ok := m.(*messageState); ok {
p = ms.pointer()
} else {
p = m.(*messageReflectWrapper).pointer()
}
return mi.marshalAppendPointer(b, p, newMarshalOptions(opts))
b, err := mi.marshalAppendPointer(in.Buf, p, newMarshalOptions(in.Options))
return piface.MarshalOutput{Buf: b}, err
}
func (mi *MessageInfo) marshalAppendPointer(b []byte, p pointer, opts marshalOptions) ([]byte, error) {

View File

@ -50,7 +50,7 @@ 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.Marshal = legacyMarshal
// We have no way to tell whether the type's Marshal method
// supports deterministic serialization or not, but this
@ -363,8 +363,8 @@ type legacyUnmarshaler interface {
}
var legacyProtoMethods = &piface.Methods{
MarshalAppend: legacyMarshalAppend,
Unmarshal: legacyUnmarshal,
Marshal: legacyMarshal,
Unmarshal: legacyUnmarshal,
// We have no way to tell whether the type's Marshal method
// supports deterministic serialization or not, but this
@ -373,26 +373,28 @@ var legacyProtoMethods = &piface.Methods{
Flags: piface.SupportMarshalDeterministic,
}
func legacyMarshalAppend(b []byte, m protoreflect.Message, opts piface.MarshalOptions) ([]byte, error) {
func legacyMarshal(m protoreflect.Message, in piface.MarshalInput) (piface.MarshalOutput, error) {
v := m.(unwrapper).protoUnwrap()
marshaler, ok := v.(legacyMarshaler)
if !ok {
return nil, errors.New("%T does not implement Marshal", v)
return piface.MarshalOutput{}, errors.New("%T does not implement Marshal", v)
}
out, err := marshaler.Marshal()
if b != nil {
out = append(b, out...)
if in.Buf != nil {
out = append(in.Buf, out...)
}
return out, err
return piface.MarshalOutput{
Buf: out,
}, err
}
func legacyUnmarshal(b []byte, m protoreflect.Message, opts piface.UnmarshalOptions) error {
func legacyUnmarshal(m protoreflect.Message, in piface.UnmarshalInput) (piface.UnmarshalOutput, error) {
v := m.(unwrapper).protoUnwrap()
unmarshaler, ok := v.(legacyUnmarshaler)
if !ok {
return errors.New("%T does not implement Marshal", v)
return piface.UnmarshalOutput{}, errors.New("%T does not implement Marshal", v)
}
return unmarshaler.Unmarshal(b)
return piface.UnmarshalOutput{}, unmarshaler.Unmarshal(in.Buf)
}
// aberrantMessageType implements MessageType for all types other than pointer-to-struct.

View File

@ -72,7 +72,11 @@ func (o UnmarshalOptions) Unmarshal(b []byte, m Message) error {
func (o UnmarshalOptions) unmarshalMessage(b []byte, m protoreflect.Message) error {
if methods := protoMethods(m); methods != nil && methods.Unmarshal != nil &&
!(o.DiscardUnknown && methods.Flags&protoiface.SupportUnmarshalDiscardUnknown == 0) {
return methods.Unmarshal(b, m, protoiface.UnmarshalOptions(o))
_, err := methods.Unmarshal(m, protoiface.UnmarshalInput{
Buf: b,
Options: protoiface.UnmarshalOptions(o),
})
return err
}
return o.unmarshalMessageSlow(b, m)
}

View File

@ -98,7 +98,7 @@ 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 &&
if methods := protoMethods(m); methods != nil && methods.Marshal != nil &&
!(o.Deterministic && methods.Flags&protoiface.SupportMarshalDeterministic == 0) {
if methods.Size != nil {
sz := methods.Size(m, protoiface.MarshalOptions(o))
@ -109,7 +109,11 @@ func (o MarshalOptions) marshalMessage(b []byte, m protoreflect.Message) ([]byte
}
o.UseCachedSize = true
}
return methods.MarshalAppend(b, m, protoiface.MarshalOptions(o))
out, err := methods.Marshal(m, protoiface.MarshalInput{
Buf: b,
Options: protoiface.MarshalOptions(o),
})
return out.Buf, err
}
return o.marshalMessageSlow(b, m)
}

View File

@ -26,11 +26,11 @@ func sizeMessage(m protoreflect.Message) (size int) {
if methods != nil && methods.Size != nil {
return methods.Size(m, protoiface.MarshalOptions{})
}
if methods != nil && methods.MarshalAppend != nil {
if methods != nil && methods.Marshal != 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)
out, _ := methods.Marshal(m, protoiface.MarshalInput{})
return len(out.Buf)
}
return sizeMessageSlow(m)
}

View File

@ -19,18 +19,34 @@ type (
pragma.NoUnkeyedLiterals
Flags supportFlags
Size func(m Message, opts marshalOptions) int
MarshalAppend func(b []byte, m Message, opts marshalOptions) ([]byte, error)
Unmarshal func(b []byte, m Message, opts unmarshalOptions) error
Marshal func(m Message, in marshalInput) (marshalOutput, error)
Unmarshal func(m Message, in unmarshalInput) (unmarshalOutput, error)
IsInitialized func(m Message) error
}
supportFlags = uint64
supportFlags = uint64
marshalInput = struct {
pragma.NoUnkeyedLiterals
Buf []byte
Options marshalOptions
}
marshalOutput = struct {
pragma.NoUnkeyedLiterals
Buf []byte
}
marshalOptions = struct {
pragma.NoUnkeyedLiterals
AllowPartial bool
Deterministic bool
UseCachedSize bool
}
unmarshalInput = struct {
pragma.NoUnkeyedLiterals
Buf []byte
Options unmarshalOptions
}
unmarshalOutput = struct {
pragma.NoUnkeyedLiterals
}
unmarshalOptions = struct {
pragma.NoUnkeyedLiterals
Merge bool

View File

@ -25,14 +25,14 @@ type Methods = struct {
// MarshalAppend must be provided if a custom Size is provided.
Size func(m protoreflect.Message, opts MarshalOptions) int
// MarshalAppend appends the wire-format encoding of m to b, returning the result.
// Marshal writes the wire-format encoding of m to the provided buffer.
// 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)
Marshal func(m protoreflect.Message, in MarshalInput) (MarshalOutput, error)
// Unmarshal parses the wire-format message in b and merges the result in m.
// Unmarshal parses the wire-format encoding of a message and merges the result to m.
// It must not reset m or perform required field checks.
Unmarshal func(b []byte, m protoreflect.Message, opts UnmarshalOptions) error
Unmarshal func(m protoreflect.Message, in UnmarshalInput) (UnmarshalOutput, error)
// IsInitialized returns an error if any required fields in m are not set.
IsInitialized func(m protoreflect.Message) error
@ -48,6 +48,21 @@ const (
SupportUnmarshalDiscardUnknown
)
// MarshalInput is input to the marshaler.
type MarshalInput = struct {
pragma.NoUnkeyedLiterals
Buf []byte // output is appended to this buffer
Options MarshalOptions
}
// MarshalOutput is output from the marshaler.
type MarshalOutput = struct {
pragma.NoUnkeyedLiterals
Buf []byte // contains marshaled message
}
// MarshalOptions configure the marshaler.
//
// This type is identical to the one in package proto.
@ -59,6 +74,21 @@ type MarshalOptions = struct {
UseCachedSize bool
}
// UnmarshalInput is input to the unmarshaler.
type UnmarshalInput = struct {
pragma.NoUnkeyedLiterals
Buf []byte // input buffer
Options UnmarshalOptions
}
// UnmarshalOutput is output from the unmarshaler.
type UnmarshalOutput = struct {
pragma.NoUnkeyedLiterals
// Contents available for future expansion.
}
// UnmarshalOptions configures the unmarshaler.
//
// This type is identical to the one in package proto.