internal/impl: refactor fast-path

Move data used by the fast-path implementations into a substructure of
MessageInfo and initialize it separately.

Change-Id: Ib855ee8ea5cb0379528b52ba0e191319aa5e2dff
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/184077
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/impl/codec_message.go b/internal/impl/codec_message.go
new file mode 100644
index 0000000..204049d
--- /dev/null
+++ b/internal/impl/codec_message.go
@@ -0,0 +1,101 @@
+// Copyright 2019 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 impl
+
+import (
+	"reflect"
+	"sort"
+
+	"google.golang.org/protobuf/internal/encoding/wire"
+	pref "google.golang.org/protobuf/reflect/protoreflect"
+	piface "google.golang.org/protobuf/runtime/protoiface"
+)
+
+// coderMessageInfo contains per-message information used by the fast-path functions.
+// This is a different type from MessageInfo to keep MessageInfo as general-purpose as
+// possible.
+type coderMessageInfo struct {
+	orderedCoderFields []*coderFieldInfo
+	sizecacheOffset    offset
+	extensionOffset    offset
+	unknownOffset      offset
+	needsInitCheck     bool
+}
+
+type coderFieldInfo struct {
+	funcs      pointerCoderFuncs // fast-path per-field functions
+	num        pref.FieldNumber  // field number
+	offset     offset            // struct field offset
+	wiretag    uint64            // field tag (number + wire type)
+	tagsize    int               // size of the varint-encoded tag
+	isPointer  bool              // true if IsNil may be called on the struct field
+	isRequired bool              // true if field is required
+}
+
+func (mi *MessageInfo) makeMethods(t reflect.Type, si structInfo) {
+	mi.sizecacheOffset = invalidOffset
+	if fx, _ := t.FieldByName("XXX_sizecache"); fx.Type == sizecacheType {
+		mi.sizecacheOffset = offsetOf(fx)
+	}
+	mi.unknownOffset = invalidOffset
+	if fx, _ := t.FieldByName("XXX_unrecognized"); fx.Type == unknownFieldsType {
+		mi.unknownOffset = offsetOf(fx)
+	}
+	mi.extensionOffset = invalidOffset
+	if fx, _ := t.FieldByName("XXX_InternalExtensions"); fx.Type == extensionFieldsType {
+		mi.extensionOffset = offsetOf(fx)
+	} else if fx, _ = t.FieldByName("XXX_extensions"); fx.Type == extensionFieldsType {
+		mi.extensionOffset = offsetOf(fx)
+	}
+
+	for i := 0; i < mi.PBType.Descriptor().Fields().Len(); i++ {
+		fd := mi.PBType.Descriptor().Fields().Get(i)
+		if fd.ContainingOneof() != nil {
+			continue
+		}
+
+		fs := si.fieldsByNumber[fd.Number()]
+		ft := fs.Type
+		var wiretag uint64
+		if !fd.IsPacked() {
+			wiretag = wire.EncodeTag(fd.Number(), wireTypes[fd.Kind()])
+		} else {
+			wiretag = wire.EncodeTag(fd.Number(), wire.BytesType)
+		}
+		mi.orderedCoderFields = append(mi.orderedCoderFields, &coderFieldInfo{
+			num:     fd.Number(),
+			offset:  offsetOf(fs),
+			wiretag: wiretag,
+			tagsize: wire.SizeVarint(wiretag),
+			funcs:   fieldCoder(fd, ft),
+			isPointer: (fd.Cardinality() == pref.Repeated ||
+				fd.Kind() == pref.MessageKind ||
+				fd.Kind() == pref.GroupKind ||
+				fd.Syntax() != pref.Proto3),
+			isRequired: fd.Cardinality() == pref.Required,
+		})
+	}
+	for i := 0; i < mi.PBType.Descriptor().Oneofs().Len(); i++ {
+		od := mi.PBType.Descriptor().Oneofs().Get(i)
+		fs := si.oneofsByName[od.Name()]
+		mi.orderedCoderFields = append(mi.orderedCoderFields, &coderFieldInfo{
+			num:       od.Fields().Get(0).Number(),
+			offset:    offsetOf(fs),
+			funcs:     makeOneofFieldCoder(fs, od, si.fieldsByNumber, si.oneofWrappersByNumber),
+			isPointer: true,
+		})
+	}
+	sort.Slice(mi.orderedCoderFields, func(i, j int) bool {
+		return mi.orderedCoderFields[i].num < mi.orderedCoderFields[j].num
+	})
+
+	mi.needsInitCheck = needsInitCheck(mi.PBType)
+	mi.methods = piface.Methods{
+		Flags:         piface.MethodFlagDeterministicMarshal,
+		MarshalAppend: mi.marshalAppend,
+		Size:          mi.size,
+		IsInitialized: mi.isInitialized,
+	}
+}