proto, runtime/protoiface: add support for fast-path marshaling

Allow message implementations to provide optimized versions of standard
operations. Generated messages now include a ProtoReflectMethods method,
returning a protoiface.Methods struct containing pointers to assorted
optional functions.

The Methods struct also includes a Flags field indicating support for
optional features such as deterministic marshaling.

Implementation of the fast paths (and tests) will come in later CLs.

Change-Id: Idd1beed0ecf43ec5e5e7b8da2ee1e08d3ce32213
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/170340
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/proto/encode.go b/proto/encode.go
index 68a3448..973e53a 100644
--- a/proto/encode.go
+++ b/proto/encode.go
@@ -12,6 +12,7 @@
 	"github.com/golang/protobuf/v2/internal/mapsort"
 	"github.com/golang/protobuf/v2/internal/pragma"
 	"github.com/golang/protobuf/v2/reflect/protoreflect"
+	"github.com/golang/protobuf/v2/runtime/protoiface"
 )
 
 // MarshalOptions configures the marshaler.
@@ -40,9 +41,15 @@
 	// detail and subject to change.
 	Deterministic bool
 
+	// Reflection forces use of the reflection-based encoder, even for
+	// messages which implement fast-path serialization.
+	Reflection bool
+
 	pragma.NoUnkeyedLiterals
 }
 
+var _ = protoiface.MarshalOptions(MarshalOptions{})
+
 // Marshal returns the wire-format encoding of m.
 func Marshal(m Message) ([]byte, error) {
 	return MarshalOptions{}.MarshalAppend(nil, m)
@@ -50,15 +57,39 @@
 
 // Marshal returns the wire-format encoding of m.
 func (o MarshalOptions) Marshal(m Message) ([]byte, error) {
-	return o.marshalMessage(nil, m.ProtoReflect())
+	return o.MarshalAppend(nil, m)
 }
 
 // MarshalAppend appends the wire-format encoding of m to b,
 // returning the result.
 func (o MarshalOptions) MarshalAppend(b []byte, m Message) ([]byte, error) {
+	if b, err := o.marshalMessageFast(b, m); err != errInternalNoFast {
+		return b, err
+	}
 	return o.marshalMessage(b, m.ProtoReflect())
 }
 
+func (o MarshalOptions) marshalMessageFast(b []byte, m Message) ([]byte, error) {
+	if o.Reflection {
+		return nil, errInternalNoFast
+	}
+	methods := protoMethods(m)
+	if methods == nil ||
+		methods.MarshalAppend == nil ||
+		(o.Deterministic && methods.Flags&protoiface.MethodFlagDeterministicMarshal == 0) {
+		return nil, errInternalNoFast
+	}
+	if methods.Size != nil {
+		sz := methods.Size(m)
+		if cap(b) < len(b)+sz {
+			x := make([]byte, len(b), len(b)+sz)
+			copy(x, b)
+			b = x
+		}
+	}
+	return methods.MarshalAppend(b, m, protoiface.MarshalOptions(o))
+}
+
 func (o MarshalOptions) marshalMessage(b []byte, m protoreflect.Message) ([]byte, error) {
 	// There are many choices for what order we visit fields in. The default one here
 	// is chosen for reasonable efficiency and simplicity given the protoreflect API.