internal/impl: pass *coderFieldInfo into fast-path functions

Refactor the fast-path size, marshal, unmarshal, and isinit functions to
take the *coderFieldInfo for the field as input.

This replaces a number of closures capturing field-specific information
with functions taking that information as an explicit parameter.

Change-Id: I8cb39701265edb7b673f6f04a0152d5f4dbb4d5d
Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/218937
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/cmd/generate-types/impl.go b/internal/cmd/generate-types/impl.go
index 737f84b..0a89144 100644
--- a/internal/cmd/generate-types/impl.go
+++ b/internal/cmd/generate-types/impl.go
@@ -92,23 +92,23 @@
 
 {{- if .FromGoType }}
 // size{{.Name}} returns the size of wire encoding a {{.GoType}} pointer as a {{.Name}}.
-func size{{.Name}}(p pointer, tagsize int, _ marshalOptions) (size int) {
+func size{{.Name}}(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	{{if not .WireType.ConstSize -}}
 	v := *p.{{.GoType.PointerMethod}}()
 	{{- end}}
-	return tagsize + {{template "Size" .}}
+	return f.tagsize + {{template "Size" .}}
 }
 
 // append{{.Name}} wire encodes a {{.GoType}} pointer as a {{.Name}}.
-func append{{.Name}}(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func append{{.Name}}(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.{{.GoType.PointerMethod}}()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	{{template "Append" .}}
 	return b, nil
 }
 
 // consume{{.Name}} wire decodes a {{.GoType}} pointer as a {{.Name}}.
-func consume{{.Name}}(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consume{{.Name}}(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != {{.WireType.Expr}} {
 		return out, errUnknown
 	}
@@ -129,9 +129,9 @@
 
 {{if or (eq .Name "Bytes") (eq .Name "String")}}
 // append{{.Name}}ValidateUTF8 wire encodes a {{.GoType}} pointer as a {{.Name}}.
-func append{{.Name}}ValidateUTF8(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func append{{.Name}}ValidateUTF8(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.{{.GoType.PointerMethod}}()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	{{template "Append" .}}
 	if !utf8.Valid{{if eq .Name "String"}}String{{end}}(v) {
 		return b, errInvalidUTF8{}
@@ -140,7 +140,7 @@
 }
 
 // consume{{.Name}}ValidateUTF8 wire decodes a {{.GoType}} pointer as a {{.Name}}.
-func consume{{.Name}}ValidateUTF8(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consume{{.Name}}ValidateUTF8(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != {{.WireType.Expr}} {
 		return out, errUnknown
 	}
@@ -165,22 +165,22 @@
 
 // size{{.Name}}NoZero returns the size of wire encoding a {{.GoType}} pointer as a {{.Name}}.
 // The zero value is not encoded.
-func size{{.Name}}NoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func size{{.Name}}NoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.{{.GoType.PointerMethod}}()
 	if {{template "IsZero" .}} {
 		return 0
 	}
-	return tagsize + {{template "Size" .}}
+	return f.tagsize + {{template "Size" .}}
 }
 
 // append{{.Name}}NoZero wire encodes a {{.GoType}} pointer as a {{.Name}}.
 // The zero value is not encoded.
-func append{{.Name}}NoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func append{{.Name}}NoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.{{.GoType.PointerMethod}}()
 	if {{template "IsZero" .}} {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	{{template "Append" .}}
 	return b, nil
 }
@@ -188,7 +188,7 @@
 {{if .ToGoTypeNoZero}}
 // consume{{.Name}}NoZero wire decodes a {{.GoType}} pointer as a {{.Name}}.
 // The zero value is not decoded.
-func consume{{.Name}}NoZero(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consume{{.Name}}NoZero(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != {{.WireType.Expr}} {
 		return out, errUnknown
 	}
@@ -211,12 +211,12 @@
 {{if or (eq .Name "Bytes") (eq .Name "String")}}
 // append{{.Name}}NoZeroValidateUTF8 wire encodes a {{.GoType}} pointer as a {{.Name}}.
 // The zero value is not encoded.
-func append{{.Name}}NoZeroValidateUTF8(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func append{{.Name}}NoZeroValidateUTF8(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.{{.GoType.PointerMethod}}()
 	if {{template "IsZero" .}} {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	{{template "Append" .}}
 	if !utf8.Valid{{if eq .Name "String"}}String{{end}}(v) {
 		return b, errInvalidUTF8{}
@@ -226,7 +226,7 @@
 
 {{if .ToGoTypeNoZero}}
 // consume{{.Name}}NoZeroValidateUTF8 wire decodes a {{.GoType}} pointer as a {{.Name}}.
-func consume{{.Name}}NoZeroValidateUTF8(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consume{{.Name}}NoZeroValidateUTF8(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != {{.WireType.Expr}} {
 		return out, errUnknown
 	}
@@ -253,24 +253,24 @@
 {{- if not .NoPointer}}
 // size{{.Name}}Ptr returns the size of wire encoding a *{{.GoType}} pointer as a {{.Name}}.
 // It panics if the pointer is nil.
-func size{{.Name}}Ptr(p pointer, tagsize int, _ marshalOptions) (size int) {
+func size{{.Name}}Ptr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	{{if not .WireType.ConstSize -}}
 	v := **p.{{.GoType.PointerMethod}}Ptr()
 	{{end -}}
-	return tagsize + {{template "Size" .}}
+	return f.tagsize + {{template "Size" .}}
 }
 
 // append{{.Name}}Ptr wire encodes a *{{.GoType}} pointer as a {{.Name}}.
 // It panics if the pointer is nil.
-func append{{.Name}}Ptr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func append{{.Name}}Ptr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.{{.GoType.PointerMethod}}Ptr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	{{template "Append" .}}
 	return b, nil
 }
 
 // consume{{.Name}}Ptr wire decodes a *{{.GoType}} pointer as a {{.Name}}.
-func consume{{.Name}}Ptr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consume{{.Name}}Ptr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != {{.WireType.Expr}} {
 		return out, errUnknown
 	}
@@ -295,30 +295,30 @@
 {{end}}
 
 // size{{.Name}}Slice returns the size of wire encoding a []{{.GoType}} pointer as a repeated {{.Name}}.
-func size{{.Name}}Slice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func size{{.Name}}Slice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.{{.GoType.PointerMethod}}Slice()
 	{{if .WireType.ConstSize -}}
-	size = len(s) * (tagsize + {{template "Size" .}})
+	size = len(s) * (f.tagsize + {{template "Size" .}})
 	{{- else -}}
 	for _, v := range s {
-		size += tagsize + {{template "Size" .}}
+		size += f.tagsize + {{template "Size" .}}
 	}
 	{{- end}}
 	return size
 }
 
 // append{{.Name}}Slice encodes a []{{.GoType}} pointer as a repeated {{.Name}}.
-func append{{.Name}}Slice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func append{{.Name}}Slice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.{{.GoType.PointerMethod}}Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		{{template "Append" .}}
 	}
 	return b, nil
 }
 
 // consume{{.Name}}Slice wire decodes a []{{.GoType}} pointer as a repeated {{.Name}}.
-func consume{{.Name}}Slice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consume{{.Name}}Slice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.{{.GoType.PointerMethod}}Slice()
 	{{- if .WireType.Packable}}
 	if wtyp == wire.BytesType {
@@ -360,10 +360,10 @@
 
 {{if or (eq .Name "Bytes") (eq .Name "String")}}
 // append{{.Name}}SliceValidateUTF8 encodes a []{{.GoType}} pointer as a repeated {{.Name}}.
-func append{{.Name}}SliceValidateUTF8(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func append{{.Name}}SliceValidateUTF8(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.{{.GoType.PointerMethod}}Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		{{template "Append" .}}
 		if !utf8.Valid{{if eq .Name "String"}}String{{end}}(v) {
 			return b, errInvalidUTF8{}
@@ -373,7 +373,7 @@
 }
 
 // consume{{.Name}}SliceValidateUTF8 wire decodes a []{{.GoType}} pointer as a repeated {{.Name}}.
-func consume{{.Name}}SliceValidateUTF8(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consume{{.Name}}SliceValidateUTF8(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.{{.GoType.PointerMethod}}Slice()
 	if wtyp != {{.WireType.Expr}} {
 		return out, errUnknown
@@ -399,7 +399,7 @@
 
 {{if or (eq .WireType "Varint") (eq .WireType "Fixed32") (eq .WireType "Fixed64")}}
 // size{{.Name}}PackedSlice returns the size of wire encoding a []{{.GoType}} pointer as a packed repeated {{.Name}}.
-func size{{.Name}}PackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func size{{.Name}}PackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.{{.GoType.PointerMethod}}Slice()
 	if len(s) == 0 {
 		return 0
@@ -412,16 +412,16 @@
 		n += {{template "Size" .}}
 	}
 	{{- end}}
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // append{{.Name}}PackedSlice encodes a []{{.GoType}} pointer as a packed repeated {{.Name}}.
-func append{{.Name}}PackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func append{{.Name}}PackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.{{.GoType.PointerMethod}}Slice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	{{if .WireType.ConstSize -}}
 	n := len(s) * {{template "Size" .}}
 	{{- else -}}
diff --git a/internal/impl/codec_field.go b/internal/impl/codec_field.go
index 433dacb..dd6a8a1 100644
--- a/internal/impl/codec_field.go
+++ b/internal/impl/codec_field.go
@@ -29,31 +29,28 @@
 //
 // The unmarshal function is set on each field individually as usual.
 func (mi *MessageInfo) initOneofFieldCoders(od pref.OneofDescriptor, si structInfo) {
-	type oneofFieldInfo struct {
-		wiretag uint64 // field tag (number + wire type)
-		tagsize int    // size of the varint-encoded tag
-		funcs   pointerCoderFuncs
-	}
 	fs := si.oneofsByName[od.Name()]
 	ft := fs.Type
-	oneofFields := make(map[reflect.Type]*oneofFieldInfo)
+	oneofFields := make(map[reflect.Type]*coderFieldInfo)
 	needIsInit := false
 	fields := od.Fields()
 	for i, lim := 0, fields.Len(); i < lim; i++ {
 		fd := od.Fields().Get(i)
 		num := fd.Number()
-		cf := mi.coderFields[num]
+		// Make a copy of the original coderFieldInfo for use in unmarshaling.
+		//
+		// oneofFields[oneofType].funcs.marshal is the field-specific marshal function.
+		//
+		// mi.coderFields[num].marshal is set on only the first field in the oneof,
+		// and dispatches to the field-specific marshaler in oneofFields.
+		cf := *mi.coderFields[num]
 		ot := si.oneofWrappersByNumber[num]
-		funcs := fieldCoder(fd, ot.Field(0).Type)
-		oneofFields[ot] = &oneofFieldInfo{
-			wiretag: cf.wiretag,
-			tagsize: cf.tagsize,
-			funcs:   funcs,
-		}
-		if funcs.isInit != nil {
+		cf.mi, cf.funcs = fieldCoder(fd, ot.Field(0).Type)
+		oneofFields[ot] = &cf
+		if cf.funcs.isInit != nil {
 			needIsInit = true
 		}
-		cf.funcs.unmarshal = func(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (unmarshalOutput, error) {
+		mi.coderFields[num].funcs.unmarshal = func(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (unmarshalOutput, error) {
 			var vw reflect.Value         // pointer to wrapper type
 			vi := p.AsValueOf(ft).Elem() // oneof field value of interface kind
 			if !vi.IsNil() && !vi.Elem().IsNil() && vi.Elem().Elem().Type() == ot {
@@ -61,7 +58,7 @@
 			} else {
 				vw = reflect.New(ot)
 			}
-			out, err := funcs.unmarshal(b, pointerOfValue(vw).Apply(zeroOffset), wtyp, opts)
+			out, err := cf.funcs.unmarshal(b, pointerOfValue(vw).Apply(zeroOffset), wtyp, &cf, opts)
 			if err != nil {
 				return out, err
 			}
@@ -69,7 +66,7 @@
 			return out, nil
 		}
 	}
-	getInfo := func(p pointer) (pointer, *oneofFieldInfo) {
+	getInfo := func(p pointer) (pointer, *coderFieldInfo) {
 		v := p.AsValueOf(ft).Elem()
 		if v.IsNil() {
 			return pointer{}, nil
@@ -81,27 +78,27 @@
 		return pointerOfValue(v).Apply(zeroOffset), oneofFields[v.Elem().Type()]
 	}
 	first := mi.coderFields[od.Fields().Get(0).Number()]
-	first.funcs.size = func(p pointer, tagsize int, opts marshalOptions) int {
+	first.funcs.size = func(p pointer, _ *coderFieldInfo, opts marshalOptions) int {
 		p, info := getInfo(p)
 		if info == nil || info.funcs.size == nil {
 			return 0
 		}
-		return info.funcs.size(p, info.tagsize, opts)
+		return info.funcs.size(p, info, opts)
 	}
-	first.funcs.marshal = func(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
+	first.funcs.marshal = func(b []byte, p pointer, _ *coderFieldInfo, opts marshalOptions) ([]byte, error) {
 		p, info := getInfo(p)
 		if info == nil || info.funcs.marshal == nil {
 			return b, nil
 		}
-		return info.funcs.marshal(b, p, info.wiretag, opts)
+		return info.funcs.marshal(b, p, info, opts)
 	}
 	if needIsInit {
-		first.funcs.isInit = func(p pointer) error {
+		first.funcs.isInit = func(p pointer, _ *coderFieldInfo) error {
 			p, info := getInfo(p)
 			if info == nil || info.funcs.isInit == nil {
 				return nil
 			}
-			return info.funcs.isInit(p)
+			return info.funcs.isInit(p, info)
 		}
 	}
 }
@@ -118,7 +115,7 @@
 
 	num := fd.Number()
 	return pointerCoderFuncs{
-		size: func(p pointer, tagsize int, opts marshalOptions) int {
+		size: func(p pointer, f *coderFieldInfo, opts marshalOptions) int {
 			m, ok := p.WeakFields().get(num)
 			if !ok {
 				return 0
@@ -127,9 +124,9 @@
 			if messageType == nil {
 				panic(fmt.Sprintf("weak message %v is not linked in", fd.Message().FullName()))
 			}
-			return sizeMessage(m, tagsize, opts)
+			return sizeMessage(m, f.tagsize, opts)
 		},
-		marshal: func(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
+		marshal: func(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
 			m, ok := p.WeakFields().get(num)
 			if !ok {
 				return b, nil
@@ -138,9 +135,9 @@
 			if messageType == nil {
 				panic(fmt.Sprintf("weak message %v is not linked in", fd.Message().FullName()))
 			}
-			return appendMessage(b, m, wiretag, opts)
+			return appendMessage(b, m, f.wiretag, opts)
 		},
-		unmarshal: func(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (unmarshalOutput, error) {
+		unmarshal: func(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (unmarshalOutput, error) {
 			fs := p.WeakFields()
 			m, ok := fs.get(num)
 			if !ok {
@@ -153,7 +150,7 @@
 			}
 			return consumeMessage(b, m, wtyp, opts)
 		},
-		isInit: func(p pointer) error {
+		isInit: func(p pointer, f *coderFieldInfo) error {
 			m, ok := p.WeakFields().get(num)
 			if !ok {
 				return nil
@@ -166,40 +163,32 @@
 func makeMessageFieldCoder(fd pref.FieldDescriptor, ft reflect.Type) pointerCoderFuncs {
 	if mi := getMessageInfo(ft); mi != nil {
 		funcs := pointerCoderFuncs{
-			size: func(p pointer, tagsize int, opts marshalOptions) int {
-				return sizeMessageInfo(p, mi, tagsize, opts)
-			},
-			marshal: func(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
-				return appendMessageInfo(b, p, wiretag, mi, opts)
-			},
-			unmarshal: func(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (unmarshalOutput, error) {
-				return consumeMessageInfo(b, p, mi, wtyp, opts)
-			},
+			size:      sizeMessageInfo,
+			marshal:   appendMessageInfo,
+			unmarshal: consumeMessageInfo,
 		}
 		if needsInitCheck(mi.Desc) {
-			funcs.isInit = func(p pointer) error {
-				return mi.isInitializedPointer(p.Elem())
-			}
+			funcs.isInit = isInitMessageInfo
 		}
 		return funcs
 	} else {
 		return pointerCoderFuncs{
-			size: func(p pointer, tagsize int, opts marshalOptions) int {
+			size: func(p pointer, f *coderFieldInfo, opts marshalOptions) int {
 				m := asMessage(p.AsValueOf(ft).Elem())
-				return sizeMessage(m, tagsize, opts)
+				return sizeMessage(m, f.tagsize, opts)
 			},
-			marshal: func(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
+			marshal: func(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
 				m := asMessage(p.AsValueOf(ft).Elem())
-				return appendMessage(b, m, wiretag, opts)
+				return appendMessage(b, m, f.wiretag, opts)
 			},
-			unmarshal: func(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (unmarshalOutput, error) {
+			unmarshal: func(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (unmarshalOutput, error) {
 				mp := p.AsValueOf(ft).Elem()
 				if mp.IsNil() {
 					mp.Set(reflect.New(ft.Elem()))
 				}
 				return consumeMessage(b, asMessage(mp), wtyp, opts)
 			},
-			isInit: func(p pointer) error {
+			isInit: func(p pointer, f *coderFieldInfo) error {
 				m := asMessage(p.AsValueOf(ft).Elem())
 				return proto.IsInitialized(m)
 			},
@@ -207,17 +196,17 @@
 	}
 }
 
-func sizeMessageInfo(p pointer, mi *MessageInfo, tagsize int, opts marshalOptions) int {
-	return wire.SizeBytes(mi.sizePointer(p.Elem(), opts)) + tagsize
+func sizeMessageInfo(p pointer, f *coderFieldInfo, opts marshalOptions) int {
+	return wire.SizeBytes(f.mi.sizePointer(p.Elem(), opts)) + f.tagsize
 }
 
-func appendMessageInfo(b []byte, p pointer, wiretag uint64, mi *MessageInfo, opts marshalOptions) ([]byte, error) {
-	b = wire.AppendVarint(b, wiretag)
-	b = wire.AppendVarint(b, uint64(mi.sizePointer(p.Elem(), opts)))
-	return mi.marshalAppendPointer(b, p.Elem(), opts)
+func appendMessageInfo(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
+	b = wire.AppendVarint(b, f.wiretag)
+	b = wire.AppendVarint(b, uint64(f.mi.sizePointer(p.Elem(), opts)))
+	return f.mi.marshalAppendPointer(b, p.Elem(), opts)
 }
 
-func consumeMessageInfo(b []byte, p pointer, mi *MessageInfo, wtyp wire.Type, opts unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeMessageInfo(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.BytesType {
 		return out, errUnknown
 	}
@@ -226,9 +215,9 @@
 		return out, wire.ParseError(n)
 	}
 	if p.Elem().IsNil() {
-		p.SetPointer(pointerOfValue(reflect.New(mi.GoReflectType.Elem())))
+		p.SetPointer(pointerOfValue(reflect.New(f.mi.GoReflectType.Elem())))
 	}
-	o, err := mi.unmarshalPointer(v, p.Elem(), 0, opts)
+	o, err := f.mi.unmarshalPointer(v, p.Elem(), 0, opts)
 	if err != nil {
 		return out, err
 	}
@@ -237,6 +226,10 @@
 	return out, nil
 }
 
+func isInitMessageInfo(p pointer, f *coderFieldInfo) error {
+	return f.mi.isInitializedPointer(p.Elem())
+}
+
 func sizeMessage(m proto.Message, tagsize int, _ marshalOptions) int {
 	return wire.SizeBytes(proto.Size(m)) + tagsize
 }
@@ -321,40 +314,32 @@
 	num := fd.Number()
 	if mi := getMessageInfo(ft); mi != nil {
 		funcs := pointerCoderFuncs{
-			size: func(p pointer, tagsize int, opts marshalOptions) int {
-				return sizeGroupType(p, mi, tagsize, opts)
-			},
-			marshal: func(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
-				return appendGroupType(b, p, wiretag, mi, opts)
-			},
-			unmarshal: func(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (unmarshalOutput, error) {
-				return consumeGroupType(b, p, mi, num, wtyp, opts)
-			},
+			size:      sizeGroupType,
+			marshal:   appendGroupType,
+			unmarshal: consumeGroupType,
 		}
 		if needsInitCheck(mi.Desc) {
-			funcs.isInit = func(p pointer) error {
-				return mi.isInitializedPointer(p.Elem())
-			}
+			funcs.isInit = isInitMessageInfo
 		}
 		return funcs
 	} else {
 		return pointerCoderFuncs{
-			size: func(p pointer, tagsize int, opts marshalOptions) int {
+			size: func(p pointer, f *coderFieldInfo, opts marshalOptions) int {
 				m := asMessage(p.AsValueOf(ft).Elem())
-				return sizeGroup(m, tagsize, opts)
+				return sizeGroup(m, f.tagsize, opts)
 			},
-			marshal: func(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
+			marshal: func(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
 				m := asMessage(p.AsValueOf(ft).Elem())
-				return appendGroup(b, m, wiretag, opts)
+				return appendGroup(b, m, f.wiretag, opts)
 			},
-			unmarshal: func(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (unmarshalOutput, error) {
+			unmarshal: func(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (unmarshalOutput, error) {
 				mp := p.AsValueOf(ft).Elem()
 				if mp.IsNil() {
 					mp.Set(reflect.New(ft.Elem()))
 				}
 				return consumeGroup(b, asMessage(mp), num, wtyp, opts)
 			},
-			isInit: func(p pointer) error {
+			isInit: func(p pointer, f *coderFieldInfo) error {
 				m := asMessage(p.AsValueOf(ft).Elem())
 				return proto.IsInitialized(m)
 			},
@@ -362,25 +347,25 @@
 	}
 }
 
-func sizeGroupType(p pointer, mi *MessageInfo, tagsize int, opts marshalOptions) int {
-	return 2*tagsize + mi.sizePointer(p.Elem(), opts)
+func sizeGroupType(p pointer, f *coderFieldInfo, opts marshalOptions) int {
+	return 2*f.tagsize + f.mi.sizePointer(p.Elem(), opts)
 }
 
-func appendGroupType(b []byte, p pointer, wiretag uint64, mi *MessageInfo, opts marshalOptions) ([]byte, error) {
-	b = wire.AppendVarint(b, wiretag) // start group
-	b, err := mi.marshalAppendPointer(b, p.Elem(), opts)
-	b = wire.AppendVarint(b, wiretag+1) // end group
+func appendGroupType(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
+	b = wire.AppendVarint(b, f.wiretag) // start group
+	b, err := f.mi.marshalAppendPointer(b, p.Elem(), opts)
+	b = wire.AppendVarint(b, f.wiretag+1) // end group
 	return b, err
 }
 
-func consumeGroupType(b []byte, p pointer, mi *MessageInfo, num wire.Number, wtyp wire.Type, opts unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeGroupType(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.StartGroupType {
 		return out, errUnknown
 	}
 	if p.Elem().IsNil() {
-		p.SetPointer(pointerOfValue(reflect.New(mi.GoReflectType.Elem())))
+		p.SetPointer(pointerOfValue(reflect.New(f.mi.GoReflectType.Elem())))
 	}
-	return mi.unmarshalPointer(b, p.Elem(), num, opts)
+	return f.mi.unmarshalPointer(b, p.Elem(), f.num, opts)
 }
 
 func sizeGroup(m proto.Message, tagsize int, _ marshalOptions) int {
@@ -416,56 +401,48 @@
 func makeMessageSliceFieldCoder(fd pref.FieldDescriptor, ft reflect.Type) pointerCoderFuncs {
 	if mi := getMessageInfo(ft); mi != nil {
 		funcs := pointerCoderFuncs{
-			size: func(p pointer, tagsize int, opts marshalOptions) int {
-				return sizeMessageSliceInfo(p, mi, tagsize, opts)
-			},
-			marshal: func(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
-				return appendMessageSliceInfo(b, p, wiretag, mi, opts)
-			},
-			unmarshal: func(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (unmarshalOutput, error) {
-				return consumeMessageSliceInfo(b, p, mi, wtyp, opts)
-			},
+			size:      sizeMessageSliceInfo,
+			marshal:   appendMessageSliceInfo,
+			unmarshal: consumeMessageSliceInfo,
 		}
 		if needsInitCheck(mi.Desc) {
-			funcs.isInit = func(p pointer) error {
-				return isInitMessageSliceInfo(p, mi)
-			}
+			funcs.isInit = isInitMessageSliceInfo
 		}
 		return funcs
 	}
 	return pointerCoderFuncs{
-		size: func(p pointer, tagsize int, opts marshalOptions) int {
-			return sizeMessageSlice(p, ft, tagsize, opts)
+		size: func(p pointer, f *coderFieldInfo, opts marshalOptions) int {
+			return sizeMessageSlice(p, ft, f.tagsize, opts)
 		},
-		marshal: func(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
-			return appendMessageSlice(b, p, wiretag, ft, opts)
+		marshal: func(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
+			return appendMessageSlice(b, p, f.wiretag, ft, opts)
 		},
-		unmarshal: func(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (unmarshalOutput, error) {
+		unmarshal: func(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (unmarshalOutput, error) {
 			return consumeMessageSlice(b, p, ft, wtyp, opts)
 		},
-		isInit: func(p pointer) error {
+		isInit: func(p pointer, f *coderFieldInfo) error {
 			return isInitMessageSlice(p, ft)
 		},
 	}
 }
 
-func sizeMessageSliceInfo(p pointer, mi *MessageInfo, tagsize int, opts marshalOptions) int {
+func sizeMessageSliceInfo(p pointer, f *coderFieldInfo, opts marshalOptions) int {
 	s := p.PointerSlice()
 	n := 0
 	for _, v := range s {
-		n += wire.SizeBytes(mi.sizePointer(v, opts)) + tagsize
+		n += wire.SizeBytes(f.mi.sizePointer(v, opts)) + f.tagsize
 	}
 	return n
 }
 
-func appendMessageSliceInfo(b []byte, p pointer, wiretag uint64, mi *MessageInfo, opts marshalOptions) ([]byte, error) {
+func appendMessageSliceInfo(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
 	s := p.PointerSlice()
 	var err error
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
-		siz := mi.sizePointer(v, opts)
+		b = wire.AppendVarint(b, f.wiretag)
+		siz := f.mi.sizePointer(v, opts)
 		b = wire.AppendVarint(b, uint64(siz))
-		b, err = mi.marshalAppendPointer(b, v, opts)
+		b, err = f.mi.marshalAppendPointer(b, v, opts)
 		if err != nil {
 			return b, err
 		}
@@ -473,7 +450,7 @@
 	return b, nil
 }
 
-func consumeMessageSliceInfo(b []byte, p pointer, mi *MessageInfo, wtyp wire.Type, opts unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeMessageSliceInfo(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.BytesType {
 		return out, errUnknown
 	}
@@ -481,9 +458,9 @@
 	if n < 0 {
 		return out, wire.ParseError(n)
 	}
-	m := reflect.New(mi.GoReflectType.Elem()).Interface()
+	m := reflect.New(f.mi.GoReflectType.Elem()).Interface()
 	mp := pointerOfIface(m)
-	o, err := mi.unmarshalPointer(v, mp, 0, opts)
+	o, err := f.mi.unmarshalPointer(v, mp, 0, opts)
 	if err != nil {
 		return out, err
 	}
@@ -493,10 +470,10 @@
 	return out, nil
 }
 
-func isInitMessageSliceInfo(p pointer, mi *MessageInfo) error {
+func isInitMessageSliceInfo(p pointer, f *coderFieldInfo) error {
 	s := p.PointerSlice()
 	for _, v := range s {
-		if err := mi.isInitializedPointer(v); err != nil {
+		if err := f.mi.isInitializedPointer(v); err != nil {
 			return err
 		}
 	}
@@ -689,34 +666,26 @@
 	num := fd.Number()
 	if mi := getMessageInfo(ft); mi != nil {
 		funcs := pointerCoderFuncs{
-			size: func(p pointer, tagsize int, opts marshalOptions) int {
-				return sizeGroupSliceInfo(p, mi, tagsize, opts)
-			},
-			marshal: func(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
-				return appendGroupSliceInfo(b, p, wiretag, mi, opts)
-			},
-			unmarshal: func(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (unmarshalOutput, error) {
-				return consumeGroupSliceInfo(b, p, num, wtyp, mi, opts)
-			},
+			size:      sizeGroupSliceInfo,
+			marshal:   appendGroupSliceInfo,
+			unmarshal: consumeGroupSliceInfo,
 		}
 		if needsInitCheck(mi.Desc) {
-			funcs.isInit = func(p pointer) error {
-				return isInitMessageSliceInfo(p, mi)
-			}
+			funcs.isInit = isInitMessageSliceInfo
 		}
 		return funcs
 	}
 	return pointerCoderFuncs{
-		size: func(p pointer, tagsize int, opts marshalOptions) int {
-			return sizeGroupSlice(p, ft, tagsize, opts)
+		size: func(p pointer, f *coderFieldInfo, opts marshalOptions) int {
+			return sizeGroupSlice(p, ft, f.tagsize, opts)
 		},
-		marshal: func(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
-			return appendGroupSlice(b, p, wiretag, ft, opts)
+		marshal: func(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
+			return appendGroupSlice(b, p, f.wiretag, ft, opts)
 		},
-		unmarshal: func(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (unmarshalOutput, error) {
+		unmarshal: func(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (unmarshalOutput, error) {
 			return consumeGroupSlice(b, p, num, wtyp, ft, opts)
 		},
-		isInit: func(p pointer) error {
+		isInit: func(p pointer, f *coderFieldInfo) error {
 			return isInitMessageSlice(p, ft)
 		},
 	}
@@ -768,36 +737,36 @@
 	return out, nil
 }
 
-func sizeGroupSliceInfo(p pointer, mi *MessageInfo, tagsize int, opts marshalOptions) int {
+func sizeGroupSliceInfo(p pointer, f *coderFieldInfo, opts marshalOptions) int {
 	s := p.PointerSlice()
 	n := 0
 	for _, v := range s {
-		n += 2*tagsize + mi.sizePointer(v, opts)
+		n += 2*f.tagsize + f.mi.sizePointer(v, opts)
 	}
 	return n
 }
 
-func appendGroupSliceInfo(b []byte, p pointer, wiretag uint64, mi *MessageInfo, opts marshalOptions) ([]byte, error) {
+func appendGroupSliceInfo(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
 	s := p.PointerSlice()
 	var err error
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag) // start group
-		b, err = mi.marshalAppendPointer(b, v, opts)
+		b = wire.AppendVarint(b, f.wiretag) // start group
+		b, err = f.mi.marshalAppendPointer(b, v, opts)
 		if err != nil {
 			return b, err
 		}
-		b = wire.AppendVarint(b, wiretag+1) // end group
+		b = wire.AppendVarint(b, f.wiretag+1) // end group
 	}
 	return b, nil
 }
 
-func consumeGroupSliceInfo(b []byte, p pointer, num wire.Number, wtyp wire.Type, mi *MessageInfo, opts unmarshalOptions) (unmarshalOutput, error) {
+func consumeGroupSliceInfo(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (unmarshalOutput, error) {
 	if wtyp != wire.StartGroupType {
 		return unmarshalOutput{}, errUnknown
 	}
-	m := reflect.New(mi.GoReflectType.Elem()).Interface()
+	m := reflect.New(f.mi.GoReflectType.Elem()).Interface()
 	mp := pointerOfIface(m)
-	out, err := mi.unmarshalPointer(b, mp, num, opts)
+	out, err := f.mi.unmarshalPointer(b, mp, f.num, opts)
 	if err != nil {
 		return out, err
 	}
diff --git a/internal/impl/codec_gen.go b/internal/impl/codec_gen.go
index 79f63fb..fa41a85 100644
--- a/internal/impl/codec_gen.go
+++ b/internal/impl/codec_gen.go
@@ -15,21 +15,21 @@
 )
 
 // sizeBool returns the size of wire encoding a bool pointer as a Bool.
-func sizeBool(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeBool(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Bool()
-	return tagsize + wire.SizeVarint(wire.EncodeBool(v))
+	return f.tagsize + wire.SizeVarint(wire.EncodeBool(v))
 }
 
 // appendBool wire encodes a bool pointer as a Bool.
-func appendBool(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendBool(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Bool()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, wire.EncodeBool(v))
 	return b, nil
 }
 
 // consumeBool wire decodes a bool pointer as a Bool.
-func consumeBool(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeBool(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -60,22 +60,22 @@
 
 // sizeBoolNoZero returns the size of wire encoding a bool pointer as a Bool.
 // The zero value is not encoded.
-func sizeBoolNoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeBoolNoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Bool()
 	if v == false {
 		return 0
 	}
-	return tagsize + wire.SizeVarint(wire.EncodeBool(v))
+	return f.tagsize + wire.SizeVarint(wire.EncodeBool(v))
 }
 
 // appendBoolNoZero wire encodes a bool pointer as a Bool.
 // The zero value is not encoded.
-func appendBoolNoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendBoolNoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Bool()
 	if v == false {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, wire.EncodeBool(v))
 	return b, nil
 }
@@ -88,22 +88,22 @@
 
 // sizeBoolPtr returns the size of wire encoding a *bool pointer as a Bool.
 // It panics if the pointer is nil.
-func sizeBoolPtr(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeBoolPtr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := **p.BoolPtr()
-	return tagsize + wire.SizeVarint(wire.EncodeBool(v))
+	return f.tagsize + wire.SizeVarint(wire.EncodeBool(v))
 }
 
 // appendBoolPtr wire encodes a *bool pointer as a Bool.
 // It panics if the pointer is nil.
-func appendBoolPtr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendBoolPtr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.BoolPtr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, wire.EncodeBool(v))
 	return b, nil
 }
 
 // consumeBoolPtr wire decodes a *bool pointer as a Bool.
-func consumeBoolPtr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeBoolPtr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -137,26 +137,26 @@
 }
 
 // sizeBoolSlice returns the size of wire encoding a []bool pointer as a repeated Bool.
-func sizeBoolSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeBoolSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.BoolSlice()
 	for _, v := range s {
-		size += tagsize + wire.SizeVarint(wire.EncodeBool(v))
+		size += f.tagsize + wire.SizeVarint(wire.EncodeBool(v))
 	}
 	return size
 }
 
 // appendBoolSlice encodes a []bool pointer as a repeated Bool.
-func appendBoolSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendBoolSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.BoolSlice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendVarint(b, wire.EncodeBool(v))
 	}
 	return b, nil
 }
 
 // consumeBoolSlice wire decodes a []bool pointer as a repeated Bool.
-func consumeBoolSlice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeBoolSlice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.BoolSlice()
 	if wtyp == wire.BytesType {
 		s := *sp
@@ -215,7 +215,7 @@
 }
 
 // sizeBoolPackedSlice returns the size of wire encoding a []bool pointer as a packed repeated Bool.
-func sizeBoolPackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeBoolPackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.BoolSlice()
 	if len(s) == 0 {
 		return 0
@@ -224,16 +224,16 @@
 	for _, v := range s {
 		n += wire.SizeVarint(wire.EncodeBool(v))
 	}
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // appendBoolPackedSlice encodes a []bool pointer as a packed repeated Bool.
-func appendBoolPackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendBoolPackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.BoolSlice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := 0
 	for _, v := range s {
 		n += wire.SizeVarint(wire.EncodeBool(v))
@@ -574,21 +574,21 @@
 }
 
 // sizeInt32 returns the size of wire encoding a int32 pointer as a Int32.
-func sizeInt32(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeInt32(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Int32()
-	return tagsize + wire.SizeVarint(uint64(v))
+	return f.tagsize + wire.SizeVarint(uint64(v))
 }
 
 // appendInt32 wire encodes a int32 pointer as a Int32.
-func appendInt32(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendInt32(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Int32()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, uint64(v))
 	return b, nil
 }
 
 // consumeInt32 wire decodes a int32 pointer as a Int32.
-func consumeInt32(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeInt32(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -619,22 +619,22 @@
 
 // sizeInt32NoZero returns the size of wire encoding a int32 pointer as a Int32.
 // The zero value is not encoded.
-func sizeInt32NoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeInt32NoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Int32()
 	if v == 0 {
 		return 0
 	}
-	return tagsize + wire.SizeVarint(uint64(v))
+	return f.tagsize + wire.SizeVarint(uint64(v))
 }
 
 // appendInt32NoZero wire encodes a int32 pointer as a Int32.
 // The zero value is not encoded.
-func appendInt32NoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendInt32NoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Int32()
 	if v == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, uint64(v))
 	return b, nil
 }
@@ -647,22 +647,22 @@
 
 // sizeInt32Ptr returns the size of wire encoding a *int32 pointer as a Int32.
 // It panics if the pointer is nil.
-func sizeInt32Ptr(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeInt32Ptr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := **p.Int32Ptr()
-	return tagsize + wire.SizeVarint(uint64(v))
+	return f.tagsize + wire.SizeVarint(uint64(v))
 }
 
 // appendInt32Ptr wire encodes a *int32 pointer as a Int32.
 // It panics if the pointer is nil.
-func appendInt32Ptr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendInt32Ptr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.Int32Ptr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, uint64(v))
 	return b, nil
 }
 
 // consumeInt32Ptr wire decodes a *int32 pointer as a Int32.
-func consumeInt32Ptr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeInt32Ptr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -696,26 +696,26 @@
 }
 
 // sizeInt32Slice returns the size of wire encoding a []int32 pointer as a repeated Int32.
-func sizeInt32Slice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeInt32Slice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Int32Slice()
 	for _, v := range s {
-		size += tagsize + wire.SizeVarint(uint64(v))
+		size += f.tagsize + wire.SizeVarint(uint64(v))
 	}
 	return size
 }
 
 // appendInt32Slice encodes a []int32 pointer as a repeated Int32.
-func appendInt32Slice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendInt32Slice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Int32Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendVarint(b, uint64(v))
 	}
 	return b, nil
 }
 
 // consumeInt32Slice wire decodes a []int32 pointer as a repeated Int32.
-func consumeInt32Slice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeInt32Slice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.Int32Slice()
 	if wtyp == wire.BytesType {
 		s := *sp
@@ -774,7 +774,7 @@
 }
 
 // sizeInt32PackedSlice returns the size of wire encoding a []int32 pointer as a packed repeated Int32.
-func sizeInt32PackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeInt32PackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Int32Slice()
 	if len(s) == 0 {
 		return 0
@@ -783,16 +783,16 @@
 	for _, v := range s {
 		n += wire.SizeVarint(uint64(v))
 	}
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // appendInt32PackedSlice encodes a []int32 pointer as a packed repeated Int32.
-func appendInt32PackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendInt32PackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Int32Slice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := 0
 	for _, v := range s {
 		n += wire.SizeVarint(uint64(v))
@@ -972,21 +972,21 @@
 }
 
 // sizeSint32 returns the size of wire encoding a int32 pointer as a Sint32.
-func sizeSint32(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSint32(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Int32()
-	return tagsize + wire.SizeVarint(wire.EncodeZigZag(int64(v)))
+	return f.tagsize + wire.SizeVarint(wire.EncodeZigZag(int64(v)))
 }
 
 // appendSint32 wire encodes a int32 pointer as a Sint32.
-func appendSint32(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSint32(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Int32()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, wire.EncodeZigZag(int64(v)))
 	return b, nil
 }
 
 // consumeSint32 wire decodes a int32 pointer as a Sint32.
-func consumeSint32(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeSint32(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -1017,22 +1017,22 @@
 
 // sizeSint32NoZero returns the size of wire encoding a int32 pointer as a Sint32.
 // The zero value is not encoded.
-func sizeSint32NoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSint32NoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Int32()
 	if v == 0 {
 		return 0
 	}
-	return tagsize + wire.SizeVarint(wire.EncodeZigZag(int64(v)))
+	return f.tagsize + wire.SizeVarint(wire.EncodeZigZag(int64(v)))
 }
 
 // appendSint32NoZero wire encodes a int32 pointer as a Sint32.
 // The zero value is not encoded.
-func appendSint32NoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSint32NoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Int32()
 	if v == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, wire.EncodeZigZag(int64(v)))
 	return b, nil
 }
@@ -1045,22 +1045,22 @@
 
 // sizeSint32Ptr returns the size of wire encoding a *int32 pointer as a Sint32.
 // It panics if the pointer is nil.
-func sizeSint32Ptr(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSint32Ptr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := **p.Int32Ptr()
-	return tagsize + wire.SizeVarint(wire.EncodeZigZag(int64(v)))
+	return f.tagsize + wire.SizeVarint(wire.EncodeZigZag(int64(v)))
 }
 
 // appendSint32Ptr wire encodes a *int32 pointer as a Sint32.
 // It panics if the pointer is nil.
-func appendSint32Ptr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSint32Ptr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.Int32Ptr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, wire.EncodeZigZag(int64(v)))
 	return b, nil
 }
 
 // consumeSint32Ptr wire decodes a *int32 pointer as a Sint32.
-func consumeSint32Ptr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeSint32Ptr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -1094,26 +1094,26 @@
 }
 
 // sizeSint32Slice returns the size of wire encoding a []int32 pointer as a repeated Sint32.
-func sizeSint32Slice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSint32Slice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Int32Slice()
 	for _, v := range s {
-		size += tagsize + wire.SizeVarint(wire.EncodeZigZag(int64(v)))
+		size += f.tagsize + wire.SizeVarint(wire.EncodeZigZag(int64(v)))
 	}
 	return size
 }
 
 // appendSint32Slice encodes a []int32 pointer as a repeated Sint32.
-func appendSint32Slice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSint32Slice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Int32Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendVarint(b, wire.EncodeZigZag(int64(v)))
 	}
 	return b, nil
 }
 
 // consumeSint32Slice wire decodes a []int32 pointer as a repeated Sint32.
-func consumeSint32Slice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeSint32Slice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.Int32Slice()
 	if wtyp == wire.BytesType {
 		s := *sp
@@ -1172,7 +1172,7 @@
 }
 
 // sizeSint32PackedSlice returns the size of wire encoding a []int32 pointer as a packed repeated Sint32.
-func sizeSint32PackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSint32PackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Int32Slice()
 	if len(s) == 0 {
 		return 0
@@ -1181,16 +1181,16 @@
 	for _, v := range s {
 		n += wire.SizeVarint(wire.EncodeZigZag(int64(v)))
 	}
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // appendSint32PackedSlice encodes a []int32 pointer as a packed repeated Sint32.
-func appendSint32PackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSint32PackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Int32Slice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := 0
 	for _, v := range s {
 		n += wire.SizeVarint(wire.EncodeZigZag(int64(v)))
@@ -1370,21 +1370,21 @@
 }
 
 // sizeUint32 returns the size of wire encoding a uint32 pointer as a Uint32.
-func sizeUint32(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeUint32(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Uint32()
-	return tagsize + wire.SizeVarint(uint64(v))
+	return f.tagsize + wire.SizeVarint(uint64(v))
 }
 
 // appendUint32 wire encodes a uint32 pointer as a Uint32.
-func appendUint32(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendUint32(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Uint32()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, uint64(v))
 	return b, nil
 }
 
 // consumeUint32 wire decodes a uint32 pointer as a Uint32.
-func consumeUint32(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeUint32(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -1415,22 +1415,22 @@
 
 // sizeUint32NoZero returns the size of wire encoding a uint32 pointer as a Uint32.
 // The zero value is not encoded.
-func sizeUint32NoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeUint32NoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Uint32()
 	if v == 0 {
 		return 0
 	}
-	return tagsize + wire.SizeVarint(uint64(v))
+	return f.tagsize + wire.SizeVarint(uint64(v))
 }
 
 // appendUint32NoZero wire encodes a uint32 pointer as a Uint32.
 // The zero value is not encoded.
-func appendUint32NoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendUint32NoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Uint32()
 	if v == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, uint64(v))
 	return b, nil
 }
@@ -1443,22 +1443,22 @@
 
 // sizeUint32Ptr returns the size of wire encoding a *uint32 pointer as a Uint32.
 // It panics if the pointer is nil.
-func sizeUint32Ptr(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeUint32Ptr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := **p.Uint32Ptr()
-	return tagsize + wire.SizeVarint(uint64(v))
+	return f.tagsize + wire.SizeVarint(uint64(v))
 }
 
 // appendUint32Ptr wire encodes a *uint32 pointer as a Uint32.
 // It panics if the pointer is nil.
-func appendUint32Ptr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendUint32Ptr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.Uint32Ptr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, uint64(v))
 	return b, nil
 }
 
 // consumeUint32Ptr wire decodes a *uint32 pointer as a Uint32.
-func consumeUint32Ptr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeUint32Ptr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -1492,26 +1492,26 @@
 }
 
 // sizeUint32Slice returns the size of wire encoding a []uint32 pointer as a repeated Uint32.
-func sizeUint32Slice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeUint32Slice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Uint32Slice()
 	for _, v := range s {
-		size += tagsize + wire.SizeVarint(uint64(v))
+		size += f.tagsize + wire.SizeVarint(uint64(v))
 	}
 	return size
 }
 
 // appendUint32Slice encodes a []uint32 pointer as a repeated Uint32.
-func appendUint32Slice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendUint32Slice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Uint32Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendVarint(b, uint64(v))
 	}
 	return b, nil
 }
 
 // consumeUint32Slice wire decodes a []uint32 pointer as a repeated Uint32.
-func consumeUint32Slice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeUint32Slice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.Uint32Slice()
 	if wtyp == wire.BytesType {
 		s := *sp
@@ -1570,7 +1570,7 @@
 }
 
 // sizeUint32PackedSlice returns the size of wire encoding a []uint32 pointer as a packed repeated Uint32.
-func sizeUint32PackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeUint32PackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Uint32Slice()
 	if len(s) == 0 {
 		return 0
@@ -1579,16 +1579,16 @@
 	for _, v := range s {
 		n += wire.SizeVarint(uint64(v))
 	}
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // appendUint32PackedSlice encodes a []uint32 pointer as a packed repeated Uint32.
-func appendUint32PackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendUint32PackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Uint32Slice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := 0
 	for _, v := range s {
 		n += wire.SizeVarint(uint64(v))
@@ -1768,21 +1768,21 @@
 }
 
 // sizeInt64 returns the size of wire encoding a int64 pointer as a Int64.
-func sizeInt64(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeInt64(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Int64()
-	return tagsize + wire.SizeVarint(uint64(v))
+	return f.tagsize + wire.SizeVarint(uint64(v))
 }
 
 // appendInt64 wire encodes a int64 pointer as a Int64.
-func appendInt64(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendInt64(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Int64()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, uint64(v))
 	return b, nil
 }
 
 // consumeInt64 wire decodes a int64 pointer as a Int64.
-func consumeInt64(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeInt64(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -1813,22 +1813,22 @@
 
 // sizeInt64NoZero returns the size of wire encoding a int64 pointer as a Int64.
 // The zero value is not encoded.
-func sizeInt64NoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeInt64NoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Int64()
 	if v == 0 {
 		return 0
 	}
-	return tagsize + wire.SizeVarint(uint64(v))
+	return f.tagsize + wire.SizeVarint(uint64(v))
 }
 
 // appendInt64NoZero wire encodes a int64 pointer as a Int64.
 // The zero value is not encoded.
-func appendInt64NoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendInt64NoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Int64()
 	if v == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, uint64(v))
 	return b, nil
 }
@@ -1841,22 +1841,22 @@
 
 // sizeInt64Ptr returns the size of wire encoding a *int64 pointer as a Int64.
 // It panics if the pointer is nil.
-func sizeInt64Ptr(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeInt64Ptr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := **p.Int64Ptr()
-	return tagsize + wire.SizeVarint(uint64(v))
+	return f.tagsize + wire.SizeVarint(uint64(v))
 }
 
 // appendInt64Ptr wire encodes a *int64 pointer as a Int64.
 // It panics if the pointer is nil.
-func appendInt64Ptr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendInt64Ptr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.Int64Ptr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, uint64(v))
 	return b, nil
 }
 
 // consumeInt64Ptr wire decodes a *int64 pointer as a Int64.
-func consumeInt64Ptr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeInt64Ptr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -1890,26 +1890,26 @@
 }
 
 // sizeInt64Slice returns the size of wire encoding a []int64 pointer as a repeated Int64.
-func sizeInt64Slice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeInt64Slice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Int64Slice()
 	for _, v := range s {
-		size += tagsize + wire.SizeVarint(uint64(v))
+		size += f.tagsize + wire.SizeVarint(uint64(v))
 	}
 	return size
 }
 
 // appendInt64Slice encodes a []int64 pointer as a repeated Int64.
-func appendInt64Slice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendInt64Slice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Int64Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendVarint(b, uint64(v))
 	}
 	return b, nil
 }
 
 // consumeInt64Slice wire decodes a []int64 pointer as a repeated Int64.
-func consumeInt64Slice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeInt64Slice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.Int64Slice()
 	if wtyp == wire.BytesType {
 		s := *sp
@@ -1968,7 +1968,7 @@
 }
 
 // sizeInt64PackedSlice returns the size of wire encoding a []int64 pointer as a packed repeated Int64.
-func sizeInt64PackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeInt64PackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Int64Slice()
 	if len(s) == 0 {
 		return 0
@@ -1977,16 +1977,16 @@
 	for _, v := range s {
 		n += wire.SizeVarint(uint64(v))
 	}
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // appendInt64PackedSlice encodes a []int64 pointer as a packed repeated Int64.
-func appendInt64PackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendInt64PackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Int64Slice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := 0
 	for _, v := range s {
 		n += wire.SizeVarint(uint64(v))
@@ -2166,21 +2166,21 @@
 }
 
 // sizeSint64 returns the size of wire encoding a int64 pointer as a Sint64.
-func sizeSint64(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSint64(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Int64()
-	return tagsize + wire.SizeVarint(wire.EncodeZigZag(v))
+	return f.tagsize + wire.SizeVarint(wire.EncodeZigZag(v))
 }
 
 // appendSint64 wire encodes a int64 pointer as a Sint64.
-func appendSint64(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSint64(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Int64()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, wire.EncodeZigZag(v))
 	return b, nil
 }
 
 // consumeSint64 wire decodes a int64 pointer as a Sint64.
-func consumeSint64(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeSint64(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -2211,22 +2211,22 @@
 
 // sizeSint64NoZero returns the size of wire encoding a int64 pointer as a Sint64.
 // The zero value is not encoded.
-func sizeSint64NoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSint64NoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Int64()
 	if v == 0 {
 		return 0
 	}
-	return tagsize + wire.SizeVarint(wire.EncodeZigZag(v))
+	return f.tagsize + wire.SizeVarint(wire.EncodeZigZag(v))
 }
 
 // appendSint64NoZero wire encodes a int64 pointer as a Sint64.
 // The zero value is not encoded.
-func appendSint64NoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSint64NoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Int64()
 	if v == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, wire.EncodeZigZag(v))
 	return b, nil
 }
@@ -2239,22 +2239,22 @@
 
 // sizeSint64Ptr returns the size of wire encoding a *int64 pointer as a Sint64.
 // It panics if the pointer is nil.
-func sizeSint64Ptr(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSint64Ptr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := **p.Int64Ptr()
-	return tagsize + wire.SizeVarint(wire.EncodeZigZag(v))
+	return f.tagsize + wire.SizeVarint(wire.EncodeZigZag(v))
 }
 
 // appendSint64Ptr wire encodes a *int64 pointer as a Sint64.
 // It panics if the pointer is nil.
-func appendSint64Ptr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSint64Ptr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.Int64Ptr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, wire.EncodeZigZag(v))
 	return b, nil
 }
 
 // consumeSint64Ptr wire decodes a *int64 pointer as a Sint64.
-func consumeSint64Ptr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeSint64Ptr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -2288,26 +2288,26 @@
 }
 
 // sizeSint64Slice returns the size of wire encoding a []int64 pointer as a repeated Sint64.
-func sizeSint64Slice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSint64Slice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Int64Slice()
 	for _, v := range s {
-		size += tagsize + wire.SizeVarint(wire.EncodeZigZag(v))
+		size += f.tagsize + wire.SizeVarint(wire.EncodeZigZag(v))
 	}
 	return size
 }
 
 // appendSint64Slice encodes a []int64 pointer as a repeated Sint64.
-func appendSint64Slice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSint64Slice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Int64Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendVarint(b, wire.EncodeZigZag(v))
 	}
 	return b, nil
 }
 
 // consumeSint64Slice wire decodes a []int64 pointer as a repeated Sint64.
-func consumeSint64Slice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeSint64Slice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.Int64Slice()
 	if wtyp == wire.BytesType {
 		s := *sp
@@ -2366,7 +2366,7 @@
 }
 
 // sizeSint64PackedSlice returns the size of wire encoding a []int64 pointer as a packed repeated Sint64.
-func sizeSint64PackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSint64PackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Int64Slice()
 	if len(s) == 0 {
 		return 0
@@ -2375,16 +2375,16 @@
 	for _, v := range s {
 		n += wire.SizeVarint(wire.EncodeZigZag(v))
 	}
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // appendSint64PackedSlice encodes a []int64 pointer as a packed repeated Sint64.
-func appendSint64PackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSint64PackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Int64Slice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := 0
 	for _, v := range s {
 		n += wire.SizeVarint(wire.EncodeZigZag(v))
@@ -2564,21 +2564,21 @@
 }
 
 // sizeUint64 returns the size of wire encoding a uint64 pointer as a Uint64.
-func sizeUint64(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeUint64(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Uint64()
-	return tagsize + wire.SizeVarint(v)
+	return f.tagsize + wire.SizeVarint(v)
 }
 
 // appendUint64 wire encodes a uint64 pointer as a Uint64.
-func appendUint64(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendUint64(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Uint64()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, v)
 	return b, nil
 }
 
 // consumeUint64 wire decodes a uint64 pointer as a Uint64.
-func consumeUint64(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeUint64(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -2609,22 +2609,22 @@
 
 // sizeUint64NoZero returns the size of wire encoding a uint64 pointer as a Uint64.
 // The zero value is not encoded.
-func sizeUint64NoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeUint64NoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Uint64()
 	if v == 0 {
 		return 0
 	}
-	return tagsize + wire.SizeVarint(v)
+	return f.tagsize + wire.SizeVarint(v)
 }
 
 // appendUint64NoZero wire encodes a uint64 pointer as a Uint64.
 // The zero value is not encoded.
-func appendUint64NoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendUint64NoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Uint64()
 	if v == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, v)
 	return b, nil
 }
@@ -2637,22 +2637,22 @@
 
 // sizeUint64Ptr returns the size of wire encoding a *uint64 pointer as a Uint64.
 // It panics if the pointer is nil.
-func sizeUint64Ptr(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeUint64Ptr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := **p.Uint64Ptr()
-	return tagsize + wire.SizeVarint(v)
+	return f.tagsize + wire.SizeVarint(v)
 }
 
 // appendUint64Ptr wire encodes a *uint64 pointer as a Uint64.
 // It panics if the pointer is nil.
-func appendUint64Ptr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendUint64Ptr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.Uint64Ptr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, v)
 	return b, nil
 }
 
 // consumeUint64Ptr wire decodes a *uint64 pointer as a Uint64.
-func consumeUint64Ptr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeUint64Ptr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -2686,26 +2686,26 @@
 }
 
 // sizeUint64Slice returns the size of wire encoding a []uint64 pointer as a repeated Uint64.
-func sizeUint64Slice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeUint64Slice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Uint64Slice()
 	for _, v := range s {
-		size += tagsize + wire.SizeVarint(v)
+		size += f.tagsize + wire.SizeVarint(v)
 	}
 	return size
 }
 
 // appendUint64Slice encodes a []uint64 pointer as a repeated Uint64.
-func appendUint64Slice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendUint64Slice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Uint64Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendVarint(b, v)
 	}
 	return b, nil
 }
 
 // consumeUint64Slice wire decodes a []uint64 pointer as a repeated Uint64.
-func consumeUint64Slice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeUint64Slice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.Uint64Slice()
 	if wtyp == wire.BytesType {
 		s := *sp
@@ -2764,7 +2764,7 @@
 }
 
 // sizeUint64PackedSlice returns the size of wire encoding a []uint64 pointer as a packed repeated Uint64.
-func sizeUint64PackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeUint64PackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Uint64Slice()
 	if len(s) == 0 {
 		return 0
@@ -2773,16 +2773,16 @@
 	for _, v := range s {
 		n += wire.SizeVarint(v)
 	}
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // appendUint64PackedSlice encodes a []uint64 pointer as a packed repeated Uint64.
-func appendUint64PackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendUint64PackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Uint64Slice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := 0
 	for _, v := range s {
 		n += wire.SizeVarint(v)
@@ -2962,21 +2962,21 @@
 }
 
 // sizeSfixed32 returns the size of wire encoding a int32 pointer as a Sfixed32.
-func sizeSfixed32(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSfixed32(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 
-	return tagsize + wire.SizeFixed32()
+	return f.tagsize + wire.SizeFixed32()
 }
 
 // appendSfixed32 wire encodes a int32 pointer as a Sfixed32.
-func appendSfixed32(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSfixed32(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Int32()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed32(b, uint32(v))
 	return b, nil
 }
 
 // consumeSfixed32 wire decodes a int32 pointer as a Sfixed32.
-func consumeSfixed32(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeSfixed32(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.Fixed32Type {
 		return out, errUnknown
 	}
@@ -2997,22 +2997,22 @@
 
 // sizeSfixed32NoZero returns the size of wire encoding a int32 pointer as a Sfixed32.
 // The zero value is not encoded.
-func sizeSfixed32NoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSfixed32NoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Int32()
 	if v == 0 {
 		return 0
 	}
-	return tagsize + wire.SizeFixed32()
+	return f.tagsize + wire.SizeFixed32()
 }
 
 // appendSfixed32NoZero wire encodes a int32 pointer as a Sfixed32.
 // The zero value is not encoded.
-func appendSfixed32NoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSfixed32NoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Int32()
 	if v == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed32(b, uint32(v))
 	return b, nil
 }
@@ -3025,21 +3025,21 @@
 
 // sizeSfixed32Ptr returns the size of wire encoding a *int32 pointer as a Sfixed32.
 // It panics if the pointer is nil.
-func sizeSfixed32Ptr(p pointer, tagsize int, _ marshalOptions) (size int) {
-	return tagsize + wire.SizeFixed32()
+func sizeSfixed32Ptr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
+	return f.tagsize + wire.SizeFixed32()
 }
 
 // appendSfixed32Ptr wire encodes a *int32 pointer as a Sfixed32.
 // It panics if the pointer is nil.
-func appendSfixed32Ptr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSfixed32Ptr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.Int32Ptr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed32(b, uint32(v))
 	return b, nil
 }
 
 // consumeSfixed32Ptr wire decodes a *int32 pointer as a Sfixed32.
-func consumeSfixed32Ptr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeSfixed32Ptr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.Fixed32Type {
 		return out, errUnknown
 	}
@@ -3063,24 +3063,24 @@
 }
 
 // sizeSfixed32Slice returns the size of wire encoding a []int32 pointer as a repeated Sfixed32.
-func sizeSfixed32Slice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSfixed32Slice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Int32Slice()
-	size = len(s) * (tagsize + wire.SizeFixed32())
+	size = len(s) * (f.tagsize + wire.SizeFixed32())
 	return size
 }
 
 // appendSfixed32Slice encodes a []int32 pointer as a repeated Sfixed32.
-func appendSfixed32Slice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSfixed32Slice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Int32Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendFixed32(b, uint32(v))
 	}
 	return b, nil
 }
 
 // consumeSfixed32Slice wire decodes a []int32 pointer as a repeated Sfixed32.
-func consumeSfixed32Slice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeSfixed32Slice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.Int32Slice()
 	if wtyp == wire.BytesType {
 		s := *sp
@@ -3119,22 +3119,22 @@
 }
 
 // sizeSfixed32PackedSlice returns the size of wire encoding a []int32 pointer as a packed repeated Sfixed32.
-func sizeSfixed32PackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSfixed32PackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Int32Slice()
 	if len(s) == 0 {
 		return 0
 	}
 	n := len(s) * wire.SizeFixed32()
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // appendSfixed32PackedSlice encodes a []int32 pointer as a packed repeated Sfixed32.
-func appendSfixed32PackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSfixed32PackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Int32Slice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := len(s) * wire.SizeFixed32()
 	b = wire.AppendVarint(b, uint64(n))
 	for _, v := range s {
@@ -3270,21 +3270,21 @@
 }
 
 // sizeFixed32 returns the size of wire encoding a uint32 pointer as a Fixed32.
-func sizeFixed32(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeFixed32(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 
-	return tagsize + wire.SizeFixed32()
+	return f.tagsize + wire.SizeFixed32()
 }
 
 // appendFixed32 wire encodes a uint32 pointer as a Fixed32.
-func appendFixed32(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFixed32(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Uint32()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed32(b, v)
 	return b, nil
 }
 
 // consumeFixed32 wire decodes a uint32 pointer as a Fixed32.
-func consumeFixed32(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeFixed32(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.Fixed32Type {
 		return out, errUnknown
 	}
@@ -3305,22 +3305,22 @@
 
 // sizeFixed32NoZero returns the size of wire encoding a uint32 pointer as a Fixed32.
 // The zero value is not encoded.
-func sizeFixed32NoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeFixed32NoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Uint32()
 	if v == 0 {
 		return 0
 	}
-	return tagsize + wire.SizeFixed32()
+	return f.tagsize + wire.SizeFixed32()
 }
 
 // appendFixed32NoZero wire encodes a uint32 pointer as a Fixed32.
 // The zero value is not encoded.
-func appendFixed32NoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFixed32NoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Uint32()
 	if v == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed32(b, v)
 	return b, nil
 }
@@ -3333,21 +3333,21 @@
 
 // sizeFixed32Ptr returns the size of wire encoding a *uint32 pointer as a Fixed32.
 // It panics if the pointer is nil.
-func sizeFixed32Ptr(p pointer, tagsize int, _ marshalOptions) (size int) {
-	return tagsize + wire.SizeFixed32()
+func sizeFixed32Ptr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
+	return f.tagsize + wire.SizeFixed32()
 }
 
 // appendFixed32Ptr wire encodes a *uint32 pointer as a Fixed32.
 // It panics if the pointer is nil.
-func appendFixed32Ptr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFixed32Ptr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.Uint32Ptr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed32(b, v)
 	return b, nil
 }
 
 // consumeFixed32Ptr wire decodes a *uint32 pointer as a Fixed32.
-func consumeFixed32Ptr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeFixed32Ptr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.Fixed32Type {
 		return out, errUnknown
 	}
@@ -3371,24 +3371,24 @@
 }
 
 // sizeFixed32Slice returns the size of wire encoding a []uint32 pointer as a repeated Fixed32.
-func sizeFixed32Slice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeFixed32Slice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Uint32Slice()
-	size = len(s) * (tagsize + wire.SizeFixed32())
+	size = len(s) * (f.tagsize + wire.SizeFixed32())
 	return size
 }
 
 // appendFixed32Slice encodes a []uint32 pointer as a repeated Fixed32.
-func appendFixed32Slice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFixed32Slice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Uint32Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendFixed32(b, v)
 	}
 	return b, nil
 }
 
 // consumeFixed32Slice wire decodes a []uint32 pointer as a repeated Fixed32.
-func consumeFixed32Slice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeFixed32Slice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.Uint32Slice()
 	if wtyp == wire.BytesType {
 		s := *sp
@@ -3427,22 +3427,22 @@
 }
 
 // sizeFixed32PackedSlice returns the size of wire encoding a []uint32 pointer as a packed repeated Fixed32.
-func sizeFixed32PackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeFixed32PackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Uint32Slice()
 	if len(s) == 0 {
 		return 0
 	}
 	n := len(s) * wire.SizeFixed32()
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // appendFixed32PackedSlice encodes a []uint32 pointer as a packed repeated Fixed32.
-func appendFixed32PackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFixed32PackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Uint32Slice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := len(s) * wire.SizeFixed32()
 	b = wire.AppendVarint(b, uint64(n))
 	for _, v := range s {
@@ -3578,21 +3578,21 @@
 }
 
 // sizeFloat returns the size of wire encoding a float32 pointer as a Float.
-func sizeFloat(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeFloat(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 
-	return tagsize + wire.SizeFixed32()
+	return f.tagsize + wire.SizeFixed32()
 }
 
 // appendFloat wire encodes a float32 pointer as a Float.
-func appendFloat(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFloat(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Float32()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed32(b, math.Float32bits(v))
 	return b, nil
 }
 
 // consumeFloat wire decodes a float32 pointer as a Float.
-func consumeFloat(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeFloat(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.Fixed32Type {
 		return out, errUnknown
 	}
@@ -3613,22 +3613,22 @@
 
 // sizeFloatNoZero returns the size of wire encoding a float32 pointer as a Float.
 // The zero value is not encoded.
-func sizeFloatNoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeFloatNoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Float32()
 	if v == 0 && !math.Signbit(float64(v)) {
 		return 0
 	}
-	return tagsize + wire.SizeFixed32()
+	return f.tagsize + wire.SizeFixed32()
 }
 
 // appendFloatNoZero wire encodes a float32 pointer as a Float.
 // The zero value is not encoded.
-func appendFloatNoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFloatNoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Float32()
 	if v == 0 && !math.Signbit(float64(v)) {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed32(b, math.Float32bits(v))
 	return b, nil
 }
@@ -3641,21 +3641,21 @@
 
 // sizeFloatPtr returns the size of wire encoding a *float32 pointer as a Float.
 // It panics if the pointer is nil.
-func sizeFloatPtr(p pointer, tagsize int, _ marshalOptions) (size int) {
-	return tagsize + wire.SizeFixed32()
+func sizeFloatPtr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
+	return f.tagsize + wire.SizeFixed32()
 }
 
 // appendFloatPtr wire encodes a *float32 pointer as a Float.
 // It panics if the pointer is nil.
-func appendFloatPtr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFloatPtr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.Float32Ptr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed32(b, math.Float32bits(v))
 	return b, nil
 }
 
 // consumeFloatPtr wire decodes a *float32 pointer as a Float.
-func consumeFloatPtr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeFloatPtr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.Fixed32Type {
 		return out, errUnknown
 	}
@@ -3679,24 +3679,24 @@
 }
 
 // sizeFloatSlice returns the size of wire encoding a []float32 pointer as a repeated Float.
-func sizeFloatSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeFloatSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Float32Slice()
-	size = len(s) * (tagsize + wire.SizeFixed32())
+	size = len(s) * (f.tagsize + wire.SizeFixed32())
 	return size
 }
 
 // appendFloatSlice encodes a []float32 pointer as a repeated Float.
-func appendFloatSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFloatSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Float32Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendFixed32(b, math.Float32bits(v))
 	}
 	return b, nil
 }
 
 // consumeFloatSlice wire decodes a []float32 pointer as a repeated Float.
-func consumeFloatSlice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeFloatSlice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.Float32Slice()
 	if wtyp == wire.BytesType {
 		s := *sp
@@ -3735,22 +3735,22 @@
 }
 
 // sizeFloatPackedSlice returns the size of wire encoding a []float32 pointer as a packed repeated Float.
-func sizeFloatPackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeFloatPackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Float32Slice()
 	if len(s) == 0 {
 		return 0
 	}
 	n := len(s) * wire.SizeFixed32()
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // appendFloatPackedSlice encodes a []float32 pointer as a packed repeated Float.
-func appendFloatPackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFloatPackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Float32Slice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := len(s) * wire.SizeFixed32()
 	b = wire.AppendVarint(b, uint64(n))
 	for _, v := range s {
@@ -3886,21 +3886,21 @@
 }
 
 // sizeSfixed64 returns the size of wire encoding a int64 pointer as a Sfixed64.
-func sizeSfixed64(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSfixed64(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 
-	return tagsize + wire.SizeFixed64()
+	return f.tagsize + wire.SizeFixed64()
 }
 
 // appendSfixed64 wire encodes a int64 pointer as a Sfixed64.
-func appendSfixed64(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSfixed64(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Int64()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed64(b, uint64(v))
 	return b, nil
 }
 
 // consumeSfixed64 wire decodes a int64 pointer as a Sfixed64.
-func consumeSfixed64(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeSfixed64(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.Fixed64Type {
 		return out, errUnknown
 	}
@@ -3921,22 +3921,22 @@
 
 // sizeSfixed64NoZero returns the size of wire encoding a int64 pointer as a Sfixed64.
 // The zero value is not encoded.
-func sizeSfixed64NoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSfixed64NoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Int64()
 	if v == 0 {
 		return 0
 	}
-	return tagsize + wire.SizeFixed64()
+	return f.tagsize + wire.SizeFixed64()
 }
 
 // appendSfixed64NoZero wire encodes a int64 pointer as a Sfixed64.
 // The zero value is not encoded.
-func appendSfixed64NoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSfixed64NoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Int64()
 	if v == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed64(b, uint64(v))
 	return b, nil
 }
@@ -3949,21 +3949,21 @@
 
 // sizeSfixed64Ptr returns the size of wire encoding a *int64 pointer as a Sfixed64.
 // It panics if the pointer is nil.
-func sizeSfixed64Ptr(p pointer, tagsize int, _ marshalOptions) (size int) {
-	return tagsize + wire.SizeFixed64()
+func sizeSfixed64Ptr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
+	return f.tagsize + wire.SizeFixed64()
 }
 
 // appendSfixed64Ptr wire encodes a *int64 pointer as a Sfixed64.
 // It panics if the pointer is nil.
-func appendSfixed64Ptr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSfixed64Ptr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.Int64Ptr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed64(b, uint64(v))
 	return b, nil
 }
 
 // consumeSfixed64Ptr wire decodes a *int64 pointer as a Sfixed64.
-func consumeSfixed64Ptr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeSfixed64Ptr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.Fixed64Type {
 		return out, errUnknown
 	}
@@ -3987,24 +3987,24 @@
 }
 
 // sizeSfixed64Slice returns the size of wire encoding a []int64 pointer as a repeated Sfixed64.
-func sizeSfixed64Slice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSfixed64Slice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Int64Slice()
-	size = len(s) * (tagsize + wire.SizeFixed64())
+	size = len(s) * (f.tagsize + wire.SizeFixed64())
 	return size
 }
 
 // appendSfixed64Slice encodes a []int64 pointer as a repeated Sfixed64.
-func appendSfixed64Slice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSfixed64Slice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Int64Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendFixed64(b, uint64(v))
 	}
 	return b, nil
 }
 
 // consumeSfixed64Slice wire decodes a []int64 pointer as a repeated Sfixed64.
-func consumeSfixed64Slice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeSfixed64Slice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.Int64Slice()
 	if wtyp == wire.BytesType {
 		s := *sp
@@ -4043,22 +4043,22 @@
 }
 
 // sizeSfixed64PackedSlice returns the size of wire encoding a []int64 pointer as a packed repeated Sfixed64.
-func sizeSfixed64PackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeSfixed64PackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Int64Slice()
 	if len(s) == 0 {
 		return 0
 	}
 	n := len(s) * wire.SizeFixed64()
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // appendSfixed64PackedSlice encodes a []int64 pointer as a packed repeated Sfixed64.
-func appendSfixed64PackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendSfixed64PackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Int64Slice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := len(s) * wire.SizeFixed64()
 	b = wire.AppendVarint(b, uint64(n))
 	for _, v := range s {
@@ -4194,21 +4194,21 @@
 }
 
 // sizeFixed64 returns the size of wire encoding a uint64 pointer as a Fixed64.
-func sizeFixed64(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeFixed64(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 
-	return tagsize + wire.SizeFixed64()
+	return f.tagsize + wire.SizeFixed64()
 }
 
 // appendFixed64 wire encodes a uint64 pointer as a Fixed64.
-func appendFixed64(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFixed64(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Uint64()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed64(b, v)
 	return b, nil
 }
 
 // consumeFixed64 wire decodes a uint64 pointer as a Fixed64.
-func consumeFixed64(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeFixed64(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.Fixed64Type {
 		return out, errUnknown
 	}
@@ -4229,22 +4229,22 @@
 
 // sizeFixed64NoZero returns the size of wire encoding a uint64 pointer as a Fixed64.
 // The zero value is not encoded.
-func sizeFixed64NoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeFixed64NoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Uint64()
 	if v == 0 {
 		return 0
 	}
-	return tagsize + wire.SizeFixed64()
+	return f.tagsize + wire.SizeFixed64()
 }
 
 // appendFixed64NoZero wire encodes a uint64 pointer as a Fixed64.
 // The zero value is not encoded.
-func appendFixed64NoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFixed64NoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Uint64()
 	if v == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed64(b, v)
 	return b, nil
 }
@@ -4257,21 +4257,21 @@
 
 // sizeFixed64Ptr returns the size of wire encoding a *uint64 pointer as a Fixed64.
 // It panics if the pointer is nil.
-func sizeFixed64Ptr(p pointer, tagsize int, _ marshalOptions) (size int) {
-	return tagsize + wire.SizeFixed64()
+func sizeFixed64Ptr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
+	return f.tagsize + wire.SizeFixed64()
 }
 
 // appendFixed64Ptr wire encodes a *uint64 pointer as a Fixed64.
 // It panics if the pointer is nil.
-func appendFixed64Ptr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFixed64Ptr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.Uint64Ptr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed64(b, v)
 	return b, nil
 }
 
 // consumeFixed64Ptr wire decodes a *uint64 pointer as a Fixed64.
-func consumeFixed64Ptr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeFixed64Ptr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.Fixed64Type {
 		return out, errUnknown
 	}
@@ -4295,24 +4295,24 @@
 }
 
 // sizeFixed64Slice returns the size of wire encoding a []uint64 pointer as a repeated Fixed64.
-func sizeFixed64Slice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeFixed64Slice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Uint64Slice()
-	size = len(s) * (tagsize + wire.SizeFixed64())
+	size = len(s) * (f.tagsize + wire.SizeFixed64())
 	return size
 }
 
 // appendFixed64Slice encodes a []uint64 pointer as a repeated Fixed64.
-func appendFixed64Slice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFixed64Slice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Uint64Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendFixed64(b, v)
 	}
 	return b, nil
 }
 
 // consumeFixed64Slice wire decodes a []uint64 pointer as a repeated Fixed64.
-func consumeFixed64Slice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeFixed64Slice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.Uint64Slice()
 	if wtyp == wire.BytesType {
 		s := *sp
@@ -4351,22 +4351,22 @@
 }
 
 // sizeFixed64PackedSlice returns the size of wire encoding a []uint64 pointer as a packed repeated Fixed64.
-func sizeFixed64PackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeFixed64PackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Uint64Slice()
 	if len(s) == 0 {
 		return 0
 	}
 	n := len(s) * wire.SizeFixed64()
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // appendFixed64PackedSlice encodes a []uint64 pointer as a packed repeated Fixed64.
-func appendFixed64PackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendFixed64PackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Uint64Slice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := len(s) * wire.SizeFixed64()
 	b = wire.AppendVarint(b, uint64(n))
 	for _, v := range s {
@@ -4502,21 +4502,21 @@
 }
 
 // sizeDouble returns the size of wire encoding a float64 pointer as a Double.
-func sizeDouble(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeDouble(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 
-	return tagsize + wire.SizeFixed64()
+	return f.tagsize + wire.SizeFixed64()
 }
 
 // appendDouble wire encodes a float64 pointer as a Double.
-func appendDouble(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendDouble(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Float64()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed64(b, math.Float64bits(v))
 	return b, nil
 }
 
 // consumeDouble wire decodes a float64 pointer as a Double.
-func consumeDouble(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeDouble(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.Fixed64Type {
 		return out, errUnknown
 	}
@@ -4537,22 +4537,22 @@
 
 // sizeDoubleNoZero returns the size of wire encoding a float64 pointer as a Double.
 // The zero value is not encoded.
-func sizeDoubleNoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeDoubleNoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Float64()
 	if v == 0 && !math.Signbit(float64(v)) {
 		return 0
 	}
-	return tagsize + wire.SizeFixed64()
+	return f.tagsize + wire.SizeFixed64()
 }
 
 // appendDoubleNoZero wire encodes a float64 pointer as a Double.
 // The zero value is not encoded.
-func appendDoubleNoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendDoubleNoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Float64()
 	if v == 0 && !math.Signbit(float64(v)) {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed64(b, math.Float64bits(v))
 	return b, nil
 }
@@ -4565,21 +4565,21 @@
 
 // sizeDoublePtr returns the size of wire encoding a *float64 pointer as a Double.
 // It panics if the pointer is nil.
-func sizeDoublePtr(p pointer, tagsize int, _ marshalOptions) (size int) {
-	return tagsize + wire.SizeFixed64()
+func sizeDoublePtr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
+	return f.tagsize + wire.SizeFixed64()
 }
 
 // appendDoublePtr wire encodes a *float64 pointer as a Double.
 // It panics if the pointer is nil.
-func appendDoublePtr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendDoublePtr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.Float64Ptr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendFixed64(b, math.Float64bits(v))
 	return b, nil
 }
 
 // consumeDoublePtr wire decodes a *float64 pointer as a Double.
-func consumeDoublePtr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeDoublePtr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.Fixed64Type {
 		return out, errUnknown
 	}
@@ -4603,24 +4603,24 @@
 }
 
 // sizeDoubleSlice returns the size of wire encoding a []float64 pointer as a repeated Double.
-func sizeDoubleSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeDoubleSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Float64Slice()
-	size = len(s) * (tagsize + wire.SizeFixed64())
+	size = len(s) * (f.tagsize + wire.SizeFixed64())
 	return size
 }
 
 // appendDoubleSlice encodes a []float64 pointer as a repeated Double.
-func appendDoubleSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendDoubleSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Float64Slice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendFixed64(b, math.Float64bits(v))
 	}
 	return b, nil
 }
 
 // consumeDoubleSlice wire decodes a []float64 pointer as a repeated Double.
-func consumeDoubleSlice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeDoubleSlice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.Float64Slice()
 	if wtyp == wire.BytesType {
 		s := *sp
@@ -4659,22 +4659,22 @@
 }
 
 // sizeDoublePackedSlice returns the size of wire encoding a []float64 pointer as a packed repeated Double.
-func sizeDoublePackedSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeDoublePackedSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.Float64Slice()
 	if len(s) == 0 {
 		return 0
 	}
 	n := len(s) * wire.SizeFixed64()
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
 // appendDoublePackedSlice encodes a []float64 pointer as a packed repeated Double.
-func appendDoublePackedSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendDoublePackedSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.Float64Slice()
 	if len(s) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := len(s) * wire.SizeFixed64()
 	b = wire.AppendVarint(b, uint64(n))
 	for _, v := range s {
@@ -4810,21 +4810,21 @@
 }
 
 // sizeString returns the size of wire encoding a string pointer as a String.
-func sizeString(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeString(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.String()
-	return tagsize + wire.SizeBytes(len(v))
+	return f.tagsize + wire.SizeBytes(len(v))
 }
 
 // appendString wire encodes a string pointer as a String.
-func appendString(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendString(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.String()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendString(b, v)
 	return b, nil
 }
 
 // consumeString wire decodes a string pointer as a String.
-func consumeString(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeString(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.BytesType {
 		return out, errUnknown
 	}
@@ -4844,9 +4844,9 @@
 }
 
 // appendStringValidateUTF8 wire encodes a string pointer as a String.
-func appendStringValidateUTF8(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendStringValidateUTF8(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.String()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendString(b, v)
 	if !utf8.ValidString(v) {
 		return b, errInvalidUTF8{}
@@ -4855,7 +4855,7 @@
 }
 
 // consumeStringValidateUTF8 wire decodes a string pointer as a String.
-func consumeStringValidateUTF8(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeStringValidateUTF8(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.BytesType {
 		return out, errUnknown
 	}
@@ -4879,22 +4879,22 @@
 
 // sizeStringNoZero returns the size of wire encoding a string pointer as a String.
 // The zero value is not encoded.
-func sizeStringNoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeStringNoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.String()
 	if len(v) == 0 {
 		return 0
 	}
-	return tagsize + wire.SizeBytes(len(v))
+	return f.tagsize + wire.SizeBytes(len(v))
 }
 
 // appendStringNoZero wire encodes a string pointer as a String.
 // The zero value is not encoded.
-func appendStringNoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendStringNoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.String()
 	if len(v) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendString(b, v)
 	return b, nil
 }
@@ -4907,12 +4907,12 @@
 
 // appendStringNoZeroValidateUTF8 wire encodes a string pointer as a String.
 // The zero value is not encoded.
-func appendStringNoZeroValidateUTF8(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendStringNoZeroValidateUTF8(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.String()
 	if len(v) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendString(b, v)
 	if !utf8.ValidString(v) {
 		return b, errInvalidUTF8{}
@@ -4928,22 +4928,22 @@
 
 // sizeStringPtr returns the size of wire encoding a *string pointer as a String.
 // It panics if the pointer is nil.
-func sizeStringPtr(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeStringPtr(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := **p.StringPtr()
-	return tagsize + wire.SizeBytes(len(v))
+	return f.tagsize + wire.SizeBytes(len(v))
 }
 
 // appendStringPtr wire encodes a *string pointer as a String.
 // It panics if the pointer is nil.
-func appendStringPtr(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendStringPtr(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := **p.StringPtr()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendString(b, v)
 	return b, nil
 }
 
 // consumeStringPtr wire decodes a *string pointer as a String.
-func consumeStringPtr(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeStringPtr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.BytesType {
 		return out, errUnknown
 	}
@@ -4967,26 +4967,26 @@
 }
 
 // sizeStringSlice returns the size of wire encoding a []string pointer as a repeated String.
-func sizeStringSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeStringSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.StringSlice()
 	for _, v := range s {
-		size += tagsize + wire.SizeBytes(len(v))
+		size += f.tagsize + wire.SizeBytes(len(v))
 	}
 	return size
 }
 
 // appendStringSlice encodes a []string pointer as a repeated String.
-func appendStringSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendStringSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.StringSlice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendString(b, v)
 	}
 	return b, nil
 }
 
 // consumeStringSlice wire decodes a []string pointer as a repeated String.
-func consumeStringSlice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeStringSlice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.StringSlice()
 	if wtyp != wire.BytesType {
 		return out, errUnknown
@@ -5007,10 +5007,10 @@
 }
 
 // appendStringSliceValidateUTF8 encodes a []string pointer as a repeated String.
-func appendStringSliceValidateUTF8(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendStringSliceValidateUTF8(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.StringSlice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendString(b, v)
 		if !utf8.ValidString(v) {
 			return b, errInvalidUTF8{}
@@ -5020,7 +5020,7 @@
 }
 
 // consumeStringSliceValidateUTF8 wire decodes a []string pointer as a repeated String.
-func consumeStringSliceValidateUTF8(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeStringSliceValidateUTF8(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.StringSlice()
 	if wtyp != wire.BytesType {
 		return out, errUnknown
@@ -5149,21 +5149,21 @@
 }
 
 // sizeBytes returns the size of wire encoding a []byte pointer as a Bytes.
-func sizeBytes(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeBytes(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Bytes()
-	return tagsize + wire.SizeBytes(len(v))
+	return f.tagsize + wire.SizeBytes(len(v))
 }
 
 // appendBytes wire encodes a []byte pointer as a Bytes.
-func appendBytes(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendBytes(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Bytes()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendBytes(b, v)
 	return b, nil
 }
 
 // consumeBytes wire decodes a []byte pointer as a Bytes.
-func consumeBytes(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeBytes(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.BytesType {
 		return out, errUnknown
 	}
@@ -5183,9 +5183,9 @@
 }
 
 // appendBytesValidateUTF8 wire encodes a []byte pointer as a Bytes.
-func appendBytesValidateUTF8(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendBytesValidateUTF8(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Bytes()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendBytes(b, v)
 	if !utf8.Valid(v) {
 		return b, errInvalidUTF8{}
@@ -5194,7 +5194,7 @@
 }
 
 // consumeBytesValidateUTF8 wire decodes a []byte pointer as a Bytes.
-func consumeBytesValidateUTF8(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeBytesValidateUTF8(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.BytesType {
 		return out, errUnknown
 	}
@@ -5218,29 +5218,29 @@
 
 // sizeBytesNoZero returns the size of wire encoding a []byte pointer as a Bytes.
 // The zero value is not encoded.
-func sizeBytesNoZero(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeBytesNoZero(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := *p.Bytes()
 	if len(v) == 0 {
 		return 0
 	}
-	return tagsize + wire.SizeBytes(len(v))
+	return f.tagsize + wire.SizeBytes(len(v))
 }
 
 // appendBytesNoZero wire encodes a []byte pointer as a Bytes.
 // The zero value is not encoded.
-func appendBytesNoZero(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendBytesNoZero(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Bytes()
 	if len(v) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendBytes(b, v)
 	return b, nil
 }
 
 // consumeBytesNoZero wire decodes a []byte pointer as a Bytes.
 // The zero value is not decoded.
-func consumeBytesNoZero(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeBytesNoZero(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.BytesType {
 		return out, errUnknown
 	}
@@ -5261,12 +5261,12 @@
 
 // appendBytesNoZeroValidateUTF8 wire encodes a []byte pointer as a Bytes.
 // The zero value is not encoded.
-func appendBytesNoZeroValidateUTF8(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendBytesNoZeroValidateUTF8(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	v := *p.Bytes()
 	if len(v) == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendBytes(b, v)
 	if !utf8.Valid(v) {
 		return b, errInvalidUTF8{}
@@ -5275,7 +5275,7 @@
 }
 
 // consumeBytesNoZeroValidateUTF8 wire decodes a []byte pointer as a Bytes.
-func consumeBytesNoZeroValidateUTF8(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeBytesNoZeroValidateUTF8(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.BytesType {
 		return out, errUnknown
 	}
@@ -5298,26 +5298,26 @@
 }
 
 // sizeBytesSlice returns the size of wire encoding a [][]byte pointer as a repeated Bytes.
-func sizeBytesSlice(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeBytesSlice(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	s := *p.BytesSlice()
 	for _, v := range s {
-		size += tagsize + wire.SizeBytes(len(v))
+		size += f.tagsize + wire.SizeBytes(len(v))
 	}
 	return size
 }
 
 // appendBytesSlice encodes a [][]byte pointer as a repeated Bytes.
-func appendBytesSlice(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendBytesSlice(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.BytesSlice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendBytes(b, v)
 	}
 	return b, nil
 }
 
 // consumeBytesSlice wire decodes a [][]byte pointer as a repeated Bytes.
-func consumeBytesSlice(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeBytesSlice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.BytesSlice()
 	if wtyp != wire.BytesType {
 		return out, errUnknown
@@ -5338,10 +5338,10 @@
 }
 
 // appendBytesSliceValidateUTF8 encodes a [][]byte pointer as a repeated Bytes.
-func appendBytesSliceValidateUTF8(b []byte, p pointer, wiretag uint64, _ marshalOptions) ([]byte, error) {
+func appendBytesSliceValidateUTF8(b []byte, p pointer, f *coderFieldInfo, _ marshalOptions) ([]byte, error) {
 	s := *p.BytesSlice()
 	for _, v := range s {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendBytes(b, v)
 		if !utf8.Valid(v) {
 			return b, errInvalidUTF8{}
@@ -5351,7 +5351,7 @@
 }
 
 // consumeBytesSliceValidateUTF8 wire decodes a [][]byte pointer as a repeated Bytes.
-func consumeBytesSliceValidateUTF8(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeBytesSliceValidateUTF8(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	sp := p.BytesSlice()
 	if wtyp != wire.BytesType {
 		return out, errUnknown
diff --git a/internal/impl/codec_map.go b/internal/impl/codec_map.go
index c8c0925..b319583 100644
--- a/internal/impl/codec_map.go
+++ b/internal/impl/codec_map.go
@@ -14,18 +14,17 @@
 )
 
 type mapInfo struct {
-	goType         reflect.Type
-	keyWiretag     uint64
-	valWiretag     uint64
-	keyFuncs       valueCoderFuncs
-	valFuncs       valueCoderFuncs
-	keyZero        pref.Value
-	keyKind        pref.Kind
-	valMessageInfo *MessageInfo
-	conv           *mapConverter
+	goType     reflect.Type
+	keyWiretag uint64
+	valWiretag uint64
+	keyFuncs   valueCoderFuncs
+	valFuncs   valueCoderFuncs
+	keyZero    pref.Value
+	keyKind    pref.Kind
+	conv       *mapConverter
 }
 
-func encoderFuncsForMap(fd pref.FieldDescriptor, ft reflect.Type) (funcs pointerCoderFuncs) {
+func encoderFuncsForMap(fd pref.FieldDescriptor, ft reflect.Type) (valueMessage *MessageInfo, funcs pointerCoderFuncs) {
 	// TODO: Consider generating specialized map coders.
 	keyField := fd.MapKey()
 	valField := fd.MapValue()
@@ -46,34 +45,34 @@
 		conv:       conv,
 	}
 	if valField.Kind() == pref.MessageKind {
-		mapi.valMessageInfo = getMessageInfo(ft.Elem())
+		valueMessage = getMessageInfo(ft.Elem())
 	}
 
 	funcs = pointerCoderFuncs{
-		size: func(p pointer, tagsize int, opts marshalOptions) int {
-			return sizeMap(p.AsValueOf(ft).Elem(), tagsize, mapi, opts)
+		size: func(p pointer, f *coderFieldInfo, opts marshalOptions) int {
+			return sizeMap(p.AsValueOf(ft).Elem(), mapi, f, opts)
 		},
-		marshal: func(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
-			return appendMap(b, p.AsValueOf(ft).Elem(), wiretag, mapi, opts)
+		marshal: func(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
+			return appendMap(b, p.AsValueOf(ft).Elem(), mapi, f, opts)
 		},
-		unmarshal: func(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (unmarshalOutput, error) {
+		unmarshal: func(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (unmarshalOutput, error) {
 			mp := p.AsValueOf(ft)
 			if mp.Elem().IsNil() {
 				mp.Elem().Set(reflect.MakeMap(mapi.goType))
 			}
-			if mapi.valMessageInfo == nil {
-				return consumeMap(b, mp.Elem(), wtyp, mapi, opts)
+			if f.mi == nil {
+				return consumeMap(b, mp.Elem(), wtyp, mapi, f, opts)
 			} else {
-				return consumeMapOfMessage(b, mp.Elem(), wtyp, mapi, opts)
+				return consumeMapOfMessage(b, mp.Elem(), wtyp, mapi, f, opts)
 			}
 		},
 	}
 	if valFuncs.isInit != nil {
-		funcs.isInit = func(p pointer) error {
-			return isInitMap(p.AsValueOf(ft).Elem(), mapi)
+		funcs.isInit = func(p pointer, f *coderFieldInfo) error {
+			return isInitMap(p.AsValueOf(ft).Elem(), mapi, f)
 		}
 	}
-	return funcs
+	return valueMessage, funcs
 }
 
 const (
@@ -81,7 +80,7 @@
 	mapValTagSize = 1 // field 2, tag size 2.
 )
 
-func sizeMap(mapv reflect.Value, tagsize int, mapi *mapInfo, opts marshalOptions) int {
+func sizeMap(mapv reflect.Value, mapi *mapInfo, f *coderFieldInfo, opts marshalOptions) int {
 	if mapv.Len() == 0 {
 		return 0
 	}
@@ -92,19 +91,19 @@
 		keySize := mapi.keyFuncs.size(key.Value(), mapKeyTagSize, opts)
 		var valSize int
 		value := mapi.conv.valConv.PBValueOf(iter.Value())
-		if mapi.valMessageInfo == nil {
+		if f.mi == nil {
 			valSize = mapi.valFuncs.size(value, mapValTagSize, opts)
 		} else {
 			p := pointerOfValue(iter.Value())
 			valSize += mapValTagSize
-			valSize += wire.SizeBytes(mapi.valMessageInfo.sizePointer(p, opts))
+			valSize += wire.SizeBytes(f.mi.sizePointer(p, opts))
 		}
-		n += tagsize + wire.SizeBytes(keySize+valSize)
+		n += f.tagsize + wire.SizeBytes(keySize+valSize)
 	}
 	return n
 }
 
-func consumeMap(b []byte, mapv reflect.Value, wtyp wire.Type, mapi *mapInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeMap(b []byte, mapv reflect.Value, wtyp wire.Type, mapi *mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.BytesType {
 		return out, errUnknown
 	}
@@ -161,7 +160,7 @@
 	return out, nil
 }
 
-func consumeMapOfMessage(b []byte, mapv reflect.Value, wtyp wire.Type, mapi *mapInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeMapOfMessage(b []byte, mapv reflect.Value, wtyp wire.Type, mapi *mapInfo, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.BytesType {
 		return out, errUnknown
 	}
@@ -171,7 +170,7 @@
 	}
 	var (
 		key = mapi.keyZero
-		val = reflect.New(mapi.valMessageInfo.GoReflectType.Elem())
+		val = reflect.New(f.mi.GoReflectType.Elem())
 	)
 	for len(b) > 0 {
 		num, wtyp, n := wire.ConsumeTag(b)
@@ -203,7 +202,7 @@
 				return out, wire.ParseError(n)
 			}
 			var o unmarshalOutput
-			o, err = mapi.valMessageInfo.unmarshalPointer(v, pointerOfValue(val), 0, opts)
+			o, err = f.mi.unmarshalPointer(v, pointerOfValue(val), 0, opts)
 			if o.initialized {
 				// Consider this map item initialized so long as we see
 				// an initialized value.
@@ -225,8 +224,8 @@
 	return out, nil
 }
 
-func appendMapItem(b []byte, keyrv, valrv reflect.Value, mapi *mapInfo, opts marshalOptions) ([]byte, error) {
-	if mapi.valMessageInfo == nil {
+func appendMapItem(b []byte, keyrv, valrv reflect.Value, mapi *mapInfo, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
+	if f.mi == nil {
 		key := mapi.conv.keyConv.PBValueOf(keyrv).MapKey()
 		val := mapi.conv.valConv.PBValueOf(valrv)
 		size := 0
@@ -241,7 +240,7 @@
 	} else {
 		key := mapi.conv.keyConv.PBValueOf(keyrv).MapKey()
 		val := pointerOfValue(valrv)
-		valSize := mapi.valMessageInfo.sizePointer(val, opts)
+		valSize := f.mi.sizePointer(val, opts)
 		size := 0
 		size += mapi.keyFuncs.size(key.Value(), mapKeyTagSize, opts)
 		size += mapValTagSize + wire.SizeBytes(valSize)
@@ -252,22 +251,22 @@
 		}
 		b = wire.AppendVarint(b, mapi.valWiretag)
 		b = wire.AppendVarint(b, uint64(valSize))
-		return mapi.valMessageInfo.marshalAppendPointer(b, val, opts)
+		return f.mi.marshalAppendPointer(b, val, opts)
 	}
 }
 
-func appendMap(b []byte, mapv reflect.Value, wiretag uint64, mapi *mapInfo, opts marshalOptions) ([]byte, error) {
+func appendMap(b []byte, mapv reflect.Value, mapi *mapInfo, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
 	if mapv.Len() == 0 {
 		return b, nil
 	}
 	if opts.Deterministic() {
-		return appendMapDeterministic(b, mapv, wiretag, mapi, opts)
+		return appendMapDeterministic(b, mapv, mapi, f, opts)
 	}
 	iter := mapRange(mapv)
 	for iter.Next() {
 		var err error
-		b = wire.AppendVarint(b, wiretag)
-		b, err = appendMapItem(b, iter.Key(), iter.Value(), mapi, opts)
+		b = wire.AppendVarint(b, f.wiretag)
+		b, err = appendMapItem(b, iter.Key(), iter.Value(), mapi, f, opts)
 		if err != nil {
 			return b, err
 		}
@@ -275,7 +274,7 @@
 	return b, nil
 }
 
-func appendMapDeterministic(b []byte, mapv reflect.Value, wiretag uint64, mapi *mapInfo, opts marshalOptions) ([]byte, error) {
+func appendMapDeterministic(b []byte, mapv reflect.Value, mapi *mapInfo, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
 	keys := mapv.MapKeys()
 	sort.Slice(keys, func(i, j int) bool {
 		switch keys[i].Kind() {
@@ -295,8 +294,8 @@
 	})
 	for _, key := range keys {
 		var err error
-		b = wire.AppendVarint(b, wiretag)
-		b, err = appendMapItem(b, key, mapv.MapIndex(key), mapi, opts)
+		b = wire.AppendVarint(b, f.wiretag)
+		b, err = appendMapItem(b, key, mapv.MapIndex(key), mapi, f, opts)
 		if err != nil {
 			return b, err
 		}
@@ -304,8 +303,8 @@
 	return b, nil
 }
 
-func isInitMap(mapv reflect.Value, mapi *mapInfo) error {
-	if mi := mapi.valMessageInfo; mi != nil {
+func isInitMap(mapv reflect.Value, mapi *mapInfo, f *coderFieldInfo) error {
+	if mi := f.mi; mi != nil {
 		mi.init()
 		if !mi.needsInitCheck {
 			return nil
diff --git a/internal/impl/codec_message.go b/internal/impl/codec_message.go
index 24a87a0..cd4ebe6 100644
--- a/internal/impl/codec_message.go
+++ b/internal/impl/codec_message.go
@@ -35,13 +35,15 @@
 
 type coderFieldInfo struct {
 	funcs      pointerCoderFuncs // fast-path per-field functions
-	validation validationInfo    // information used by message validation
-	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
+	mi         *MessageInfo      // field's message
+	ft         reflect.Type
+	validation validationInfo   // information used by message validation
+	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) makeCoderMethods(t reflect.Type, si structInfo) {
@@ -67,6 +69,7 @@
 		}
 		var fieldOffset offset
 		var funcs pointerCoderFuncs
+		var childMessage *MessageInfo
 		switch {
 		case fd.ContainingOneof() != nil:
 			fieldOffset = offsetOf(fs, mi.Exporter)
@@ -75,14 +78,16 @@
 			funcs = makeWeakMessageFieldCoder(fd)
 		default:
 			fieldOffset = offsetOf(fs, mi.Exporter)
-			funcs = fieldCoder(fd, ft)
+			childMessage, funcs = fieldCoder(fd, ft)
 		}
 		cf := &coderFieldInfo{
 			num:        fd.Number(),
 			offset:     fieldOffset,
 			wiretag:    wiretag,
+			ft:         ft,
 			tagsize:    wire.SizeVarint(wiretag),
 			funcs:      funcs,
+			mi:         childMessage,
 			validation: newFieldValidationInfo(mi, si, fd, ft),
 			isPointer: (fd.Cardinality() == pref.Repeated ||
 				fd.Kind() == pref.MessageKind ||
diff --git a/internal/impl/codec_reflect.go b/internal/impl/codec_reflect.go
index 75420d9..ae872aa 100644
--- a/internal/impl/codec_reflect.go
+++ b/internal/impl/codec_reflect.go
@@ -12,19 +12,19 @@
 	"google.golang.org/protobuf/internal/encoding/wire"
 )
 
-func sizeEnum(p pointer, tagsize int, _ marshalOptions) (size int) {
+func sizeEnum(p pointer, f *coderFieldInfo, _ marshalOptions) (size int) {
 	v := p.v.Elem().Int()
-	return tagsize + wire.SizeVarint(uint64(v))
+	return f.tagsize + wire.SizeVarint(uint64(v))
 }
 
-func appendEnum(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
+func appendEnum(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
 	v := p.v.Elem().Int()
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	b = wire.AppendVarint(b, uint64(v))
 	return b, nil
 }
 
-func consumeEnum(b []byte, p pointer, wtyp wire.Type, _ unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeEnum(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, _ unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
@@ -43,18 +43,18 @@
 	unmarshal: consumeEnum,
 }
 
-func sizeEnumNoZero(p pointer, tagsize int, opts marshalOptions) (size int) {
+func sizeEnumNoZero(p pointer, f *coderFieldInfo, opts marshalOptions) (size int) {
 	if p.v.Elem().Int() == 0 {
 		return 0
 	}
-	return sizeEnum(p, tagsize, opts)
+	return sizeEnum(p, f, opts)
 }
 
-func appendEnumNoZero(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
+func appendEnumNoZero(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
 	if p.v.Elem().Int() == 0 {
 		return b, nil
 	}
-	return appendEnum(b, p, wiretag, opts)
+	return appendEnum(b, p, f, opts)
 }
 
 var coderEnumNoZero = pointerCoderFuncs{
@@ -63,22 +63,22 @@
 	unmarshal: consumeEnum,
 }
 
-func sizeEnumPtr(p pointer, tagsize int, opts marshalOptions) (size int) {
-	return sizeEnum(pointer{p.v.Elem()}, tagsize, opts)
+func sizeEnumPtr(p pointer, f *coderFieldInfo, opts marshalOptions) (size int) {
+	return sizeEnum(pointer{p.v.Elem()}, f, opts)
 }
 
-func appendEnumPtr(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
-	return appendEnum(b, pointer{p.v.Elem()}, wiretag, opts)
+func appendEnumPtr(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
+	return appendEnum(b, pointer{p.v.Elem()}, f, opts)
 }
 
-func consumeEnumPtr(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeEnumPtr(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
 	if wtyp != wire.VarintType {
 		return out, errUnknown
 	}
 	if p.v.Elem().IsNil() {
 		p.v.Elem().Set(reflect.New(p.v.Elem().Type().Elem()))
 	}
-	return consumeEnum(b, pointer{p.v.Elem()}, wtyp, opts)
+	return consumeEnum(b, pointer{p.v.Elem()}, wtyp, f, opts)
 }
 
 var coderEnumPtr = pointerCoderFuncs{
@@ -87,24 +87,24 @@
 	unmarshal: consumeEnumPtr,
 }
 
-func sizeEnumSlice(p pointer, tagsize int, opts marshalOptions) (size int) {
+func sizeEnumSlice(p pointer, f *coderFieldInfo, opts marshalOptions) (size int) {
 	s := p.v.Elem()
 	for i, llen := 0, s.Len(); i < llen; i++ {
-		size += wire.SizeVarint(uint64(s.Index(i).Int())) + tagsize
+		size += wire.SizeVarint(uint64(s.Index(i).Int())) + f.tagsize
 	}
 	return size
 }
 
-func appendEnumSlice(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
+func appendEnumSlice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
 	s := p.v.Elem()
 	for i, llen := 0, s.Len(); i < llen; i++ {
-		b = wire.AppendVarint(b, wiretag)
+		b = wire.AppendVarint(b, f.wiretag)
 		b = wire.AppendVarint(b, uint64(s.Index(i).Int()))
 	}
 	return b, nil
 }
 
-func consumeEnumSlice(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (out unmarshalOutput, err error) {
+func consumeEnumSlice(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (out unmarshalOutput, err error) {
 	s := p.v.Elem()
 	if wtyp == wire.BytesType {
 		b, n := wire.ConsumeBytes(b)
@@ -144,7 +144,7 @@
 	unmarshal: consumeEnumSlice,
 }
 
-func sizeEnumPackedSlice(p pointer, tagsize int, opts marshalOptions) (size int) {
+func sizeEnumPackedSlice(p pointer, f *coderFieldInfo, opts marshalOptions) (size int) {
 	s := p.v.Elem()
 	llen := s.Len()
 	if llen == 0 {
@@ -154,16 +154,16 @@
 	for i := 0; i < llen; i++ {
 		n += wire.SizeVarint(uint64(s.Index(i).Int()))
 	}
-	return tagsize + wire.SizeBytes(n)
+	return f.tagsize + wire.SizeBytes(n)
 }
 
-func appendEnumPackedSlice(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error) {
+func appendEnumPackedSlice(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error) {
 	s := p.v.Elem()
 	llen := s.Len()
 	if llen == 0 {
 		return b, nil
 	}
-	b = wire.AppendVarint(b, wiretag)
+	b = wire.AppendVarint(b, f.wiretag)
 	n := 0
 	for i := 0; i < llen; i++ {
 		n += wire.SizeVarint(uint64(s.Index(i).Int()))
diff --git a/internal/impl/codec_tables.go b/internal/impl/codec_tables.go
index 38c4e7e..2c62bda 100644
--- a/internal/impl/codec_tables.go
+++ b/internal/impl/codec_tables.go
@@ -15,10 +15,11 @@
 
 // pointerCoderFuncs is a set of pointer encoding functions.
 type pointerCoderFuncs struct {
-	size      func(p pointer, tagsize int, opts marshalOptions) int
-	marshal   func(b []byte, p pointer, wiretag uint64, opts marshalOptions) ([]byte, error)
-	unmarshal func(b []byte, p pointer, wtyp wire.Type, opts unmarshalOptions) (unmarshalOutput, error)
-	isInit    func(p pointer) error
+	mi        *MessageInfo
+	size      func(p pointer, f *coderFieldInfo, opts marshalOptions) int
+	marshal   func(b []byte, p pointer, f *coderFieldInfo, opts marshalOptions) ([]byte, error)
+	unmarshal func(b []byte, p pointer, wtyp wire.Type, f *coderFieldInfo, opts unmarshalOptions) (unmarshalOutput, error)
+	isInit    func(p pointer, f *coderFieldInfo) error
 }
 
 // valueCoderFuncs is a set of protoreflect.Value encoding functions.
@@ -31,7 +32,7 @@
 
 // fieldCoder returns pointer functions for a field, used for operating on
 // struct fields.
-func fieldCoder(fd pref.FieldDescriptor, ft reflect.Type) pointerCoderFuncs {
+func fieldCoder(fd pref.FieldDescriptor, ft reflect.Type) (*MessageInfo, pointerCoderFuncs) {
 	switch {
 	case fd.IsMap():
 		return encoderFuncsForMap(fd, ft)
@@ -44,84 +45,84 @@
 		switch fd.Kind() {
 		case pref.BoolKind:
 			if ft.Kind() == reflect.Bool {
-				return coderBoolSlice
+				return nil, coderBoolSlice
 			}
 		case pref.EnumKind:
 			if ft.Kind() == reflect.Int32 {
-				return coderEnumSlice
+				return nil, coderEnumSlice
 			}
 		case pref.Int32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderInt32Slice
+				return nil, coderInt32Slice
 			}
 		case pref.Sint32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderSint32Slice
+				return nil, coderSint32Slice
 			}
 		case pref.Uint32Kind:
 			if ft.Kind() == reflect.Uint32 {
-				return coderUint32Slice
+				return nil, coderUint32Slice
 			}
 		case pref.Int64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderInt64Slice
+				return nil, coderInt64Slice
 			}
 		case pref.Sint64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderSint64Slice
+				return nil, coderSint64Slice
 			}
 		case pref.Uint64Kind:
 			if ft.Kind() == reflect.Uint64 {
-				return coderUint64Slice
+				return nil, coderUint64Slice
 			}
 		case pref.Sfixed32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderSfixed32Slice
+				return nil, coderSfixed32Slice
 			}
 		case pref.Fixed32Kind:
 			if ft.Kind() == reflect.Uint32 {
-				return coderFixed32Slice
+				return nil, coderFixed32Slice
 			}
 		case pref.FloatKind:
 			if ft.Kind() == reflect.Float32 {
-				return coderFloatSlice
+				return nil, coderFloatSlice
 			}
 		case pref.Sfixed64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderSfixed64Slice
+				return nil, coderSfixed64Slice
 			}
 		case pref.Fixed64Kind:
 			if ft.Kind() == reflect.Uint64 {
-				return coderFixed64Slice
+				return nil, coderFixed64Slice
 			}
 		case pref.DoubleKind:
 			if ft.Kind() == reflect.Float64 {
-				return coderDoubleSlice
+				return nil, coderDoubleSlice
 			}
 		case pref.StringKind:
 			if ft.Kind() == reflect.String && strs.EnforceUTF8(fd) {
-				return coderStringSliceValidateUTF8
+				return nil, coderStringSliceValidateUTF8
 			}
 			if ft.Kind() == reflect.String {
-				return coderStringSlice
+				return nil, coderStringSlice
 			}
 			if ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8 && strs.EnforceUTF8(fd) {
-				return coderBytesSliceValidateUTF8
+				return nil, coderBytesSliceValidateUTF8
 			}
 			if ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8 {
-				return coderBytesSlice
+				return nil, coderBytesSlice
 			}
 		case pref.BytesKind:
 			if ft.Kind() == reflect.String {
-				return coderStringSlice
+				return nil, coderStringSlice
 			}
 			if ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8 {
-				return coderBytesSlice
+				return nil, coderBytesSlice
 			}
 		case pref.MessageKind:
-			return makeMessageSliceFieldCoder(fd, ft)
+			return getMessageInfo(ft), makeMessageSliceFieldCoder(fd, ft)
 		case pref.GroupKind:
-			return makeGroupSliceFieldCoder(fd, ft)
+			return getMessageInfo(ft), makeGroupSliceFieldCoder(fd, ft)
 		}
 	case fd.Cardinality() == pref.Repeated && fd.IsPacked():
 		// Packed repeated fields.
@@ -135,144 +136,144 @@
 		switch fd.Kind() {
 		case pref.BoolKind:
 			if ft.Kind() == reflect.Bool {
-				return coderBoolPackedSlice
+				return nil, coderBoolPackedSlice
 			}
 		case pref.EnumKind:
 			if ft.Kind() == reflect.Int32 {
-				return coderEnumPackedSlice
+				return nil, coderEnumPackedSlice
 			}
 		case pref.Int32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderInt32PackedSlice
+				return nil, coderInt32PackedSlice
 			}
 		case pref.Sint32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderSint32PackedSlice
+				return nil, coderSint32PackedSlice
 			}
 		case pref.Uint32Kind:
 			if ft.Kind() == reflect.Uint32 {
-				return coderUint32PackedSlice
+				return nil, coderUint32PackedSlice
 			}
 		case pref.Int64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderInt64PackedSlice
+				return nil, coderInt64PackedSlice
 			}
 		case pref.Sint64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderSint64PackedSlice
+				return nil, coderSint64PackedSlice
 			}
 		case pref.Uint64Kind:
 			if ft.Kind() == reflect.Uint64 {
-				return coderUint64PackedSlice
+				return nil, coderUint64PackedSlice
 			}
 		case pref.Sfixed32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderSfixed32PackedSlice
+				return nil, coderSfixed32PackedSlice
 			}
 		case pref.Fixed32Kind:
 			if ft.Kind() == reflect.Uint32 {
-				return coderFixed32PackedSlice
+				return nil, coderFixed32PackedSlice
 			}
 		case pref.FloatKind:
 			if ft.Kind() == reflect.Float32 {
-				return coderFloatPackedSlice
+				return nil, coderFloatPackedSlice
 			}
 		case pref.Sfixed64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderSfixed64PackedSlice
+				return nil, coderSfixed64PackedSlice
 			}
 		case pref.Fixed64Kind:
 			if ft.Kind() == reflect.Uint64 {
-				return coderFixed64PackedSlice
+				return nil, coderFixed64PackedSlice
 			}
 		case pref.DoubleKind:
 			if ft.Kind() == reflect.Float64 {
-				return coderDoublePackedSlice
+				return nil, coderDoublePackedSlice
 			}
 		}
 	case fd.Kind() == pref.MessageKind:
-		return makeMessageFieldCoder(fd, ft)
+		return getMessageInfo(ft), makeMessageFieldCoder(fd, ft)
 	case fd.Kind() == pref.GroupKind:
-		return makeGroupFieldCoder(fd, ft)
+		return getMessageInfo(ft), makeGroupFieldCoder(fd, ft)
 	case fd.Syntax() == pref.Proto3 && fd.ContainingOneof() == nil:
 		// Populated oneof fields always encode even if set to the zero value,
 		// which normally are not encoded in proto3.
 		switch fd.Kind() {
 		case pref.BoolKind:
 			if ft.Kind() == reflect.Bool {
-				return coderBoolNoZero
+				return nil, coderBoolNoZero
 			}
 		case pref.EnumKind:
 			if ft.Kind() == reflect.Int32 {
-				return coderEnumNoZero
+				return nil, coderEnumNoZero
 			}
 		case pref.Int32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderInt32NoZero
+				return nil, coderInt32NoZero
 			}
 		case pref.Sint32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderSint32NoZero
+				return nil, coderSint32NoZero
 			}
 		case pref.Uint32Kind:
 			if ft.Kind() == reflect.Uint32 {
-				return coderUint32NoZero
+				return nil, coderUint32NoZero
 			}
 		case pref.Int64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderInt64NoZero
+				return nil, coderInt64NoZero
 			}
 		case pref.Sint64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderSint64NoZero
+				return nil, coderSint64NoZero
 			}
 		case pref.Uint64Kind:
 			if ft.Kind() == reflect.Uint64 {
-				return coderUint64NoZero
+				return nil, coderUint64NoZero
 			}
 		case pref.Sfixed32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderSfixed32NoZero
+				return nil, coderSfixed32NoZero
 			}
 		case pref.Fixed32Kind:
 			if ft.Kind() == reflect.Uint32 {
-				return coderFixed32NoZero
+				return nil, coderFixed32NoZero
 			}
 		case pref.FloatKind:
 			if ft.Kind() == reflect.Float32 {
-				return coderFloatNoZero
+				return nil, coderFloatNoZero
 			}
 		case pref.Sfixed64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderSfixed64NoZero
+				return nil, coderSfixed64NoZero
 			}
 		case pref.Fixed64Kind:
 			if ft.Kind() == reflect.Uint64 {
-				return coderFixed64NoZero
+				return nil, coderFixed64NoZero
 			}
 		case pref.DoubleKind:
 			if ft.Kind() == reflect.Float64 {
-				return coderDoubleNoZero
+				return nil, coderDoubleNoZero
 			}
 		case pref.StringKind:
 			if ft.Kind() == reflect.String && strs.EnforceUTF8(fd) {
-				return coderStringNoZeroValidateUTF8
+				return nil, coderStringNoZeroValidateUTF8
 			}
 			if ft.Kind() == reflect.String {
-				return coderStringNoZero
+				return nil, coderStringNoZero
 			}
 			if ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8 && strs.EnforceUTF8(fd) {
-				return coderBytesNoZeroValidateUTF8
+				return nil, coderBytesNoZeroValidateUTF8
 			}
 			if ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8 {
-				return coderBytesNoZero
+				return nil, coderBytesNoZero
 			}
 		case pref.BytesKind:
 			if ft.Kind() == reflect.String {
-				return coderStringNoZero
+				return nil, coderStringNoZero
 			}
 			if ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8 {
-				return coderBytesNoZero
+				return nil, coderBytesNoZero
 			}
 		}
 	case ft.Kind() == reflect.Ptr:
@@ -280,146 +281,146 @@
 		switch fd.Kind() {
 		case pref.BoolKind:
 			if ft.Kind() == reflect.Bool {
-				return coderBoolPtr
+				return nil, coderBoolPtr
 			}
 		case pref.EnumKind:
 			if ft.Kind() == reflect.Int32 {
-				return coderEnumPtr
+				return nil, coderEnumPtr
 			}
 		case pref.Int32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderInt32Ptr
+				return nil, coderInt32Ptr
 			}
 		case pref.Sint32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderSint32Ptr
+				return nil, coderSint32Ptr
 			}
 		case pref.Uint32Kind:
 			if ft.Kind() == reflect.Uint32 {
-				return coderUint32Ptr
+				return nil, coderUint32Ptr
 			}
 		case pref.Int64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderInt64Ptr
+				return nil, coderInt64Ptr
 			}
 		case pref.Sint64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderSint64Ptr
+				return nil, coderSint64Ptr
 			}
 		case pref.Uint64Kind:
 			if ft.Kind() == reflect.Uint64 {
-				return coderUint64Ptr
+				return nil, coderUint64Ptr
 			}
 		case pref.Sfixed32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderSfixed32Ptr
+				return nil, coderSfixed32Ptr
 			}
 		case pref.Fixed32Kind:
 			if ft.Kind() == reflect.Uint32 {
-				return coderFixed32Ptr
+				return nil, coderFixed32Ptr
 			}
 		case pref.FloatKind:
 			if ft.Kind() == reflect.Float32 {
-				return coderFloatPtr
+				return nil, coderFloatPtr
 			}
 		case pref.Sfixed64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderSfixed64Ptr
+				return nil, coderSfixed64Ptr
 			}
 		case pref.Fixed64Kind:
 			if ft.Kind() == reflect.Uint64 {
-				return coderFixed64Ptr
+				return nil, coderFixed64Ptr
 			}
 		case pref.DoubleKind:
 			if ft.Kind() == reflect.Float64 {
-				return coderDoublePtr
+				return nil, coderDoublePtr
 			}
 		case pref.StringKind:
 			if ft.Kind() == reflect.String {
-				return coderStringPtr
+				return nil, coderStringPtr
 			}
 		case pref.BytesKind:
 			if ft.Kind() == reflect.String {
-				return coderStringPtr
+				return nil, coderStringPtr
 			}
 		}
 	default:
 		switch fd.Kind() {
 		case pref.BoolKind:
 			if ft.Kind() == reflect.Bool {
-				return coderBool
+				return nil, coderBool
 			}
 		case pref.EnumKind:
 			if ft.Kind() == reflect.Int32 {
-				return coderEnum
+				return nil, coderEnum
 			}
 		case pref.Int32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderInt32
+				return nil, coderInt32
 			}
 		case pref.Sint32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderSint32
+				return nil, coderSint32
 			}
 		case pref.Uint32Kind:
 			if ft.Kind() == reflect.Uint32 {
-				return coderUint32
+				return nil, coderUint32
 			}
 		case pref.Int64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderInt64
+				return nil, coderInt64
 			}
 		case pref.Sint64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderSint64
+				return nil, coderSint64
 			}
 		case pref.Uint64Kind:
 			if ft.Kind() == reflect.Uint64 {
-				return coderUint64
+				return nil, coderUint64
 			}
 		case pref.Sfixed32Kind:
 			if ft.Kind() == reflect.Int32 {
-				return coderSfixed32
+				return nil, coderSfixed32
 			}
 		case pref.Fixed32Kind:
 			if ft.Kind() == reflect.Uint32 {
-				return coderFixed32
+				return nil, coderFixed32
 			}
 		case pref.FloatKind:
 			if ft.Kind() == reflect.Float32 {
-				return coderFloat
+				return nil, coderFloat
 			}
 		case pref.Sfixed64Kind:
 			if ft.Kind() == reflect.Int64 {
-				return coderSfixed64
+				return nil, coderSfixed64
 			}
 		case pref.Fixed64Kind:
 			if ft.Kind() == reflect.Uint64 {
-				return coderFixed64
+				return nil, coderFixed64
 			}
 		case pref.DoubleKind:
 			if ft.Kind() == reflect.Float64 {
-				return coderDouble
+				return nil, coderDouble
 			}
 		case pref.StringKind:
 			if ft.Kind() == reflect.String && strs.EnforceUTF8(fd) {
-				return coderStringValidateUTF8
+				return nil, coderStringValidateUTF8
 			}
 			if ft.Kind() == reflect.String {
-				return coderString
+				return nil, coderString
 			}
 			if ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8 && strs.EnforceUTF8(fd) {
-				return coderBytesValidateUTF8
+				return nil, coderBytesValidateUTF8
 			}
 			if ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8 {
-				return coderBytes
+				return nil, coderBytes
 			}
 		case pref.BytesKind:
 			if ft.Kind() == reflect.String {
-				return coderString
+				return nil, coderString
 			}
 			if ft.Kind() == reflect.Slice && ft.Elem().Kind() == reflect.Uint8 {
-				return coderBytes
+				return nil, coderBytes
 			}
 		}
 	}
diff --git a/internal/impl/decode.go b/internal/impl/decode.go
index 6e78faa..f3dcdf4 100644
--- a/internal/impl/decode.go
+++ b/internal/impl/decode.go
@@ -120,7 +120,7 @@
 				break
 			}
 			var o unmarshalOutput
-			o, err = f.funcs.unmarshal(b, p.Apply(f.offset), wtyp, opts)
+			o, err = f.funcs.unmarshal(b, p.Apply(f.offset), wtyp, f, opts)
 			n = o.n
 			if err != nil {
 				break
diff --git a/internal/impl/encode.go b/internal/impl/encode.go
index 608e57f..8923774 100644
--- a/internal/impl/encode.go
+++ b/internal/impl/encode.go
@@ -69,7 +69,7 @@
 		if f.isPointer && fptr.Elem().IsNil() {
 			continue
 		}
-		size += f.funcs.size(fptr, f.tagsize, opts)
+		size += f.funcs.size(fptr, f, opts)
 	}
 	if mi.unknownOffset.IsValid() {
 		u := *p.Apply(mi.unknownOffset).Bytes()
@@ -119,7 +119,7 @@
 		if f.isPointer && fptr.Elem().IsNil() {
 			continue
 		}
-		b, err = f.funcs.marshal(b, fptr, f.wiretag, opts)
+		b, err = f.funcs.marshal(b, fptr, f, opts)
 		if err != nil {
 			return b, err
 		}
diff --git a/internal/impl/isinit.go b/internal/impl/isinit.go
index d3a01bc..63d1fa5 100644
--- a/internal/impl/isinit.go
+++ b/internal/impl/isinit.go
@@ -54,7 +54,7 @@
 		if f.funcs.isInit == nil {
 			continue
 		}
-		if err := f.funcs.isInit(fptr); err != nil {
+		if err := f.funcs.isInit(fptr, f); err != nil {
 			return err
 		}
 	}