internal/impl: faster oneof marshaling

Change size, marshal, and isinit operations on oneofs to look up the
currently-set oneof type in a map rather than testing for each possible
oneof field in turn.

Significantly improves oneof encoding speed for oneofs with a
substantial number of fields:

  go test ./proto -bench=./oneof.*string.*test.TestAll -benchmem -count=8 -cpu=1

  name                                        old time/op    new time/op    delta
  Encode/oneof_(string)_(*test.TestAllTypes)     911ns ± 1%     397ns ± 3%  -56.45%  (p=0.000 n=8+7)
  Decode/oneof_(string)_(*test.TestAllTypes)     899ns ± 1%     922ns ± 1%   +2.49%  (p=0.001 n=7+7)

Fixes golang/protobuf#950

Change-Id: I9393a87975ce09011d885a8af4a63a639ea8452f
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/210281
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/impl/codec_message.go b/internal/impl/codec_message.go
index d7584d4..6cfb5c7 100644
--- a/internal/impl/codec_message.go
+++ b/internal/impl/codec_message.go
@@ -68,7 +68,6 @@
 		switch {
 		case fd.ContainingOneof() != nil:
 			fieldOffset = offsetOf(fs, mi.Exporter)
-			funcs = makeOneofFieldCoder(fd, si)
 		case fd.IsWeak():
 			fieldOffset = si.weakOffset
 			funcs = makeWeakMessageFieldCoder(fd)
@@ -91,6 +90,9 @@
 		mi.orderedCoderFields = append(mi.orderedCoderFields, cf)
 		mi.coderFields[cf.num] = cf
 	}
+	for i, oneofs := 0, mi.Desc.Oneofs(); i < oneofs.Len(); i++ {
+		mi.initOneofFieldCoders(oneofs.Get(i), si)
+	}
 	if messageset.IsMessageSet(mi.Desc) {
 		if !mi.extensionOffset.IsValid() {
 			panic(fmt.Sprintf("%v: MessageSet with no extensions field", mi.Desc.FullName()))