encoding/textpb: initial textproto unmarshaling

This initial textproto unmarshaling implementation covers messages
without the use of extensions, Any expansion, nor weak.

Updated encoding tests. Split some testcases to keep each simpler.

Added TestRoundTrip for example messages like the well-known types.

Change-Id: Icffab02834aa004fa8409a9da70624f687f604fb
Reviewed-on: https://go-review.googlesource.com/c/153020
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/encoding/textpb/decode.go b/encoding/textpb/decode.go
new file mode 100644
index 0000000..656133f
--- /dev/null
+++ b/encoding/textpb/decode.go
@@ -0,0 +1,393 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package textpb
+
+import (
+	"fmt"
+
+	"github.com/golang/protobuf/v2/internal/encoding/text"
+	"github.com/golang/protobuf/v2/internal/errors"
+	"github.com/golang/protobuf/v2/internal/pragma"
+	"github.com/golang/protobuf/v2/internal/set"
+	"github.com/golang/protobuf/v2/proto"
+	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
+)
+
+// Unmarshal reads the given []byte into the given proto.Message.
+// TODO: may want to describe when Unmarshal returns error.
+func Unmarshal(m proto.Message, b []byte) error {
+	return UnmarshalOptions{}.Unmarshal(m, b)
+}
+
+// UnmarshalOptions is a configurable textproto format parser.
+type UnmarshalOptions struct {
+	pragma.NoUnkeyedLiterals
+}
+
+// Unmarshal reads the given []byte and populates the given proto.Message using options in
+// UnmarshalOptions object.
+func (o UnmarshalOptions) Unmarshal(m proto.Message, b []byte) error {
+	var nerr errors.NonFatal
+
+	mr := m.ProtoReflect()
+	// Clear all fields before populating it.
+	// TODO: Determine if this needs to be consistent with jsonpb and binary unmarshal where
+	// behavior is to merge values into existing message. If decision is to not clear the fields
+	// ahead, code will need to be updated properly when merging nested messages.
+	resetMessage(mr)
+
+	// Parse into text.Value of message type.
+	val, err := text.Unmarshal(b)
+	if !nerr.Merge(err) {
+		return err
+	}
+
+	err = o.unmarshalMessage(val.Message(), mr)
+	if !nerr.Merge(err) {
+		return err
+	}
+
+	return nerr.E
+}
+
+// resetMessage clears all fields of given protoreflect.Message.
+// TODO: This should go into the proto package.
+func resetMessage(m pref.Message) {
+	knownFields := m.KnownFields()
+	knownFields.Range(func(num pref.FieldNumber, _ pref.Value) bool {
+		knownFields.Clear(num)
+		return true
+	})
+	unknownFields := m.UnknownFields()
+	unknownFields.Range(func(num pref.FieldNumber, _ pref.RawFields) bool {
+		unknownFields.Set(num, nil)
+		return true
+	})
+
+	extTypes := knownFields.ExtensionTypes()
+	extTypes.Range(func(xt pref.ExtensionType) bool {
+		extTypes.Remove(xt)
+		return true
+	})
+}
+
+// unmarshalMessage unmarshals a [][2]text.Value message into the given protoreflect.Message.
+func (o UnmarshalOptions) unmarshalMessage(tmsg [][2]text.Value, m pref.Message) error {
+	var nerr errors.NonFatal
+
+	msgType := m.Type()
+	fieldDescs := msgType.Fields()
+	knownFields := m.KnownFields()
+	var reqNums set.Ints
+	var seenNums set.Ints
+
+	for _, tfield := range tmsg {
+		tkey := tfield[0]
+		tval := tfield[1]
+
+		var fd pref.FieldDescriptor
+		if name, ok := tkey.Name(); ok {
+			fd = fieldDescs.ByName(name)
+		}
+		if fd == nil {
+			// TODO: Can provide option to ignore unknown message fields.
+			// TODO: Simply ignore and skip reserved field names.
+			return errors.New("%v contains unknown field: %v", msgType.FullName(), tkey)
+		}
+
+		if cardinality := fd.Cardinality(); cardinality == pref.Repeated {
+			// Map or list fields have cardinality of repeated.
+			if err := o.unmarshalRepeated(tval, fd, knownFields); !nerr.Merge(err) {
+				return err
+			}
+		} else {
+			// Required or optional fields.
+			num := uint64(fd.Number())
+			if seenNums.Has(num) {
+				return errors.New("non-repeated field %v is repeated", fd.FullName())
+			}
+			if err := o.unmarshalSingular(tval, fd, knownFields); !nerr.Merge(err) {
+				return err
+			}
+			if cardinality == pref.Required {
+				reqNums.Set(num)
+			}
+			seenNums.Set(num)
+		}
+	}
+
+	// Check for any missing required fields.
+	allReqNums := msgType.RequiredNumbers()
+	if reqNums.Len() != allReqNums.Len() {
+		for i := 0; i < allReqNums.Len(); i++ {
+			if num := allReqNums.Get(i); !reqNums.Has(uint64(num)) {
+				nerr.AppendRequiredNotSet(string(fieldDescs.ByNumber(num).FullName()))
+			}
+		}
+	}
+
+	return nerr.E
+}
+
+// unmarshalSingular unmarshals given text.Value into the non-repeated field.
+func (o UnmarshalOptions) unmarshalSingular(input text.Value, fd pref.FieldDescriptor, knownFields pref.KnownFields) error {
+	num := fd.Number()
+
+	var nerr errors.NonFatal
+	var val pref.Value
+	switch fd.Kind() {
+	case pref.MessageKind, pref.GroupKind:
+		if input.Type() != text.Message {
+			return errors.New("%v contains invalid message/group value: %v", fd.FullName(), input)
+		}
+		m := knownFields.NewMessage(num).ProtoReflect()
+		if err := o.unmarshalMessage(input.Message(), m); !nerr.Merge(err) {
+			return err
+		}
+		val = pref.ValueOf(m)
+	default:
+		var err error
+		val, err = unmarshalScalar(input, fd)
+		if !nerr.Merge(err) {
+			return err
+		}
+	}
+	knownFields.Set(num, val)
+
+	return nerr.E
+}
+
+// unmarshalRepeated unmarshals given text.Value into a repeated field. Caller should only
+// call this for cardinality=repeated.
+func (o UnmarshalOptions) unmarshalRepeated(input text.Value, fd pref.FieldDescriptor, knownFields pref.KnownFields) error {
+	var items []text.Value
+	// If input is not a list, turn it into a list.
+	if input.Type() != text.List {
+		items = []text.Value{input}
+	} else {
+		items = input.List()
+	}
+
+	var nerr errors.NonFatal
+	num := fd.Number()
+	val := knownFields.Get(num)
+	if !fd.IsMap() {
+		if err := o.unmarshalList(items, fd, val.List()); !nerr.Merge(err) {
+			return err
+		}
+	} else {
+		if err := o.unmarshalMap(items, fd, val.Map()); !nerr.Merge(err) {
+			return err
+		}
+	}
+
+	return nerr.E
+}
+
+// unmarshalScalar converts the given text.Value to a scalar/enum protoreflect.Value specified in
+// the given FieldDescriptor. Caller should not pass in a FieldDescriptor for a message/group kind.
+func unmarshalScalar(input text.Value, fd pref.FieldDescriptor) (pref.Value, error) {
+	const b32 = false
+	const b64 = true
+
+	switch kind := fd.Kind(); kind {
+	case pref.BoolKind:
+		if b, ok := input.Bool(); ok {
+			return pref.ValueOf(bool(b)), nil
+		}
+	case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
+		if n, ok := input.Int(b32); ok {
+			return pref.ValueOf(int32(n)), nil
+		}
+	case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
+		if n, ok := input.Int(b64); ok {
+			return pref.ValueOf(int64(n)), nil
+		}
+	case pref.Uint32Kind, pref.Fixed32Kind:
+		if n, ok := input.Uint(b32); ok {
+			return pref.ValueOf(uint32(n)), nil
+		}
+	case pref.Uint64Kind, pref.Fixed64Kind:
+		if n, ok := input.Uint(b64); ok {
+			return pref.ValueOf(uint64(n)), nil
+		}
+	case pref.FloatKind:
+		if n, ok := input.Float(b32); ok {
+			return pref.ValueOf(float32(n)), nil
+		}
+	case pref.DoubleKind:
+		if n, ok := input.Float(b64); ok {
+			return pref.ValueOf(float64(n)), nil
+		}
+	case pref.StringKind:
+		if input.Type() == text.String {
+			return pref.ValueOf(string(input.String())), nil
+		}
+	case pref.BytesKind:
+		if input.Type() == text.String {
+			return pref.ValueOf([]byte(input.String())), nil
+		}
+	case pref.EnumKind:
+		// If input is int32, use directly.
+		if n, ok := input.Int(b32); ok {
+			return pref.ValueOf(pref.EnumNumber(n)), nil
+		} else {
+			if name, ok := input.Name(); ok {
+				// Lookup EnumNumber based on name.
+				if enumVal := fd.EnumType().Values().ByName(name); enumVal != nil {
+					return pref.ValueOf(enumVal.Number()), nil
+				}
+			}
+		}
+	default:
+		panic(fmt.Sprintf("invalid scalar kind %v", kind))
+	}
+
+	return pref.Value{}, errors.New("%v contains invalid scalar value: %v", fd.FullName(), input)
+}
+
+// unmarshalList unmarshals given []text.Value into given protoreflect.List.
+func (o UnmarshalOptions) unmarshalList(inputList []text.Value, fd pref.FieldDescriptor, list pref.List) error {
+	var nerr errors.NonFatal
+
+	switch fd.Kind() {
+	case pref.MessageKind, pref.GroupKind:
+		for _, input := range inputList {
+			if input.Type() != text.Message {
+				return errors.New("%v contains invalid message/group value: %v", fd.FullName(), input)
+			}
+			m := list.NewMessage().ProtoReflect()
+			if err := o.unmarshalMessage(input.Message(), m); !nerr.Merge(err) {
+				return err
+			}
+			list.Append(pref.ValueOf(m))
+		}
+	default:
+		for _, input := range inputList {
+			val, err := unmarshalScalar(input, fd)
+			if !nerr.Merge(err) {
+				return err
+			}
+			list.Append(val)
+		}
+	}
+
+	return nerr.E
+}
+
+// unmarshalMap unmarshals given []text.Value into given protoreflect.Map.
+func (o UnmarshalOptions) unmarshalMap(input []text.Value, fd pref.FieldDescriptor, mmap pref.Map) error {
+	var nerr errors.NonFatal
+	fields := fd.MessageType().Fields()
+	keyDesc := fields.ByNumber(1)
+	valDesc := fields.ByNumber(2)
+
+	// Determine ahead whether map entry is a scalar type or a message type in order to call the
+	// appropriate unmarshalMapValue func inside the for loop below.
+	unmarshalMapValue := o.unmarshalMapScalarValue
+	switch valDesc.Kind() {
+	case pref.MessageKind, pref.GroupKind:
+		unmarshalMapValue = o.unmarshalMapMessageValue
+	}
+
+	for _, entry := range input {
+		if entry.Type() != text.Message {
+			return errors.New("%v contains invalid map entry: %v", fd.FullName(), entry)
+		}
+		tkey, tval, err := parseMapEntry(entry.Message(), fd.FullName())
+		if !nerr.Merge(err) {
+			return err
+		}
+		pkey, err := unmarshalMapKey(tkey, keyDesc)
+		if !nerr.Merge(err) {
+			return err
+		}
+		err = unmarshalMapValue(tval, pkey, valDesc, mmap)
+		if !nerr.Merge(err) {
+			return err
+		}
+	}
+
+	return nerr.E
+}
+
+// parseMapEntry parses [][2]text.Value for field names key and value, and return corresponding
+// field values. If there are duplicate field names, the value for the last field is returned. If
+// the field name does not exist, it will return the zero value of text.Value. It will return an
+// error if there are unknown field names.
+func parseMapEntry(mapEntry [][2]text.Value, name pref.FullName) (key text.Value, value text.Value, err error) {
+	for _, field := range mapEntry {
+		keyStr, ok := field[0].Name()
+		if ok {
+			switch keyStr {
+			case "key":
+				if key.Type() != 0 {
+					return key, value, errors.New("%v contains duplicate key field", name)
+				}
+				key = field[1]
+			case "value":
+				if value.Type() != 0 {
+					return key, value, errors.New("%v contains duplicate value field", name)
+				}
+				value = field[1]
+			default:
+				ok = false
+			}
+		}
+		if !ok {
+			// TODO: Do not return error if ignore unknown option is added and enabled.
+			return key, value, errors.New("%v contains unknown map entry name: %v", name, field[0])
+		}
+	}
+	return key, value, nil
+}
+
+// unmarshalMapKey converts given text.Value into a protoreflect.MapKey. A map key type is any
+// integral or string type.
+func unmarshalMapKey(input text.Value, fd pref.FieldDescriptor) (pref.MapKey, error) {
+	// If input is not set, use the zero value.
+	if input.Type() == 0 {
+		return fd.Default().MapKey(), nil
+	}
+
+	val, err := unmarshalScalar(input, fd)
+	if err != nil {
+		return pref.MapKey{}, errors.New("%v contains invalid key: %v", fd.FullName(), input)
+	}
+	return val.MapKey(), nil
+}
+
+// unmarshalMapMessageValue unmarshals given message-type text.Value into a protoreflect.Map for
+// the given MapKey.
+func (o UnmarshalOptions) unmarshalMapMessageValue(input text.Value, pkey pref.MapKey, _ pref.FieldDescriptor, mmap pref.Map) error {
+	var nerr errors.NonFatal
+	var value [][2]text.Value
+	if input.Type() != 0 {
+		value = input.Message()
+	}
+	m := mmap.NewMessage().ProtoReflect()
+	if err := o.unmarshalMessage(value, m); !nerr.Merge(err) {
+		return err
+	}
+	mmap.Set(pkey, pref.ValueOf(m))
+	return nerr.E
+}
+
+// unmarshalMapScalarValue unmarshals given scalar-type text.Value into a protoreflect.Map
+// for the given MapKey.
+func (o UnmarshalOptions) unmarshalMapScalarValue(input text.Value, pkey pref.MapKey, fd pref.FieldDescriptor, mmap pref.Map) error {
+	var val pref.Value
+	if input.Type() == 0 {
+		val = fd.Default()
+	} else {
+		var err error
+		val, err = unmarshalScalar(input, fd)
+		if err != nil {
+			return err
+		}
+	}
+	mmap.Set(pkey, val)
+	return nil
+}
diff --git a/encoding/textpb/decode_test.go b/encoding/textpb/decode_test.go
new file mode 100644
index 0000000..2717f38
--- /dev/null
+++ b/encoding/textpb/decode_test.go
@@ -0,0 +1,985 @@
+// Copyright 2018 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package textpb_test
+
+import (
+	"math"
+	"testing"
+
+	protoV1 "github.com/golang/protobuf/proto"
+	"github.com/golang/protobuf/v2/encoding/textpb"
+	"github.com/golang/protobuf/v2/internal/scalar"
+
+	// The legacy package must be imported prior to use of any legacy messages.
+	// TODO: Remove this when protoV1 registers these hooks for you.
+	_ "github.com/golang/protobuf/v2/internal/legacy"
+
+	"github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb2"
+	"github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb3"
+)
+
+func TestUnmarshal(t *testing.T) {
+	// TODO: Switch to using proto.Message for inputMessage and wantMessage fields when v2
+	// proto.Equal is implemented.
+	tests := []struct {
+		desc         string
+		inputMessage protoV1.Message
+		inputText    string
+		wantMessage  protoV1.Message
+		wantErr      bool
+	}{{
+		desc:         "proto2 empty message",
+		inputMessage: &pb2.Scalars{},
+		wantMessage:  &pb2.Scalars{},
+	}, {
+		desc:         "proto2 optional scalar fields set to zero values",
+		inputMessage: &pb2.Scalars{},
+		inputText: `opt_bool: false
+opt_int32: 0
+opt_int64: 0
+opt_uint32: 0
+opt_uint64: 0
+opt_sint32: 0
+opt_sint64: 0
+opt_fixed32: 0
+opt_fixed64: 0
+opt_sfixed32: 0
+opt_sfixed64: 0
+opt_float: 0
+opt_double: 0
+opt_bytes: ""
+opt_string: ""
+`,
+		wantMessage: &pb2.Scalars{
+			OptBool:     scalar.Bool(false),
+			OptInt32:    scalar.Int32(0),
+			OptInt64:    scalar.Int64(0),
+			OptUint32:   scalar.Uint32(0),
+			OptUint64:   scalar.Uint64(0),
+			OptSint32:   scalar.Int32(0),
+			OptSint64:   scalar.Int64(0),
+			OptFixed32:  scalar.Uint32(0),
+			OptFixed64:  scalar.Uint64(0),
+			OptSfixed32: scalar.Int32(0),
+			OptSfixed64: scalar.Int64(0),
+			OptFloat:    scalar.Float32(0),
+			OptDouble:   scalar.Float64(0),
+			OptBytes:    []byte{},
+			OptString:   scalar.String(""),
+		},
+	}, {
+		desc:         "proto3 scalar fields set to zero values",
+		inputMessage: &pb3.Scalars{},
+		inputText: `s_bool: false
+s_int32: 0
+s_int64: 0
+s_uint32: 0
+s_uint64: 0
+s_sint32: 0
+s_sint64: 0
+s_fixed32: 0
+s_fixed64: 0
+s_sfixed32: 0
+s_sfixed64: 0
+s_float: 0
+s_double: 0
+s_bytes: ""
+s_string: ""
+`,
+		wantMessage: &pb3.Scalars{},
+	}, {
+		desc:         "proto2 optional scalar fields",
+		inputMessage: &pb2.Scalars{},
+		inputText: `opt_bool: true
+opt_int32: 255
+opt_int64: 3735928559
+opt_uint32: 0xff
+opt_uint64: 0xdeadbeef
+opt_sint32: -1001
+opt_sint64: -0xffff
+opt_fixed64: 64
+opt_sfixed32: -32
+opt_float: 1.234
+opt_double: 1.23e+100
+opt_bytes: "\xe8\xb0\xb7\xe6\xad\x8c"
+opt_string: "谷歌"
+`,
+		wantMessage: &pb2.Scalars{
+			OptBool:     scalar.Bool(true),
+			OptInt32:    scalar.Int32(0xff),
+			OptInt64:    scalar.Int64(0xdeadbeef),
+			OptUint32:   scalar.Uint32(0xff),
+			OptUint64:   scalar.Uint64(0xdeadbeef),
+			OptSint32:   scalar.Int32(-1001),
+			OptSint64:   scalar.Int64(-0xffff),
+			OptFixed64:  scalar.Uint64(64),
+			OptSfixed32: scalar.Int32(-32),
+			OptFloat:    scalar.Float32(1.234),
+			OptDouble:   scalar.Float64(1.23e100),
+			OptBytes:    []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
+			OptString:   scalar.String("谷歌"),
+		},
+	}, {
+		desc:         "proto3 scalar fields",
+		inputMessage: &pb3.Scalars{},
+		inputText: `s_bool: true
+s_int32: 255
+s_int64: 3735928559
+s_uint32: 0xff
+s_uint64: 0xdeadbeef
+s_sint32: -1001
+s_sint64: -0xffff
+s_fixed64: 64
+s_sfixed32: -32
+s_float: 1.234
+s_double: 1.23e+100
+s_bytes: "\xe8\xb0\xb7\xe6\xad\x8c"
+s_string: "谷歌"
+`,
+		wantMessage: &pb3.Scalars{
+			SBool:     true,
+			SInt32:    0xff,
+			SInt64:    0xdeadbeef,
+			SUint32:   0xff,
+			SUint64:   0xdeadbeef,
+			SSint32:   -1001,
+			SSint64:   -0xffff,
+			SFixed64:  64,
+			SSfixed32: -32,
+			SFloat:    1.234,
+			SDouble:   1.23e100,
+			SBytes:    []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
+			SString:   "谷歌",
+		},
+	}, {
+		desc:         "proto2 message contains unknown field",
+		inputMessage: &pb2.Scalars{},
+		inputText:    "unknown_field: 123",
+		wantErr:      true,
+	}, {
+		desc:         "proto3 message contains unknown field",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "unknown_field: 456",
+		wantErr:      true,
+	}, {
+		desc:         "proto2 numeric key field",
+		inputMessage: &pb2.Scalars{},
+		inputText:    "1: true",
+		wantErr:      true,
+	}, {
+		desc:         "proto3 numeric key field",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "1: true",
+		wantErr:      true,
+	}, {
+		desc:         "invalid bool value",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_bool: 123",
+		wantErr:      true,
+	}, {
+		desc:         "invalid int32 value",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_int32: not_a_num",
+		wantErr:      true,
+	}, {
+		desc:         "invalid int64 value",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_int64: 'not a num either'",
+		wantErr:      true,
+	}, {
+		desc:         "invalid uint32 value",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_fixed32: -42",
+		wantErr:      true,
+	}, {
+		desc:         "invalid uint64 value",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_uint64: -47",
+		wantErr:      true,
+	}, {
+		desc:         "invalid sint32 value",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_sint32: '42'",
+		wantErr:      true,
+	}, {
+		desc:         "invalid sint64 value",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_sint64: '-47'",
+		wantErr:      true,
+	}, {
+		desc:         "invalid fixed32 value",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_fixed32: -42",
+		wantErr:      true,
+	}, {
+		desc:         "invalid fixed64 value",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_fixed64: -42",
+		wantErr:      true,
+	}, {
+		desc:         "invalid sfixed32 value",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_sfixed32: 'not valid'",
+		wantErr:      true,
+	}, {
+		desc:         "invalid sfixed64 value",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_sfixed64: bad",
+		wantErr:      true,
+	}, {
+		desc:         "float32 positive infinity",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_float: inf",
+		wantMessage: &pb3.Scalars{
+			SFloat: float32(math.Inf(1)),
+		},
+	}, {
+		desc:         "float32 negative infinity",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_float: -inf",
+		wantMessage: &pb3.Scalars{
+			SFloat: float32(math.Inf(-1)),
+		},
+	}, {
+		desc:         "float64 positive infinity",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_double: inf",
+		wantMessage: &pb3.Scalars{
+			SDouble: math.Inf(1),
+		},
+	}, {
+		desc:         "float64 negative infinity",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_double: -inf",
+		wantMessage: &pb3.Scalars{
+			SDouble: math.Inf(-1),
+		},
+	}, {
+		desc:         "invalid string value",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_string: invalid_string",
+		wantErr:      true,
+	}, {
+		desc:         "proto2 bytes set to empty string",
+		inputMessage: &pb2.Scalars{},
+		inputText:    "opt_bytes: ''",
+		wantMessage: &pb2.Scalars{
+			OptBytes: []byte(""),
+		},
+	}, {
+		desc:         "proto3 bytes set to empty string",
+		inputMessage: &pb3.Scalars{},
+		inputText:    "s_bytes: ''",
+		wantMessage:  &pb3.Scalars{},
+	}, {
+		desc:         "proto2 duplicate singular field",
+		inputMessage: &pb2.Scalars{},
+		inputText: `
+opt_bool: true
+opt_bool: false
+`,
+		wantErr: true,
+	}, {
+		desc:         "proto2 invalid singular field",
+		inputMessage: &pb2.Scalars{},
+		inputText: `
+opt_bool: [true, false]
+`,
+		wantErr: true,
+	}, {
+		desc:         "proto2 more duplicate singular field",
+		inputMessage: &pb2.Scalars{},
+		inputText: `
+opt_bool: true
+opt_string: "hello"
+opt_bool: false
+`,
+		wantErr: true,
+	}, {
+		desc:         "proto3 duplicate singular field",
+		inputMessage: &pb3.Scalars{},
+		inputText: `
+s_bool: false
+s_bool: true
+`,
+		wantErr: true,
+	}, {
+		desc:         "proto3 more duplicate singular field",
+		inputMessage: &pb3.Scalars{},
+		inputText: `
+s_bool: false
+s_string: ""
+s_bool: true
+`,
+		wantErr: true,
+	}, {
+		desc:         "proto2 enum",
+		inputMessage: &pb2.Enums{},
+		inputText: `
+opt_enum: FIRST
+opt_nested_enum: UNO
+`,
+		wantMessage: &pb2.Enums{
+			OptEnum:       pb2.Enum_FIRST.Enum(),
+			OptNestedEnum: pb2.Enums_UNO.Enum(),
+		},
+	}, {
+		desc:         "proto2 enum set to numeric values",
+		inputMessage: &pb2.Enums{},
+		inputText: `
+opt_enum: 1
+opt_nested_enum: 2
+`,
+		wantMessage: &pb2.Enums{
+			OptEnum:       pb2.Enum_FIRST.Enum(),
+			OptNestedEnum: pb2.Enums_DOS.Enum(),
+		},
+	}, {
+		desc:         "proto2 enum set to unnamed numeric values",
+		inputMessage: &pb2.Enums{},
+		inputText: `
+opt_enum: 101
+opt_nested_enum: -101
+`,
+		wantMessage: &pb2.Enums{
+			OptEnum:       pb2Enum(101),
+			OptNestedEnum: pb2Enums_NestedEnum(-101),
+		},
+	}, {
+		desc:         "proto2 enum set to invalid named",
+		inputMessage: &pb2.Enums{},
+		inputText: `
+opt_enum: UNNAMED 
+opt_nested_enum: UNNAMED_TOO
+`,
+		wantErr: true,
+	}, {
+		desc:         "proto3 enum name value",
+		inputMessage: &pb3.Enums{},
+		inputText: `
+s_enum: ONE
+s_nested_enum: DIEZ
+`,
+		wantMessage: &pb3.Enums{
+			SEnum:       pb3.Enum_ONE,
+			SNestedEnum: pb3.Enums_DIEZ,
+		},
+	}, {
+		desc:         "proto3 enum numeric value",
+		inputMessage: &pb3.Enums{},
+		inputText: `
+s_enum: 2
+s_nested_enum: 1
+`,
+		wantMessage: &pb3.Enums{
+			SEnum:       pb3.Enum_TWO,
+			SNestedEnum: pb3.Enums_UNO,
+		},
+	}, {
+		desc:         "proto3 enum unnamed numeric value",
+		inputMessage: &pb3.Enums{},
+		inputText: `
+s_enum: 0x7fffffff
+s_nested_enum: -0x80000000
+`,
+		wantMessage: &pb3.Enums{
+			SEnum:       0x7fffffff,
+			SNestedEnum: -0x80000000,
+		},
+	}, {
+		desc:         "proto2 nested empty messages",
+		inputMessage: &pb2.Nests{},
+		inputText: `
+opt_nested: {}
+optgroup: {}
+`,
+		wantMessage: &pb2.Nests{
+			OptNested: &pb2.Nested{},
+			Optgroup:  &pb2.Nests_OptGroup{},
+		},
+	}, {
+		desc:         "proto2 nested messages",
+		inputMessage: &pb2.Nests{},
+		inputText: `
+opt_nested: {
+  opt_string: "nested message"
+  opt_nested: {
+    opt_string: "another nested message"
+  }
+}
+`,
+		wantMessage: &pb2.Nests{
+			OptNested: &pb2.Nested{
+				OptString: scalar.String("nested message"),
+				OptNested: &pb2.Nested{
+					OptString: scalar.String("another nested message"),
+				},
+			},
+		},
+	}, {
+		desc:         "proto3 nested empty message",
+		inputMessage: &pb3.Nests{},
+		inputText:    "s_nested: {}",
+		wantMessage: &pb3.Nests{
+			SNested: &pb3.Nested{},
+		},
+	}, {
+		desc:         "proto3 nested message",
+		inputMessage: &pb3.Nests{},
+		inputText: `
+s_nested: {
+  s_string: "nested message"
+  s_nested: {
+    s_string: "another nested message"
+  }
+}
+`,
+		wantMessage: &pb3.Nests{
+			SNested: &pb3.Nested{
+				SString: "nested message",
+				SNested: &pb3.Nested{
+					SString: "another nested message",
+				},
+			},
+		},
+	}, {
+		desc:         "oneof field set to empty string",
+		inputMessage: &pb2.Oneofs{},
+		inputText:    "str: ''",
+		wantMessage: &pb2.Oneofs{
+			Union: &pb2.Oneofs_Str{},
+		},
+	}, {
+		desc:         "oneof field set to string",
+		inputMessage: &pb2.Oneofs{},
+		inputText:    "str: 'hello'",
+		wantMessage: &pb2.Oneofs{
+			Union: &pb2.Oneofs_Str{
+				Str: "hello",
+			},
+		},
+	}, {
+		desc:         "oneof field set to empty message",
+		inputMessage: &pb2.Oneofs{},
+		inputText:    "msg: {}",
+		wantMessage: &pb2.Oneofs{
+			Union: &pb2.Oneofs_Msg{
+				Msg: &pb2.Nested{},
+			},
+		},
+	}, {
+		desc:         "oneof field set to message",
+		inputMessage: &pb2.Oneofs{},
+		inputText: `
+msg: {
+  opt_string: "nested message"
+}
+`,
+		wantMessage: &pb2.Oneofs{
+			Union: &pb2.Oneofs_Msg{
+				Msg: &pb2.Nested{
+					OptString: scalar.String("nested message"),
+				},
+			},
+		},
+	}, {
+		desc:         "repeated scalar using same field name",
+		inputMessage: &pb2.Repeats{},
+		inputText: `
+rpt_string: "a"
+rpt_string: "b"
+rpt_int32: 0xff
+rpt_float: 1.23
+rpt_bytes: "bytes"
+`,
+		wantMessage: &pb2.Repeats{
+			RptString: []string{"a", "b"},
+			RptInt32:  []int32{0xff},
+			RptFloat:  []float32{1.23},
+			RptBytes:  [][]byte{[]byte("bytes")},
+		},
+	}, {
+		desc:         "repeated using mix of [] and repeated field name",
+		inputMessage: &pb2.Repeats{},
+		inputText: `
+rpt_string: "a"
+rpt_bool: true
+rpt_string: ["x", "y"]
+rpt_bool: [ false, true ]
+rpt_string: "b"
+`,
+		wantMessage: &pb2.Repeats{
+			RptString: []string{"a", "x", "y", "b"},
+			RptBool:   []bool{true, false, true},
+		},
+	}, {
+		desc:         "repeated enums",
+		inputMessage: &pb2.Enums{},
+		inputText: `
+rpt_enum: TENTH
+rpt_enum: 1
+rpt_nested_enum: [DOS, 2]
+rpt_enum: 42
+rpt_nested_enum: -47
+`,
+		wantMessage: &pb2.Enums{
+			RptEnum:       []pb2.Enum{pb2.Enum_TENTH, pb2.Enum_FIRST, 42},
+			RptNestedEnum: []pb2.Enums_NestedEnum{pb2.Enums_DOS, pb2.Enums_DOS, -47},
+		},
+	}, {
+		desc:         "repeated nested messages",
+		inputMessage: &pb2.Nests{},
+		inputText: `
+rpt_nested: {
+  opt_string: "repeat nested one"
+}
+rpt_nested: {
+  opt_string: "repeat nested two"
+  opt_nested: {
+    opt_string: "inside repeat nested two"
+  }
+}
+rpt_nested: {}
+`,
+		wantMessage: &pb2.Nests{
+			RptNested: []*pb2.Nested{
+				{
+					OptString: scalar.String("repeat nested one"),
+				},
+				{
+					OptString: scalar.String("repeat nested two"),
+					OptNested: &pb2.Nested{
+						OptString: scalar.String("inside repeat nested two"),
+					},
+				},
+				{},
+			},
+		},
+	}, {
+		desc:         "repeated group fields",
+		inputMessage: &pb2.Nests{},
+		inputText: `
+rptgroup: {
+  rpt_bool: true
+  rpt_bool: false
+}
+rptgroup: {}
+`,
+		wantMessage: &pb2.Nests{
+			Rptgroup: []*pb2.Nests_RptGroup{
+				{
+					RptBool: []bool{true, false},
+				},
+				{},
+			},
+		},
+	}, {
+		desc:         "map fields 1",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+int32_to_str: {
+  key: -101
+  value: "-101"
+}
+int32_to_str: {
+  key: 0
+  value: "zero"
+}
+sfixed64_to_bool: {
+  key: 0
+  value: false
+}
+int32_to_str: {
+  key: 255
+  value: "0xff"
+}
+bool_to_uint32: {
+  key: false
+  value: 101
+}
+sfixed64_to_bool: {
+  key: 51966
+  value: true
+}
+bool_to_uint32: {
+  key: true
+  value: 42
+}
+`,
+		wantMessage: &pb2.Maps{
+			Int32ToStr: map[int32]string{
+				-101: "-101",
+				0xff: "0xff",
+				0:    "zero",
+			},
+			Sfixed64ToBool: map[int64]bool{
+				0xcafe: true,
+				0:      false,
+			},
+			BoolToUint32: map[bool]uint32{
+				true:  42,
+				false: 101,
+			},
+		},
+	}, {
+		desc:         "map fields 2",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+uint64_to_enum: {
+  key: 1
+  value: FIRST
+}
+uint64_to_enum: {
+  key: 2
+  value: SECOND
+}
+uint64_to_enum: {
+  key: 10
+  value: TENTH
+}
+`,
+		wantMessage: &pb2.Maps{
+			Uint64ToEnum: map[uint64]pb2.Enum{
+				1:  pb2.Enum_FIRST,
+				2:  pb2.Enum_SECOND,
+				10: pb2.Enum_TENTH,
+			},
+		},
+	}, {
+		desc:         "map fields 3",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+str_to_nested: {
+  key: "nested_one"
+  value: {
+    opt_string: "nested in a map"
+  }
+}
+`,
+		wantMessage: &pb2.Maps{
+			StrToNested: map[string]*pb2.Nested{
+				"nested_one": &pb2.Nested{
+					OptString: scalar.String("nested in a map"),
+				},
+			},
+		},
+	}, {
+		desc:         "map fields 4",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+str_to_oneofs: {
+  key: "nested"
+  value: {
+    msg: {
+      opt_string: "nested oneof in map field value"
+    }
+  }
+}
+str_to_oneofs: {
+  key: "string"
+  value: {
+    str: "hello"
+  }
+}
+`,
+		wantMessage: &pb2.Maps{
+			StrToOneofs: map[string]*pb2.Oneofs{
+				"string": &pb2.Oneofs{
+					Union: &pb2.Oneofs_Str{
+						Str: "hello",
+					},
+				},
+				"nested": &pb2.Oneofs{
+					Union: &pb2.Oneofs_Msg{
+						Msg: &pb2.Nested{
+							OptString: scalar.String("nested oneof in map field value"),
+						},
+					},
+				},
+			},
+		},
+	}, {
+		desc:         "map contains duplicate keys",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+int32_to_str: {
+  key: 0
+  value: "cero"
+}
+int32_to_str: {
+  key: 0
+  value: "zero"
+}
+`,
+		wantMessage: &pb2.Maps{
+			Int32ToStr: map[int32]string{
+				0: "zero",
+			},
+		},
+	}, {
+		desc:         "map contains duplicate key fields",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+int32_to_str: {
+  key: 0
+  key: 1
+  value: "cero"
+}
+`,
+		wantErr: true,
+	}, {
+		desc:         "map contains duplicate value fields",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+int32_to_str: {
+  key: 1
+  value: "cero"
+  value: "uno"
+}
+`,
+		wantErr: true,
+	}, {
+		desc:         "map contains missing key",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+int32_to_str: {
+  value: "zero"
+}
+`,
+		wantMessage: &pb2.Maps{
+			Int32ToStr: map[int32]string{
+				0: "zero",
+			},
+		},
+	}, {
+		desc:         "map contains missing value",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+int32_to_str: {
+  key: 100
+}
+`,
+		wantMessage: &pb2.Maps{
+			Int32ToStr: map[int32]string{
+				100: "",
+			},
+		},
+	}, {
+		desc:         "map contains missing key and value",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+int32_to_str: {}
+`,
+		wantMessage: &pb2.Maps{
+			Int32ToStr: map[int32]string{
+				0: "",
+			},
+		},
+	}, {
+		desc:         "map contains unknown field",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+int32_to_str: {
+  key: 0
+  value: "cero"
+  unknown: "bad"
+}
+`,
+		wantErr: true,
+	}, {
+		desc:         "map contains extension-like key field",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+int32_to_str: {
+  [key]: 10
+  value: "ten"
+}
+`,
+		wantErr: true,
+	}, {
+		desc:         "map contains invalid key",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+int32_to_str: {
+  key: "invalid"
+  value: "cero"
+}
+`,
+		wantErr: true,
+	}, {
+		desc:         "map contains invalid value",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+int32_to_str: {
+  key: 100
+  value: 101
+}
+`,
+		wantErr: true,
+	}, {
+		desc:         "map using mix of [] and repeated",
+		inputMessage: &pb2.Maps{},
+		inputText: `
+int32_to_str: {
+  key: 1
+  value: "one"
+}
+int32_to_str: [
+  {
+    key: 2
+    value: "not this"
+  },
+  {
+  },
+  {
+    key: 3
+    value: "three"
+  }
+]
+int32_to_str: {
+  key: 2
+  value: "two"
+}
+`,
+		wantMessage: &pb2.Maps{
+			Int32ToStr: map[int32]string{
+				0: "",
+				1: "one",
+				2: "two",
+				3: "three",
+			},
+		},
+	}, {
+		desc:         "proto2 required fields not set",
+		inputMessage: &pb2.Requireds{},
+		wantErr:      true,
+	}, {
+		desc:         "proto2 required field set but not optional",
+		inputMessage: &pb2.PartialRequired{},
+		inputText:    "req_string: 'this is required'",
+		wantMessage: &pb2.PartialRequired{
+			ReqString: scalar.String("this is required"),
+		},
+	}, {
+		desc:         "proto2 required fields partially set",
+		inputMessage: &pb2.Requireds{},
+		inputText: `
+req_bool: false
+req_fixed32: 47
+req_sfixed64: 3203386110
+req_string: "hello"
+req_enum: FIRST
+`,
+		wantMessage: &pb2.Requireds{
+			ReqBool:     scalar.Bool(false),
+			ReqFixed32:  scalar.Uint32(47),
+			ReqSfixed64: scalar.Int64(0xbeefcafe),
+			ReqString:   scalar.String("hello"),
+			ReqEnum:     pb2.Enum_FIRST.Enum(),
+		},
+		wantErr: true,
+	}, {
+		desc:         "proto2 required fields all set",
+		inputMessage: &pb2.Requireds{},
+		inputText: `
+req_bool: false
+req_fixed32: 0
+req_fixed64: 0
+req_sfixed32: 0
+req_sfixed64: 0
+req_float: 0
+req_double: 0
+req_string: ""
+req_bytes: ""
+req_enum: UNKNOWN
+req_nested: {}
+`,
+		wantMessage: &pb2.Requireds{
+			ReqBool:     scalar.Bool(false),
+			ReqFixed32:  scalar.Uint32(0),
+			ReqFixed64:  scalar.Uint64(0),
+			ReqSfixed32: scalar.Int32(0),
+			ReqSfixed64: scalar.Int64(0),
+			ReqFloat:    scalar.Float32(0),
+			ReqDouble:   scalar.Float64(0),
+			ReqString:   scalar.String(""),
+			ReqEnum:     pb2.Enum_UNKNOWN.Enum(),
+			ReqBytes:    []byte{},
+			ReqNested:   &pb2.Nested{},
+		},
+	}, {
+		desc:         "indirect required field",
+		inputMessage: &pb2.IndirectRequired{},
+		inputText:    "opt_nested: {}",
+		wantMessage: &pb2.IndirectRequired{
+			OptNested: &pb2.NestedWithRequired{},
+		},
+		wantErr: true,
+	}, {
+		desc:         "indirect required field in repeated",
+		inputMessage: &pb2.IndirectRequired{},
+		inputText: `
+rpt_nested: {
+  req_string: "one"
+}
+rpt_nested: {}
+rpt_nested: {
+  req_string: "three"
+}
+`,
+		wantMessage: &pb2.IndirectRequired{
+			RptNested: []*pb2.NestedWithRequired{
+				{
+					ReqString: scalar.String("one"),
+				},
+				{},
+				{
+					ReqString: scalar.String("three"),
+				},
+			},
+		},
+		wantErr: true,
+	}, {
+		desc:         "indirect required field in map",
+		inputMessage: &pb2.IndirectRequired{},
+		inputText: `
+str_to_nested: {
+  key: "missing"
+}
+str_to_nested: {
+  key: "contains"
+  value: {
+    req_string: "here"
+  }
+}
+`,
+		wantMessage: &pb2.IndirectRequired{
+			StrToNested: map[string]*pb2.NestedWithRequired{
+				"missing": &pb2.NestedWithRequired{},
+				"contains": &pb2.NestedWithRequired{
+					ReqString: scalar.String("here"),
+				},
+			},
+		},
+		wantErr: true,
+	}}
+
+	for _, tt := range tests {
+		tt := tt
+		t.Run(tt.desc, func(t *testing.T) {
+			t.Parallel()
+			err := textpb.Unmarshal(M(tt.inputMessage), []byte(tt.inputText))
+			if err != nil && !tt.wantErr {
+				t.Errorf("Unmarshal() returned error: %v\n\n", err)
+			}
+			if err == nil && tt.wantErr {
+				t.Error("Unmarshal() got nil error, want error\n\n")
+			}
+			if tt.wantMessage != nil && !protoV1.Equal(tt.inputMessage, tt.wantMessage) {
+				t.Errorf("Unmarshal()\n<got>\n%v\n<want>\n%v\n", tt.inputMessage, tt.wantMessage)
+			}
+		})
+	}
+}
diff --git a/encoding/textpb/encode.go b/encoding/textpb/encode.go
index 1b11e66..5d00cc2 100644
--- a/encoding/textpb/encode.go
+++ b/encoding/textpb/encode.go
@@ -14,7 +14,7 @@
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
 )
 
-// Marshal marshals a proto.Message in text format using default options.
+// Marshal writes the given proto.Message in textproto format using default options.
 // TODO: may want to describe when Marshal returns error.
 func Marshal(m proto.Message) ([]byte, error) {
 	return MarshalOptions{}.Marshal(m)
@@ -28,20 +28,15 @@
 	Compact bool
 }
 
-// Marshal returns the given proto.Message in text format using options in MarshalOptions object.
+// Marshal writes the given proto.Message in textproto format using options in MarshalOptions object.
 func (o MarshalOptions) Marshal(m proto.Message) ([]byte, error) {
 	var nerr errors.NonFatal
 	var v text.Value
 
-	if m == nil {
-		// TODO: Make sure this is consistent with jsonpb and binary serialization.
-		v = text.ValueOf([][2]text.Value{})
-	} else {
-		var err error
-		v, err = o.marshalMessage(m.ProtoReflect())
-		if !nerr.Merge(err) {
-			return nil, err
-		}
+	var err error
+	v, err = o.marshalMessage(m.ProtoReflect())
+	if !nerr.Merge(err) {
+		return nil, err
 	}
 
 	indent := "  "
@@ -69,31 +64,31 @@
 	knownFields := m.KnownFields()
 	size := fieldDescs.Len()
 	for i := 0; i < size; i++ {
-		fieldDesc := fieldDescs.Get(i)
-		fieldNum := fieldDesc.Number()
+		fd := fieldDescs.Get(i)
+		num := fd.Number()
 
-		if !knownFields.Has(fieldNum) {
-			if fieldDesc.Cardinality() == pref.Required {
+		if !knownFields.Has(num) {
+			if fd.Cardinality() == pref.Required {
 				// Treat unset required fields as a non-fatal error.
-				nerr.AppendRequiredNotSet(string(fieldDesc.FullName()))
+				nerr.AppendRequiredNotSet(string(fd.FullName()))
 			}
 			continue
 		}
 
-		txtName := text.ValueOf(fieldDesc.Name())
-		value := knownFields.Get(fieldNum)
+		tname := text.ValueOf(fd.Name())
+		pval := knownFields.Get(num)
 
-		if fieldDesc.Cardinality() == pref.Repeated {
+		if fd.Cardinality() == pref.Repeated {
 			// Map or repeated fields.
 			var items []text.Value
 			var err error
-			if fieldDesc.IsMap() {
-				items, err = o.marshalMap(value.Map(), fieldDesc)
+			if fd.IsMap() {
+				items, err = o.marshalMap(pval.Map(), fd)
 				if !nerr.Merge(err) {
 					return text.Value{}, err
 				}
 			} else {
-				items, err = o.marshalList(value.List(), fieldDesc)
+				items, err = o.marshalList(pval.List(), fd)
 				if !nerr.Merge(err) {
 					return text.Value{}, err
 				}
@@ -101,15 +96,15 @@
 
 			// Add each item as key: value field.
 			for _, item := range items {
-				msgFields = append(msgFields, [2]text.Value{txtName, item})
+				msgFields = append(msgFields, [2]text.Value{tname, item})
 			}
 		} else {
 			// Required or optional fields.
-			txtValue, err := o.marshalSingular(value, fieldDesc)
+			tval, err := o.marshalSingular(pval, fd)
 			if !nerr.Merge(err) {
 				return text.Value{}, err
 			}
-			msgFields = append(msgFields, [2]text.Value{txtName, txtValue})
+			msgFields = append(msgFields, [2]text.Value{tname, tval})
 		}
 
 	}
diff --git a/encoding/textpb/encode_test.go b/encoding/textpb/encode_test.go
index ffb2c5a..498b2a7 100644
--- a/encoding/textpb/encode_test.go
+++ b/encoding/textpb/encode_test.go
@@ -9,6 +9,7 @@
 	"strings"
 	"testing"
 
+	protoV1 "github.com/golang/protobuf/proto"
 	"github.com/golang/protobuf/v2/encoding/textpb"
 	"github.com/golang/protobuf/v2/internal/detrand"
 	"github.com/golang/protobuf/v2/internal/impl"
@@ -21,12 +22,6 @@
 	// TODO: Remove this when protoV1 registers these hooks for you.
 	_ "github.com/golang/protobuf/v2/internal/legacy"
 
-	anypb "github.com/golang/protobuf/ptypes/any"
-	durpb "github.com/golang/protobuf/ptypes/duration"
-	emptypb "github.com/golang/protobuf/ptypes/empty"
-	stpb "github.com/golang/protobuf/ptypes/struct"
-	tspb "github.com/golang/protobuf/ptypes/timestamp"
-	wpb "github.com/golang/protobuf/ptypes/wrappers"
 	"github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb2"
 	"github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb3"
 )
@@ -45,26 +40,35 @@
 	return strings.Split(s, "\n")
 })
 
+func pb2Enum(i int32) *pb2.Enum {
+	p := new(pb2.Enum)
+	*p = pb2.Enum(i)
+	return p
+}
+
+func pb2Enums_NestedEnum(i int32) *pb2.Enums_NestedEnum {
+	p := new(pb2.Enums_NestedEnum)
+	*p = pb2.Enums_NestedEnum(i)
+	return p
+}
+
 func TestMarshal(t *testing.T) {
 	tests := []struct {
 		desc    string
-		input   proto.Message
+		input   protoV1.Message
 		want    string
 		wantErr bool
 	}{{
-		desc: "nil message",
-		want: "\n",
-	}, {
 		desc:  "proto2 optional scalar fields not set",
-		input: M(&pb2.Scalars{}),
+		input: &pb2.Scalars{},
 		want:  "\n",
 	}, {
 		desc:  "proto3 scalar fields not set",
-		input: M(&pb3.Scalars{}),
+		input: &pb3.Scalars{},
 		want:  "\n",
 	}, {
 		desc: "proto2 optional scalar fields set to zero values",
-		input: M(&pb2.Scalars{
+		input: &pb2.Scalars{
 			OptBool:     scalar.Bool(false),
 			OptInt32:    scalar.Int32(0),
 			OptInt64:    scalar.Int64(0),
@@ -80,7 +84,7 @@
 			OptDouble:   scalar.Float64(0),
 			OptBytes:    []byte{},
 			OptString:   scalar.String(""),
-		}),
+		},
 		want: `opt_bool: false
 opt_int32: 0
 opt_int64: 0
@@ -99,7 +103,7 @@
 `,
 	}, {
 		desc: "proto3 scalar fields set to zero values",
-		input: M(&pb3.Scalars{
+		input: &pb3.Scalars{
 			SBool:     false,
 			SInt32:    0,
 			SInt64:    0,
@@ -115,11 +119,11 @@
 			SDouble:   0,
 			SBytes:    []byte{},
 			SString:   "",
-		}),
+		},
 		want: "\n",
 	}, {
 		desc: "proto2 optional scalar fields set to some values",
-		input: M(&pb2.Scalars{
+		input: &pb2.Scalars{
 			OptBool:     scalar.Bool(true),
 			OptInt32:    scalar.Int32(0xff),
 			OptInt64:    scalar.Int64(0xdeadbeef),
@@ -135,7 +139,7 @@
 			// TODO: Update encoder to not output UTF8 for bytes.
 			OptBytes:  []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
 			OptString: scalar.String("谷歌"),
-		}),
+		},
 		want: `opt_bool: true
 opt_int32: 255
 opt_int64: 3735928559
@@ -151,84 +155,253 @@
 opt_string: "谷歌"
 `,
 	}, {
-		desc:  "proto3 enum empty message",
-		input: M(&pb3.Enums{}),
-		want:  "\n",
-	}, {
-		desc: "proto3 enum",
-		input: M(&pb3.Enums{
-			SEnum:         pb3.Enum_ONE,
-			RptEnum:       []pb3.Enum{pb3.Enum_ONE, 10, 0, 21, -1},
-			SNestedEnum:   pb3.Enums_DIEZ,
-			RptNestedEnum: []pb3.Enums_NestedEnum{21, pb3.Enums_CERO, -7, 10},
-		}),
-		want: `s_enum: ONE
-rpt_enum: ONE
-rpt_enum: TEN
-rpt_enum: ZERO
-rpt_enum: 21
-rpt_enum: -1
-s_nested_enum: DIEZ
-rpt_nested_enum: 21
-rpt_nested_enum: CERO
-rpt_nested_enum: -7
-rpt_nested_enum: DIEZ
-`,
-	}, {
 		desc: "float32 nan",
-		input: M(&pb3.Scalars{
+		input: &pb3.Scalars{
 			SFloat: float32(math.NaN()),
-		}),
+		},
 		want: "s_float: nan\n",
 	}, {
 		desc: "float32 positive infinity",
-		input: M(&pb3.Scalars{
+		input: &pb3.Scalars{
 			SFloat: float32(math.Inf(1)),
-		}),
+		},
 		want: "s_float: inf\n",
 	}, {
 		desc: "float32 negative infinity",
-		input: M(&pb3.Scalars{
+		input: &pb3.Scalars{
 			SFloat: float32(math.Inf(-1)),
-		}),
+		},
 		want: "s_float: -inf\n",
 	}, {
 		desc: "float64 nan",
-		input: M(&pb3.Scalars{
+		input: &pb3.Scalars{
 			SDouble: math.NaN(),
-		}),
+		},
 		want: "s_double: nan\n",
 	}, {
 		desc: "float64 positive infinity",
-		input: M(&pb3.Scalars{
+		input: &pb3.Scalars{
 			SDouble: math.Inf(1),
-		}),
+		},
 		want: "s_double: inf\n",
 	}, {
 		desc: "float64 negative infinity",
-		input: M(&pb3.Scalars{
+		input: &pb3.Scalars{
 			SDouble: math.Inf(-1),
-		}),
+		},
 		want: "s_double: -inf\n",
 	}, {
 		desc: "proto2 bytes set to empty string",
-		input: M(&pb2.Scalars{
+		input: &pb2.Scalars{
 			OptBytes: []byte(""),
-		}),
+		},
 		want: "opt_bytes: \"\"\n",
 	}, {
 		desc: "proto3 bytes set to empty string",
-		input: M(&pb3.Scalars{
+		input: &pb3.Scalars{
 			SBytes: []byte(""),
-		}),
+		},
 		want: "\n",
 	}, {
-		desc:  "proto2 repeated not set",
-		input: M(&pb2.Repeats{}),
+		desc:  "proto2 enum not set",
+		input: &pb2.Enums{},
 		want:  "\n",
 	}, {
-		desc: "proto2 repeated set to empty slices",
-		input: M(&pb2.Repeats{
+		desc: "proto2 enum set to zero value",
+		input: &pb2.Enums{
+			OptEnum:       pb2.Enum_UNKNOWN.Enum(),
+			OptNestedEnum: pb2Enums_NestedEnum(0),
+		},
+		want: `opt_enum: UNKNOWN
+opt_nested_enum: 0
+`,
+	}, {
+		desc: "proto2 enum",
+		input: &pb2.Enums{
+			OptEnum:       pb2.Enum_FIRST.Enum(),
+			OptNestedEnum: pb2.Enums_UNO.Enum(),
+		},
+		want: `opt_enum: FIRST
+opt_nested_enum: UNO
+`,
+	}, {
+		desc: "proto2 enum set to numeric values",
+		input: &pb2.Enums{
+			OptEnum:       pb2Enum(1),
+			OptNestedEnum: pb2Enums_NestedEnum(2),
+		},
+		want: `opt_enum: FIRST
+opt_nested_enum: DOS
+`,
+	}, {
+		desc: "proto2 enum set to unnamed numeric values",
+		input: &pb2.Enums{
+			OptEnum:       pb2Enum(101),
+			OptNestedEnum: pb2Enums_NestedEnum(-101),
+		},
+		want: `opt_enum: 101
+opt_nested_enum: -101
+`,
+	}, {
+		desc:  "proto3 enum not set",
+		input: &pb3.Enums{},
+		want:  "\n",
+	}, {
+		desc: "proto3 enum set to zero value",
+		input: &pb3.Enums{
+			SEnum:       pb3.Enum_ZERO,
+			SNestedEnum: pb3.Enums_CERO,
+		},
+		want: "\n",
+	}, {
+		desc: "proto3 enum",
+		input: &pb3.Enums{
+			SEnum:       pb3.Enum_ONE,
+			SNestedEnum: pb3.Enums_DIEZ,
+		},
+		want: `s_enum: ONE
+s_nested_enum: DIEZ
+`,
+	}, {
+		desc: "proto3 enum set to numeric values",
+		input: &pb3.Enums{
+			SEnum:       2,
+			SNestedEnum: 1,
+		},
+		want: `s_enum: TWO
+s_nested_enum: UNO
+`,
+	}, {
+		desc: "proto3 enum set to unnamed numeric values",
+		input: &pb3.Enums{
+			SEnum:       -47,
+			SNestedEnum: 47,
+		},
+		want: `s_enum: -47
+s_nested_enum: 47
+`,
+	}, {
+		desc:  "proto2 nested message not set",
+		input: &pb2.Nests{},
+		want:  "\n",
+	}, {
+		desc: "proto2 nested message set to empty",
+		input: &pb2.Nests{
+			OptNested: &pb2.Nested{},
+			Optgroup:  &pb2.Nests_OptGroup{},
+		},
+		want: `opt_nested: {}
+optgroup: {}
+`,
+	}, {
+		desc: "proto2 nested messages",
+		input: &pb2.Nests{
+			OptNested: &pb2.Nested{
+				OptString: scalar.String("nested message"),
+				OptNested: &pb2.Nested{
+					OptString: scalar.String("another nested message"),
+				},
+			},
+		},
+		want: `opt_nested: {
+  opt_string: "nested message"
+  opt_nested: {
+    opt_string: "another nested message"
+  }
+}
+`,
+	}, {
+		desc: "proto2 group fields",
+		input: &pb2.Nests{
+			Optgroup: &pb2.Nests_OptGroup{
+				OptBool:   scalar.Bool(true),
+				OptString: scalar.String("inside a group"),
+				OptNested: &pb2.Nested{
+					OptString: scalar.String("nested message inside a group"),
+				},
+				Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
+					OptEnum: pb2.Enum_TENTH.Enum(),
+				},
+			},
+		},
+		want: `optgroup: {
+  opt_bool: true
+  opt_string: "inside a group"
+  opt_nested: {
+    opt_string: "nested message inside a group"
+  }
+  optnestedgroup: {
+    opt_enum: TENTH
+  }
+}
+`,
+	}, {
+		desc:  "proto3 nested message not set",
+		input: &pb3.Nests{},
+		want:  "\n",
+	}, {
+		desc: "proto3 nested message",
+		input: &pb3.Nests{
+			SNested: &pb3.Nested{
+				SString: "nested message",
+				SNested: &pb3.Nested{
+					SString: "another nested message",
+				},
+			},
+		},
+		want: `s_nested: {
+  s_string: "nested message"
+  s_nested: {
+    s_string: "another nested message"
+  }
+}
+`,
+	}, {
+		desc:  "oneof fields",
+		input: &pb2.Oneofs{},
+		want:  "\n",
+	}, {
+		desc: "oneof field set to empty string",
+		input: &pb2.Oneofs{
+			Union: &pb2.Oneofs_Str{},
+		},
+		want: "str: \"\"\n",
+	}, {
+		desc: "oneof field set to string",
+		input: &pb2.Oneofs{
+			Union: &pb2.Oneofs_Str{
+				Str: "hello",
+			},
+		},
+		want: "str: \"hello\"\n",
+	}, {
+		desc: "oneof field set to empty message",
+		input: &pb2.Oneofs{
+			Union: &pb2.Oneofs_Msg{
+				Msg: &pb2.Nested{},
+			},
+		},
+		want: "msg: {}\n",
+	}, {
+		desc: "oneof field set to message",
+		input: &pb2.Oneofs{
+			Union: &pb2.Oneofs_Msg{
+				Msg: &pb2.Nested{
+					OptString: scalar.String("nested message"),
+				},
+			},
+		},
+		want: `msg: {
+  opt_string: "nested message"
+}
+`,
+	}, {
+		desc:  "repeated not set",
+		input: &pb2.Repeats{},
+		want:  "\n",
+	}, {
+		desc: "repeated set to empty slices",
+		input: &pb2.Repeats{
 			RptBool:   []bool{},
 			RptInt32:  []int32{},
 			RptInt64:  []int64{},
@@ -237,11 +410,11 @@
 			RptFloat:  []float32{},
 			RptDouble: []float64{},
 			RptBytes:  [][]byte{},
-		}),
+		},
 		want: "\n",
 	}, {
-		desc: "proto2 repeated set to some values",
-		input: M(&pb2.Repeats{
+		desc: "repeated set to some values",
+		input: &pb2.Repeats{
 			RptBool:   []bool{true, false, true, true},
 			RptInt32:  []int32{1, 6, 0, 0},
 			RptInt64:  []int64{-64, 47},
@@ -254,7 +427,7 @@
 				[]byte("hello"),
 				[]byte("\xe4\xb8\x96\xe7\x95\x8c"),
 			},
-		}),
+		},
 		want: `rpt_bool: true
 rpt_bool: false
 rpt_bool: true
@@ -278,77 +451,29 @@
 rpt_bytes: "世界"
 `,
 	}, {
-		desc:  "proto2 enum fields not set",
-		input: M(&pb2.Enums{}),
-		want:  "\n",
-	}, {
-		desc: "proto2 enum fields",
-		input: M(&pb2.Enums{
-			OptEnum:       pb2.Enum_FIRST.Enum(),
+		desc: "repeated enum",
+		input: &pb2.Enums{
 			RptEnum:       []pb2.Enum{pb2.Enum_FIRST, 2, pb2.Enum_TENTH, 42},
-			OptNestedEnum: pb2.Enums_UNO.Enum(),
 			RptNestedEnum: []pb2.Enums_NestedEnum{2, 47, 10},
-		}),
-		want: `opt_enum: FIRST
-rpt_enum: FIRST
+		},
+		want: `rpt_enum: FIRST
 rpt_enum: SECOND
 rpt_enum: TENTH
 rpt_enum: 42
-opt_nested_enum: UNO
 rpt_nested_enum: DOS
 rpt_nested_enum: 47
 rpt_nested_enum: DIEZ
 `,
 	}, {
-		desc: "proto3 enum fields set to zero value",
-		input: M(&pb3.Enums{
-			SEnum:         pb3.Enum_ZERO,
-			RptEnum:       []pb3.Enum{},
-			SNestedEnum:   pb3.Enums_CERO,
-			RptNestedEnum: []pb3.Enums_NestedEnum{},
-		}),
-		want: "\n",
-	}, {
-		desc: "proto3 enum fields",
-		input: M(&pb3.Enums{
-			SEnum:         pb3.Enum_TWO,
-			RptEnum:       []pb3.Enum{1, 0, 0},
-			SNestedEnum:   pb3.Enums_DOS,
-			RptNestedEnum: []pb3.Enums_NestedEnum{101, pb3.Enums_DIEZ, 10},
-		}),
-		want: `s_enum: TWO
-rpt_enum: ONE
-rpt_enum: ZERO
-rpt_enum: ZERO
-s_nested_enum: DOS
-rpt_nested_enum: 101
-rpt_nested_enum: DIEZ
-rpt_nested_enum: DIEZ
-`,
-	}, {
-		desc:  "proto2 nested message not set",
-		input: M(&pb2.Nests{}),
-		want:  "\n",
-	}, {
-		desc: "proto2 nested message set to empty",
-		input: M(&pb2.Nests{
-			OptNested: &pb2.Nested{},
-			Optgroup:  &pb2.Nests_OptGroup{},
+		desc: "repeated nested message set to empty",
+		input: &pb2.Nests{
 			RptNested: []*pb2.Nested{},
 			Rptgroup:  []*pb2.Nests_RptGroup{},
-		}),
-		want: `opt_nested: {}
-optgroup: {}
-`,
+		},
+		want: "\n",
 	}, {
-		desc: "proto2 nested messages",
-		input: M(&pb2.Nests{
-			OptNested: &pb2.Nested{
-				OptString: scalar.String("nested message"),
-				OptNested: &pb2.Nested{
-					OptString: scalar.String("another nested message"),
-				},
-			},
+		desc: "repeated nested messages",
+		input: &pb2.Nests{
 			RptNested: []*pb2.Nested{
 				{
 					OptString: scalar.String("repeat nested one"),
@@ -361,14 +486,8 @@
 				},
 				{},
 			},
-		}),
-		want: `opt_nested: {
-  opt_string: "nested message"
-  opt_nested: {
-    opt_string: "another nested message"
-  }
-}
-rpt_nested: {
+		},
+		want: `rpt_nested: {
   opt_string: "repeat nested one"
 }
 rpt_nested: {
@@ -380,191 +499,39 @@
 rpt_nested: {}
 `,
 	}, {
-		desc: "proto2 group fields",
-		input: M(&pb2.Nests{
-			Optgroup: &pb2.Nests_OptGroup{
-				OptBool:   scalar.Bool(true),
-				OptString: scalar.String("inside a group"),
-				OptNested: &pb2.Nested{
-					OptString: scalar.String("nested message inside a group"),
-				},
-				Optnestedgroup: &pb2.Nests_OptGroup_OptNestedGroup{
-					OptEnum: pb2.Enum_TENTH.Enum(),
-				},
-			},
+		desc: "repeated group fields",
+		input: &pb2.Nests{
 			Rptgroup: []*pb2.Nests_RptGroup{
 				{
 					RptBool: []bool{true, false},
 				},
 				{},
 			},
-		}),
-		want: `optgroup: {
-  opt_bool: true
-  opt_string: "inside a group"
-  opt_nested: {
-    opt_string: "nested message inside a group"
-  }
-  optnestedgroup: {
-    opt_enum: TENTH
-  }
-}
-rptgroup: {
+		},
+		want: `rptgroup: {
   rpt_bool: true
   rpt_bool: false
 }
 rptgroup: {}
 `,
 	}, {
-		desc:  "proto3 nested message not set",
-		input: M(&pb3.Nests{}),
-		want:  "\n",
-	}, {
-		desc: "proto3 nested message",
-		input: M(&pb3.Nests{
-			SNested: &pb3.Nested{
-				SString: "nested message",
-				SNested: &pb3.Nested{
-					SString: "another nested message",
-				},
-			},
-			RptNested: []*pb3.Nested{
-				{
-					SString: "repeated nested one",
-					SNested: &pb3.Nested{
-						SString: "inside repeated nested one",
-					},
-				},
-				{
-					SString: "repeated nested two",
-				},
-				{},
-			},
-		}),
-		want: `s_nested: {
-  s_string: "nested message"
-  s_nested: {
-    s_string: "another nested message"
-  }
-}
-rpt_nested: {
-  s_string: "repeated nested one"
-  s_nested: {
-    s_string: "inside repeated nested one"
-  }
-}
-rpt_nested: {
-  s_string: "repeated nested two"
-}
-rpt_nested: {}
-`,
-	}, {
-		desc:    "proto2 required fields not set",
-		input:   M(&pb2.Requireds{}),
-		want:    "\n",
-		wantErr: true,
-	}, {
-		desc: "proto2 required fields partially set",
-		input: M(&pb2.Requireds{
-			ReqBool:     scalar.Bool(false),
-			ReqFixed32:  scalar.Uint32(47),
-			ReqSfixed64: scalar.Int64(0xbeefcafe),
-			ReqDouble:   scalar.Float64(math.NaN()),
-			ReqString:   scalar.String("hello"),
-			ReqEnum:     pb2.Enum_FIRST.Enum(),
-		}),
-		want: `req_bool: false
-req_fixed32: 47
-req_sfixed64: 3203386110
-req_double: nan
-req_string: "hello"
-req_enum: FIRST
-`,
-		wantErr: true,
-	}, {
-		desc: "proto2 required fields all set",
-		input: M(&pb2.Requireds{
-			ReqBool:     scalar.Bool(false),
-			ReqFixed32:  scalar.Uint32(0),
-			ReqFixed64:  scalar.Uint64(0),
-			ReqSfixed32: scalar.Int32(0),
-			ReqSfixed64: scalar.Int64(0),
-			ReqFloat:    scalar.Float32(0),
-			ReqDouble:   scalar.Float64(0),
-			ReqString:   scalar.String(""),
-			ReqEnum:     pb2.Enum_UNKNOWN.Enum(),
-			ReqBytes:    []byte{},
-			ReqNested:   &pb2.Nested{},
-		}),
-		want: `req_bool: false
-req_fixed32: 0
-req_fixed64: 0
-req_sfixed32: 0
-req_sfixed64: 0
-req_float: 0
-req_double: 0
-req_string: ""
-req_bytes: ""
-req_enum: UNKNOWN
-req_nested: {}
-`,
-	}, {
-		desc:  "oneof fields",
-		input: M(&pb2.Oneofs{}),
-		want:  "\n",
-	}, {
-		desc: "oneof field set to empty string",
-		input: M(&pb2.Oneofs{
-			Union: &pb2.Oneofs_Str{},
-		}),
-		want: "str: \"\"\n",
-	}, {
-		desc: "oneof field set to string",
-		input: M(&pb2.Oneofs{
-			Union: &pb2.Oneofs_Str{
-				Str: "hello",
-			},
-		}),
-		want: "str: \"hello\"\n",
-	}, {
-		desc: "oneof field set to empty message",
-		input: M(&pb2.Oneofs{
-			Union: &pb2.Oneofs_Msg{
-				Msg: &pb2.Nested{},
-			},
-		}),
-		want: "msg: {}\n",
-	}, {
-		desc: "oneof field set to message",
-		input: M(&pb2.Oneofs{
-			Union: &pb2.Oneofs_Msg{
-				Msg: &pb2.Nested{
-					OptString: scalar.String("nested message"),
-				},
-			},
-		}),
-		want: `msg: {
-  opt_string: "nested message"
-}
-`,
-	}, {
 		desc:  "map fields empty",
-		input: M(&pb2.Maps{}),
+		input: &pb2.Maps{},
 		want:  "\n",
 	}, {
 		desc: "map fields set to empty maps",
-		input: M(&pb2.Maps{
+		input: &pb2.Maps{
 			Int32ToStr:     map[int32]string{},
 			Sfixed64ToBool: map[int64]bool{},
 			BoolToUint32:   map[bool]uint32{},
 			Uint64ToEnum:   map[uint64]pb2.Enum{},
 			StrToNested:    map[string]*pb2.Nested{},
 			StrToOneofs:    map[string]*pb2.Oneofs{},
-		}),
+		},
 		want: "\n",
 	}, {
 		desc: "map fields 1",
-		input: M(&pb2.Maps{
+		input: &pb2.Maps{
 			Int32ToStr: map[int32]string{
 				-101: "-101",
 				0xff: "0xff",
@@ -578,7 +545,7 @@
 				true:  42,
 				false: 101,
 			},
-		}),
+		},
 		want: `int32_to_str: {
   key: -101
   value: "-101"
@@ -610,13 +577,13 @@
 `,
 	}, {
 		desc: "map fields 2",
-		input: M(&pb2.Maps{
+		input: &pb2.Maps{
 			Uint64ToEnum: map[uint64]pb2.Enum{
 				1:  pb2.Enum_FIRST,
 				2:  pb2.Enum_SECOND,
 				10: pb2.Enum_TENTH,
 			},
-		}),
+		},
 		want: `uint64_to_enum: {
   key: 1
   value: FIRST
@@ -632,13 +599,13 @@
 `,
 	}, {
 		desc: "map fields 3",
-		input: M(&pb2.Maps{
+		input: &pb2.Maps{
 			StrToNested: map[string]*pb2.Nested{
 				"nested_one": &pb2.Nested{
 					OptString: scalar.String("nested in a map"),
 				},
 			},
-		}),
+		},
 		want: `str_to_nested: {
   key: "nested_one"
   value: {
@@ -648,7 +615,7 @@
 `,
 	}, {
 		desc: "map fields 4",
-		input: M(&pb2.Maps{
+		input: &pb2.Maps{
 			StrToOneofs: map[string]*pb2.Oneofs{
 				"string": &pb2.Oneofs{
 					Union: &pb2.Oneofs_Str{
@@ -663,7 +630,7 @@
 					},
 				},
 			},
-		}),
+		},
 		want: `str_to_oneofs: {
   key: "nested"
   value: {
@@ -680,255 +647,113 @@
 }
 `,
 	}, {
-		desc:  "well-known type fields not set",
-		input: M(&pb2.KnownTypes{}),
-		want:  "\n",
+		desc:    "proto2 required fields not set",
+		input:   &pb2.Requireds{},
+		want:    "\n",
+		wantErr: true,
 	}, {
-		desc: "well-known type fields set to empty messages",
-		input: M(&pb2.KnownTypes{
-			OptBool:      &wpb.BoolValue{},
-			OptInt32:     &wpb.Int32Value{},
-			OptInt64:     &wpb.Int64Value{},
-			OptUint32:    &wpb.UInt32Value{},
-			OptUint64:    &wpb.UInt64Value{},
-			OptFloat:     &wpb.FloatValue{},
-			OptDouble:    &wpb.DoubleValue{},
-			OptString:    &wpb.StringValue{},
-			OptBytes:     &wpb.BytesValue{},
-			OptDuration:  &durpb.Duration{},
-			OptTimestamp: &tspb.Timestamp{},
-			OptStruct:    &stpb.Struct{},
-			OptList:      &stpb.ListValue{},
-			OptValue:     &stpb.Value{},
-			OptEmpty:     &emptypb.Empty{},
-			OptAny:       &anypb.Any{},
-		}),
-		want: `opt_bool: {}
-opt_int32: {}
-opt_int64: {}
-opt_uint32: {}
-opt_uint64: {}
-opt_float: {}
-opt_double: {}
-opt_string: {}
-opt_bytes: {}
-opt_duration: {}
-opt_timestamp: {}
-opt_struct: {}
-opt_list: {}
-opt_value: {}
-opt_empty: {}
-opt_any: {}
+		desc: "proto2 required fields partially set",
+		input: &pb2.Requireds{
+			ReqBool:     scalar.Bool(false),
+			ReqFixed32:  scalar.Uint32(47),
+			ReqSfixed64: scalar.Int64(0xbeefcafe),
+			ReqDouble:   scalar.Float64(math.NaN()),
+			ReqString:   scalar.String("hello"),
+			ReqEnum:     pb2.Enum_FIRST.Enum(),
+		},
+		want: `req_bool: false
+req_fixed32: 47
+req_sfixed64: 3203386110
+req_double: nan
+req_string: "hello"
+req_enum: FIRST
+`,
+		wantErr: true,
+	}, {
+		desc: "proto2 required fields all set",
+		input: &pb2.Requireds{
+			ReqBool:     scalar.Bool(false),
+			ReqFixed32:  scalar.Uint32(0),
+			ReqFixed64:  scalar.Uint64(0),
+			ReqSfixed32: scalar.Int32(0),
+			ReqSfixed64: scalar.Int64(0),
+			ReqFloat:    scalar.Float32(0),
+			ReqDouble:   scalar.Float64(0),
+			ReqString:   scalar.String(""),
+			ReqEnum:     pb2.Enum_UNKNOWN.Enum(),
+			ReqBytes:    []byte{},
+			ReqNested:   &pb2.Nested{},
+		},
+		want: `req_bool: false
+req_fixed32: 0
+req_fixed64: 0
+req_sfixed32: 0
+req_sfixed64: 0
+req_float: 0
+req_double: 0
+req_string: ""
+req_bytes: ""
+req_enum: UNKNOWN
+req_nested: {}
 `,
 	}, {
-		desc: "well-known type scalar fields",
-		input: M(&pb2.KnownTypes{
-			OptBool: &wpb.BoolValue{
-				Value: true,
-			},
-			OptInt32: &wpb.Int32Value{
-				Value: -42,
-			},
-			OptInt64: &wpb.Int64Value{
-				Value: -42,
-			},
-			OptUint32: &wpb.UInt32Value{
-				Value: 0xff,
-			},
-			OptUint64: &wpb.UInt64Value{
-				Value: 0xffff,
-			},
-			OptFloat: &wpb.FloatValue{
-				Value: 1.234,
-			},
-			OptDouble: &wpb.DoubleValue{
-				Value: 1.23e308,
-			},
-			OptString: &wpb.StringValue{
-				Value: "谷歌",
-			},
-			OptBytes: &wpb.BytesValue{
-				Value: []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
-			},
-		}),
-		want: `opt_bool: {
-  value: true
-}
-opt_int32: {
-  value: -42
-}
-opt_int64: {
-  value: -42
-}
-opt_uint32: {
-  value: 255
-}
-opt_uint64: {
-  value: 65535
-}
-opt_float: {
-  value: 1.2339999675750732
-}
-opt_double: {
-  value: 1.23e+308
-}
-opt_string: {
-  value: "谷歌"
-}
-opt_bytes: {
-  value: "谷歌"
-}
-`,
+		desc: "indirect required field",
+		input: &pb2.IndirectRequired{
+			OptNested: &pb2.NestedWithRequired{},
+		},
+		want:    "opt_nested: {}\n",
+		wantErr: true,
 	}, {
-		desc: "well-known type time-related fields",
-		input: M(&pb2.KnownTypes{
-			OptDuration: &durpb.Duration{
-				Seconds: -3600,
-				Nanos:   -123,
-			},
-			OptTimestamp: &tspb.Timestamp{
-				Seconds: 1257894000,
-				Nanos:   123,
-			},
-		}),
-		want: `opt_duration: {
-  seconds: -3600
-  nanos: -123
-}
-opt_timestamp: {
-  seconds: 1257894000
-  nanos: 123
-}
-`,
+		desc: "indirect required field in empty repeated",
+		input: &pb2.IndirectRequired{
+			RptNested: []*pb2.NestedWithRequired{},
+		},
+		want: "\n",
 	}, {
-		desc: "well-known type struct field and different Value types",
-		input: M(&pb2.KnownTypes{
-			OptStruct: &stpb.Struct{
-				Fields: map[string]*stpb.Value{
-					"bool": &stpb.Value{
-						Kind: &stpb.Value_BoolValue{
-							BoolValue: true,
-						},
-					},
-					"double": &stpb.Value{
-						Kind: &stpb.Value_NumberValue{
-							NumberValue: 3.1415,
-						},
-					},
-					"null": &stpb.Value{
-						Kind: &stpb.Value_NullValue{
-							NullValue: stpb.NullValue_NULL_VALUE,
-						},
-					},
-					"string": &stpb.Value{
-						Kind: &stpb.Value_StringValue{
-							StringValue: "string",
-						},
-					},
-					"struct": &stpb.Value{
-						Kind: &stpb.Value_StructValue{
-							StructValue: &stpb.Struct{
-								Fields: map[string]*stpb.Value{
-									"bool": &stpb.Value{
-										Kind: &stpb.Value_BoolValue{
-											BoolValue: false,
-										},
-									},
-								},
-							},
-						},
-					},
-					"list": &stpb.Value{
-						Kind: &stpb.Value_ListValue{
-							ListValue: &stpb.ListValue{
-								Values: []*stpb.Value{
-									{
-										Kind: &stpb.Value_BoolValue{
-											BoolValue: false,
-										},
-									},
-									{
-										Kind: &stpb.Value_StringValue{
-											StringValue: "hello",
-										},
-									},
-								},
-							},
-						},
-					},
-				},
+		desc: "indirect required field in repeated",
+		input: &pb2.IndirectRequired{
+			RptNested: []*pb2.NestedWithRequired{
+				&pb2.NestedWithRequired{},
 			},
-		}),
-		want: `opt_struct: {
-  fields: {
-    key: "bool"
-    value: {
-      bool_value: true
-    }
-  }
-  fields: {
-    key: "double"
-    value: {
-      number_value: 3.1415
-    }
-  }
-  fields: {
-    key: "list"
-    value: {
-      list_value: {
-        values: {
-          bool_value: false
-        }
-        values: {
-          string_value: "hello"
-        }
-      }
-    }
-  }
-  fields: {
-    key: "null"
-    value: {
-      null_value: NULL_VALUE
-    }
-  }
-  fields: {
-    key: "string"
-    value: {
-      string_value: "string"
-    }
-  }
-  fields: {
-    key: "struct"
-    value: {
-      struct_value: {
-        fields: {
-          key: "bool"
-          value: {
-            bool_value: false
-          }
-        }
-      }
-    }
-  }
+		},
+		want:    "rpt_nested: {}\n",
+		wantErr: true,
+	}, {
+		desc: "indirect required field in empty map",
+		input: &pb2.IndirectRequired{
+			StrToNested: map[string]*pb2.NestedWithRequired{},
+		},
+		want: "\n",
+	}, {
+		desc: "indirect required field in map",
+		input: &pb2.IndirectRequired{
+			StrToNested: map[string]*pb2.NestedWithRequired{
+				"fail": &pb2.NestedWithRequired{},
+			},
+		},
+		want: `str_to_nested: {
+  key: "fail"
+  value: {}
 }
 `,
+		wantErr: true,
 	}}
 
 	for _, tt := range tests {
 		tt := tt
 		t.Run(tt.desc, func(t *testing.T) {
 			t.Parallel()
-			want := tt.want
-			b, err := textpb.Marshal(tt.input)
+			b, err := textpb.Marshal(M(tt.input))
 			if err != nil && !tt.wantErr {
 				t.Errorf("Marshal() returned error: %v\n\n", err)
 			}
-			if tt.wantErr && err == nil {
-				t.Errorf("Marshal() got nil error, want error\n\n")
+			if err == nil && tt.wantErr {
+				t.Error("Marshal() got nil error, want error\n\n")
 			}
-			if got := string(b); got != want {
-				t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, want)
-				if diff := cmp.Diff(want, got, splitLines); diff != "" {
+			got := string(b)
+			if tt.want != "" && got != tt.want {
+				t.Errorf("Marshal()\n<got>\n%v\n<want>\n%v\n", got, tt.want)
+				if diff := cmp.Diff(tt.want, got, splitLines); diff != "" {
 					t.Errorf("Marshal() diff -want +got\n%v\n", diff)
 				}
 			}
diff --git a/encoding/textpb/other_test.go b/encoding/textpb/other_test.go
new file mode 100644
index 0000000..4dc79cb
--- /dev/null
+++ b/encoding/textpb/other_test.go
@@ -0,0 +1,166 @@
+package textpb_test
+
+import (
+	"testing"
+
+	protoV1 "github.com/golang/protobuf/proto"
+	"github.com/golang/protobuf/v2/encoding/textpb"
+	"github.com/golang/protobuf/v2/encoding/textpb/testprotos/pb2"
+
+	// The legacy package must be imported prior to use of any legacy messages.
+	// TODO: Remove this when protoV1 registers these hooks for you.
+	_ "github.com/golang/protobuf/v2/internal/legacy"
+
+	anypb "github.com/golang/protobuf/ptypes/any"
+	durpb "github.com/golang/protobuf/ptypes/duration"
+	emptypb "github.com/golang/protobuf/ptypes/empty"
+	stpb "github.com/golang/protobuf/ptypes/struct"
+	tspb "github.com/golang/protobuf/ptypes/timestamp"
+	wpb "github.com/golang/protobuf/ptypes/wrappers"
+)
+
+func TestRoundTrip(t *testing.T) {
+	tests := []struct {
+		desc    string
+		message protoV1.Message
+	}{{
+		desc: "well-known type fields set to empty messages",
+		message: &pb2.KnownTypes{
+			OptBool:      &wpb.BoolValue{},
+			OptInt32:     &wpb.Int32Value{},
+			OptInt64:     &wpb.Int64Value{},
+			OptUint32:    &wpb.UInt32Value{},
+			OptUint64:    &wpb.UInt64Value{},
+			OptFloat:     &wpb.FloatValue{},
+			OptDouble:    &wpb.DoubleValue{},
+			OptString:    &wpb.StringValue{},
+			OptBytes:     &wpb.BytesValue{},
+			OptDuration:  &durpb.Duration{},
+			OptTimestamp: &tspb.Timestamp{},
+			OptStruct:    &stpb.Struct{},
+			OptList:      &stpb.ListValue{},
+			OptValue:     &stpb.Value{},
+			OptEmpty:     &emptypb.Empty{},
+			OptAny:       &anypb.Any{},
+		},
+	}, {
+		desc: "well-known type scalar fields",
+		message: &pb2.KnownTypes{
+			OptBool: &wpb.BoolValue{
+				Value: true,
+			},
+			OptInt32: &wpb.Int32Value{
+				Value: -42,
+			},
+			OptInt64: &wpb.Int64Value{
+				Value: -42,
+			},
+			OptUint32: &wpb.UInt32Value{
+				Value: 0xff,
+			},
+			OptUint64: &wpb.UInt64Value{
+				Value: 0xffff,
+			},
+			OptFloat: &wpb.FloatValue{
+				Value: 1.234,
+			},
+			OptDouble: &wpb.DoubleValue{
+				Value: 1.23e308,
+			},
+			OptString: &wpb.StringValue{
+				Value: "谷歌",
+			},
+			OptBytes: &wpb.BytesValue{
+				Value: []byte("\xe8\xb0\xb7\xe6\xad\x8c"),
+			},
+		},
+	}, {
+		desc: "well-known type time-related fields",
+		message: &pb2.KnownTypes{
+			OptDuration: &durpb.Duration{
+				Seconds: -3600,
+				Nanos:   -123,
+			},
+			OptTimestamp: &tspb.Timestamp{
+				Seconds: 1257894000,
+				Nanos:   123,
+			},
+		},
+	}, {
+		desc: "well-known type struct field and different Value types",
+		message: &pb2.KnownTypes{
+			OptStruct: &stpb.Struct{
+				Fields: map[string]*stpb.Value{
+					"bool": &stpb.Value{
+						Kind: &stpb.Value_BoolValue{
+							BoolValue: true,
+						},
+					},
+					"double": &stpb.Value{
+						Kind: &stpb.Value_NumberValue{
+							NumberValue: 3.1415,
+						},
+					},
+					"null": &stpb.Value{
+						Kind: &stpb.Value_NullValue{
+							NullValue: stpb.NullValue_NULL_VALUE,
+						},
+					},
+					"string": &stpb.Value{
+						Kind: &stpb.Value_StringValue{
+							StringValue: "string",
+						},
+					},
+					"struct": &stpb.Value{
+						Kind: &stpb.Value_StructValue{
+							StructValue: &stpb.Struct{
+								Fields: map[string]*stpb.Value{
+									"bool": &stpb.Value{
+										Kind: &stpb.Value_BoolValue{
+											BoolValue: false,
+										},
+									},
+								},
+							},
+						},
+					},
+					"list": &stpb.Value{
+						Kind: &stpb.Value_ListValue{
+							ListValue: &stpb.ListValue{
+								Values: []*stpb.Value{
+									{
+										Kind: &stpb.Value_BoolValue{
+											BoolValue: false,
+										},
+									},
+									{
+										Kind: &stpb.Value_StringValue{
+											StringValue: "hello",
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}}
+
+	for _, tt := range tests {
+		tt := tt
+		t.Run(tt.desc, func(t *testing.T) {
+			t.Parallel()
+			b, err := textpb.Marshal(M(tt.message))
+			if err != nil {
+				t.Errorf("Marshal() returned error: %v\n\n", err)
+			}
+			want := protoV1.Clone(tt.message)
+			want.Reset()
+			err = textpb.Unmarshal(M(want), b)
+			if err != nil {
+				t.Errorf("Unmarshal() returned error: %v\n\n", err)
+			}
+		})
+	}
+}
diff --git a/encoding/textpb/testprotos/pb2/test.pb.go b/encoding/textpb/testprotos/pb2/test.pb.go
index e06f5f3..d7e7342 100644
--- a/encoding/textpb/testprotos/pb2/test.pb.go
+++ b/encoding/textpb/testprotos/pb2/test.pb.go
@@ -808,6 +808,54 @@
 	return nil
 }
 
+// Message contains both required and optional fields.
+type PartialRequired struct {
+	ReqString            *string  `protobuf:"bytes,1,req,name=req_string,json=reqString" json:"req_string,omitempty"`
+	OptString            *string  `protobuf:"bytes,2,opt,name=opt_string,json=optString" json:"opt_string,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *PartialRequired) Reset()         { *m = PartialRequired{} }
+func (m *PartialRequired) String() string { return proto.CompactTextString(m) }
+func (*PartialRequired) ProtoMessage()    {}
+func (*PartialRequired) Descriptor() ([]byte, []int) {
+	return fileDescriptor_c8d7acc1bcec9a72, []int{6}
+}
+
+func (m *PartialRequired) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_PartialRequired.Unmarshal(m, b)
+}
+func (m *PartialRequired) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_PartialRequired.Marshal(b, m, deterministic)
+}
+func (m *PartialRequired) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_PartialRequired.Merge(m, src)
+}
+func (m *PartialRequired) XXX_Size() int {
+	return xxx_messageInfo_PartialRequired.Size(m)
+}
+func (m *PartialRequired) XXX_DiscardUnknown() {
+	xxx_messageInfo_PartialRequired.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_PartialRequired proto.InternalMessageInfo
+
+func (m *PartialRequired) GetReqString() string {
+	if m != nil && m.ReqString != nil {
+		return *m.ReqString
+	}
+	return ""
+}
+
+func (m *PartialRequired) GetOptString() string {
+	if m != nil && m.OptString != nil {
+		return *m.OptString
+	}
+	return ""
+}
+
 // Message contains oneof field.
 type Oneofs struct {
 	// Types that are valid to be assigned to Union:
@@ -823,7 +871,7 @@
 func (m *Oneofs) String() string { return proto.CompactTextString(m) }
 func (*Oneofs) ProtoMessage()    {}
 func (*Oneofs) Descriptor() ([]byte, []int) {
-	return fileDescriptor_c8d7acc1bcec9a72, []int{6}
+	return fileDescriptor_c8d7acc1bcec9a72, []int{7}
 }
 
 func (m *Oneofs) XXX_Unmarshal(b []byte) error {
@@ -968,7 +1016,7 @@
 func (m *Maps) String() string { return proto.CompactTextString(m) }
 func (*Maps) ProtoMessage()    {}
 func (*Maps) Descriptor() ([]byte, []int) {
-	return fileDescriptor_c8d7acc1bcec9a72, []int{7}
+	return fileDescriptor_c8d7acc1bcec9a72, []int{8}
 }
 
 func (m *Maps) XXX_Unmarshal(b []byte) error {
@@ -1031,6 +1079,101 @@
 	return nil
 }
 
+// Following messages are for testing required field nested in optional, repeated and map fields.
+type NestedWithRequired struct {
+	ReqString            *string  `protobuf:"bytes,1,req,name=req_string,json=reqString" json:"req_string,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *NestedWithRequired) Reset()         { *m = NestedWithRequired{} }
+func (m *NestedWithRequired) String() string { return proto.CompactTextString(m) }
+func (*NestedWithRequired) ProtoMessage()    {}
+func (*NestedWithRequired) Descriptor() ([]byte, []int) {
+	return fileDescriptor_c8d7acc1bcec9a72, []int{9}
+}
+
+func (m *NestedWithRequired) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_NestedWithRequired.Unmarshal(m, b)
+}
+func (m *NestedWithRequired) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_NestedWithRequired.Marshal(b, m, deterministic)
+}
+func (m *NestedWithRequired) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_NestedWithRequired.Merge(m, src)
+}
+func (m *NestedWithRequired) XXX_Size() int {
+	return xxx_messageInfo_NestedWithRequired.Size(m)
+}
+func (m *NestedWithRequired) XXX_DiscardUnknown() {
+	xxx_messageInfo_NestedWithRequired.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_NestedWithRequired proto.InternalMessageInfo
+
+func (m *NestedWithRequired) GetReqString() string {
+	if m != nil && m.ReqString != nil {
+		return *m.ReqString
+	}
+	return ""
+}
+
+type IndirectRequired struct {
+	OptNested            *NestedWithRequired            `protobuf:"bytes,1,opt,name=opt_nested,json=optNested" json:"opt_nested,omitempty"`
+	RptNested            []*NestedWithRequired          `protobuf:"bytes,2,rep,name=rpt_nested,json=rptNested" json:"rpt_nested,omitempty"`
+	StrToNested          map[string]*NestedWithRequired `protobuf:"bytes,3,rep,name=str_to_nested,json=strToNested" json:"str_to_nested,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+	XXX_NoUnkeyedLiteral struct{}                       `json:"-"`
+	XXX_unrecognized     []byte                         `json:"-"`
+	XXX_sizecache        int32                          `json:"-"`
+}
+
+func (m *IndirectRequired) Reset()         { *m = IndirectRequired{} }
+func (m *IndirectRequired) String() string { return proto.CompactTextString(m) }
+func (*IndirectRequired) ProtoMessage()    {}
+func (*IndirectRequired) Descriptor() ([]byte, []int) {
+	return fileDescriptor_c8d7acc1bcec9a72, []int{10}
+}
+
+func (m *IndirectRequired) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_IndirectRequired.Unmarshal(m, b)
+}
+func (m *IndirectRequired) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_IndirectRequired.Marshal(b, m, deterministic)
+}
+func (m *IndirectRequired) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_IndirectRequired.Merge(m, src)
+}
+func (m *IndirectRequired) XXX_Size() int {
+	return xxx_messageInfo_IndirectRequired.Size(m)
+}
+func (m *IndirectRequired) XXX_DiscardUnknown() {
+	xxx_messageInfo_IndirectRequired.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_IndirectRequired proto.InternalMessageInfo
+
+func (m *IndirectRequired) GetOptNested() *NestedWithRequired {
+	if m != nil {
+		return m.OptNested
+	}
+	return nil
+}
+
+func (m *IndirectRequired) GetRptNested() []*NestedWithRequired {
+	if m != nil {
+		return m.RptNested
+	}
+	return nil
+}
+
+func (m *IndirectRequired) GetStrToNested() map[string]*NestedWithRequired {
+	if m != nil {
+		return m.StrToNested
+	}
+	return nil
+}
+
 // Message contains well-known type fields.
 type KnownTypes struct {
 	OptBool              *wrappers.BoolValue   `protobuf:"bytes,1,opt,name=opt_bool,json=optBool" json:"opt_bool,omitempty"`
@@ -1058,7 +1201,7 @@
 func (m *KnownTypes) String() string { return proto.CompactTextString(m) }
 func (*KnownTypes) ProtoMessage()    {}
 func (*KnownTypes) Descriptor() ([]byte, []int) {
-	return fileDescriptor_c8d7acc1bcec9a72, []int{8}
+	return fileDescriptor_c8d7acc1bcec9a72, []int{11}
 }
 
 func (m *KnownTypes) XXX_Unmarshal(b []byte) error {
@@ -1203,6 +1346,7 @@
 	proto.RegisterType((*Nests_RptGroup)(nil), "pb2.Nests.RptGroup")
 	proto.RegisterType((*Nested)(nil), "pb2.Nested")
 	proto.RegisterType((*Requireds)(nil), "pb2.Requireds")
+	proto.RegisterType((*PartialRequired)(nil), "pb2.PartialRequired")
 	proto.RegisterType((*Oneofs)(nil), "pb2.Oneofs")
 	proto.RegisterType((*Maps)(nil), "pb2.Maps")
 	proto.RegisterMapType((map[bool]uint32)(nil), "pb2.Maps.BoolToUint32Entry")
@@ -1211,6 +1355,9 @@
 	proto.RegisterMapType((map[string]*Nested)(nil), "pb2.Maps.StrToNestedEntry")
 	proto.RegisterMapType((map[string]*Oneofs)(nil), "pb2.Maps.StrToOneofsEntry")
 	proto.RegisterMapType((map[uint64]Enum)(nil), "pb2.Maps.Uint64ToEnumEntry")
+	proto.RegisterType((*NestedWithRequired)(nil), "pb2.NestedWithRequired")
+	proto.RegisterType((*IndirectRequired)(nil), "pb2.IndirectRequired")
+	proto.RegisterMapType((map[string]*NestedWithRequired)(nil), "pb2.IndirectRequired.StrToNestedEntry")
 	proto.RegisterType((*KnownTypes)(nil), "pb2.KnownTypes")
 }
 
@@ -1219,101 +1366,106 @@
 }
 
 var fileDescriptor_c8d7acc1bcec9a72 = []byte{
-	// 1521 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x58, 0xdb, 0x6e, 0xdb, 0x46,
-	0x10, 0x0d, 0x49, 0x5d, 0x57, 0xbe, 0x28, 0x4c, 0xd2, 0xd2, 0x72, 0x12, 0x33, 0x42, 0x0a, 0xb0,
-	0x01, 0x2a, 0x01, 0x8a, 0x22, 0x18, 0x75, 0x2e, 0xb0, 0x6b, 0x3b, 0xb7, 0xd6, 0x06, 0x28, 0xb9,
-	0x05, 0xf2, 0x62, 0x48, 0xf2, 0x5a, 0x15, 0x2a, 0x71, 0xa9, 0xe5, 0x32, 0x89, 0xfe, 0xa3, 0xdf,
-	0xd0, 0x9f, 0xe8, 0x9f, 0xf4, 0xa5, 0x3f, 0xd1, 0x87, 0x02, 0x7d, 0x29, 0x66, 0x96, 0x97, 0x15,
-	0x25, 0x19, 0x79, 0xd3, 0xee, 0xcc, 0x39, 0xb3, 0x33, 0xb3, 0x7b, 0x76, 0x29, 0xf2, 0x2d, 0xf5,
-	0x86, 0xec, 0x6a, 0xec, 0x8d, 0x9a, 0x82, 0x7e, 0x16, 0xfe, 0xa0, 0x29, 0x68, 0x20, 0x7c, 0xce,
-	0x04, 0x0b, 0x9a, 0xfe, 0xa0, 0x85, 0xc3, 0x06, 0x8e, 0x4d, 0xc3, 0x1f, 0xb4, 0x6a, 0x3b, 0x23,
-	0xc6, 0x46, 0x13, 0xda, 0xc4, 0xa9, 0x41, 0x78, 0xdd, 0xec, 0x7b, 0x73, 0x69, 0xaf, 0xed, 0x66,
-	0x4d, 0x74, 0xea, 0x8b, 0xd8, 0xf8, 0x30, 0x6b, 0xbc, 0x0a, 0x79, 0x5f, 0x8c, 0x99, 0x17, 0xd9,
-	0xef, 0x67, 0xed, 0x81, 0xe0, 0xe1, 0x30, 0x0a, 0x5d, 0xdb, 0xcb, 0x5a, 0xc5, 0x78, 0x4a, 0x03,
-	0xd1, 0x9f, 0xfa, 0xeb, 0xe8, 0x3f, 0xf1, 0xbe, 0xef, 0x53, 0x1e, 0x48, 0x7b, 0xfd, 0x2f, 0x83,
-	0x14, 0xbb, 0xc3, 0xfe, 0xa4, 0xcf, 0x03, 0x73, 0x87, 0x94, 0x98, 0x2f, 0x2e, 0x07, 0x8c, 0x4d,
-	0x2c, 0xcd, 0xd6, 0x9c, 0x92, 0x5b, 0x64, 0xbe, 0x38, 0x62, 0x6c, 0x62, 0xee, 0x92, 0x32, 0x98,
-	0xc6, 0x9e, 0x78, 0xda, 0xb2, 0x74, 0x5b, 0x73, 0xf2, 0x2e, 0xf8, 0xbe, 0x85, 0xb1, 0x62, 0xec,
-	0xb4, 0x2d, 0xc3, 0xd6, 0x1c, 0x23, 0x36, 0x76, 0xda, 0xe6, 0x03, 0x42, 0xc0, 0x18, 0x4a, 0x68,
-	0xce, 0xd6, 0x9c, 0x4d, 0x17, 0xdc, 0x2f, 0x70, 0x42, 0x35, 0x77, 0xda, 0x56, 0xde, 0xd6, 0x9c,
-	0x5c, 0x62, 0x4e, 0xd1, 0x81, 0x44, 0x17, 0x6c, 0xcd, 0xb9, 0x8d, 0xe6, 0xee, 0x02, 0x3a, 0x90,
-	0xe8, 0xa2, 0xad, 0x39, 0x66, 0x62, 0xee, 0xb4, 0xcd, 0x3d, 0x52, 0x01, 0xf3, 0xf5, 0xf8, 0x33,
-	0xbd, 0x7a, 0xda, 0xb2, 0x4a, 0xb6, 0xe6, 0x14, 0x5d, 0x40, 0x9c, 0xca, 0x99, 0x05, 0x87, 0x4e,
-	0xdb, 0x2a, 0xdb, 0x9a, 0x53, 0x48, 0x1d, 0x3a, 0x6d, 0xf3, 0x11, 0xd9, 0xc0, 0x00, 0x31, 0x05,
-	0xb1, 0x35, 0x67, 0xdb, 0x05, 0x50, 0x37, 0x9a, 0x5a, 0x74, 0xe9, 0xb4, 0xad, 0x8a, 0xad, 0x39,
-	0x55, 0xc5, 0xa5, 0xd3, 0x8e, 0x0b, 0x74, 0x3d, 0x61, 0x7d, 0x61, 0xdd, 0xb5, 0x35, 0x47, 0xc7,
-	0x02, 0x9d, 0xc2, 0x38, 0xce, 0xe1, 0x8a, 0x85, 0x83, 0x09, 0xb5, 0xee, 0xd9, 0x9a, 0xa3, 0x61,
-	0x0e, 0xc7, 0x38, 0x11, 0x63, 0x07, 0x73, 0x41, 0x03, 0x6b, 0xcb, 0xd6, 0x9c, 0x0d, 0xc4, 0x1e,
-	0xc1, 0x38, 0xc9, 0x5f, 0xf0, 0xb1, 0x37, 0xb2, 0x36, 0x6d, 0xcd, 0x29, 0xcb, 0xfc, 0x71, 0xa2,
-	0xfe, 0xbb, 0x4e, 0x8a, 0x2e, 0xf5, 0x69, 0x5f, 0x60, 0x73, 0x79, 0xda, 0x5c, 0x03, 0x9a, 0xcb,
-	0xd3, 0xe6, 0x72, 0xa5, 0xb9, 0x06, 0x34, 0x97, 0x2b, 0xcd, 0xe5, 0x4a, 0x73, 0x0d, 0x68, 0x2e,
-	0x57, 0x9a, 0xcb, 0xd5, 0xe6, 0x1a, 0xd0, 0x5c, 0xae, 0x36, 0x97, 0xab, 0xcd, 0x35, 0xa0, 0xb9,
-	0x3c, 0x69, 0x6e, 0x44, 0x2d, 0xcb, 0x52, 0xb0, 0x0d, 0x28, 0x0b, 0x57, 0xca, 0xc2, 0xd3, 0xb2,
-	0x14, 0x6d, 0x03, 0xca, 0xc2, 0x93, 0xb2, 0x44, 0xe6, 0x28, 0xf3, 0x6d, 0xdb, 0x80, 0xcc, 0x79,
-	0x9c, 0x79, 0x4c, 0x1d, 0x57, 0xcd, 0x80, 0xaa, 0xf1, 0xa8, 0x6a, 0xf5, 0xff, 0x34, 0x92, 0x3f,
-	0xf1, 0xc2, 0x69, 0x60, 0x3e, 0x96, 0x3b, 0x9e, 0x7a, 0xe1, 0x14, 0x77, 0xfc, 0x56, 0xab, 0xdc,
-	0xf0, 0x07, 0xad, 0x06, 0x58, 0x71, 0xf3, 0xc3, 0x0f, 0xf0, 0xe2, 0xb1, 0x17, 0x94, 0x67, 0xd1,
-	0x8b, 0x47, 0x5e, 0x2f, 0xc8, 0x36, 0x70, 0x79, 0x34, 0x10, 0xf4, 0x4a, 0x3a, 0x1b, 0x48, 0x79,
-	0x2f, 0x71, 0x0e, 0x1a, 0x67, 0x68, 0x45, 0xe0, 0x26, 0xf3, 0x45, 0x3a, 0x04, 0x38, 0xcf, 0xc0,
-	0x73, 0x18, 0x6b, 0x1d, 0x9c, 0xab, 0xf0, 0xba, 0x43, 0x88, 0x42, 0x56, 0x24, 0xc6, 0xc5, 0xd9,
-	0x79, 0x55, 0x83, 0x1f, 0xc7, 0xe7, 0xdd, 0xaa, 0x6e, 0x96, 0x48, 0xee, 0xf8, 0xed, 0xc9, 0x87,
-	0x2a, 0xa9, 0xff, 0x6d, 0x90, 0x3c, 0xb8, 0x06, 0xe6, 0x13, 0xb9, 0x7b, 0x64, 0x48, 0xcc, 0xbf,
-	0xd2, 0xaa, 0x60, 0x34, 0x49, 0x85, 0x5b, 0x49, 0xfe, 0x34, 0x9b, 0x58, 0xa9, 0x11, 0x67, 0xa1,
-	0x8f, 0xe7, 0x9f, 0xb4, 0xee, 0x24, 0x9e, 0x41, 0xe3, 0xdc, 0x17, 0xaf, 0xc1, 0xe4, 0x26, 0x4e,
-	0x40, 0x9e, 0xe6, 0x83, 0x1b, 0x27, 0x4b, 0xce, 0x55, 0x72, 0x1e, 0x93, 0x43, 0xd2, 0x8b, 0xe4,
-	0x6e, 0x42, 0x1e, 0x3b, 0xd5, 0xfe, 0xd1, 0x48, 0x29, 0x8e, 0x79, 0x93, 0x6c, 0x2d, 0x9e, 0x0f,
-	0x3d, 0x73, 0x3e, 0x32, 0x05, 0x30, 0x6e, 0x2c, 0xc0, 0x3b, 0xb2, 0xc5, 0x7c, 0x21, 0x5d, 0xe3,
-	0x95, 0x42, 0x19, 0xea, 0x2b, 0xca, 0x00, 0x3f, 0x24, 0x4c, 0x2e, 0x3c, 0x83, 0xac, 0x75, 0xc8,
-	0xd6, 0xa2, 0xc7, 0x97, 0x6d, 0xc4, 0xda, 0x37, 0xa4, 0xe4, 0x2a, 0x59, 0xaf, 0x39, 0xcf, 0xf5,
-	0x2e, 0x29, 0x44, 0x8b, 0x5e, 0xcc, 0x5f, 0xbb, 0x39, 0x7f, 0xfd, 0xa6, 0xfc, 0xeb, 0xff, 0xea,
-	0xa4, 0xec, 0xd2, 0x59, 0x38, 0xe6, 0xf4, 0x4a, 0xaa, 0x09, 0x9d, 0xc5, 0xd1, 0x75, 0x8c, 0x4e,
-	0x67, 0x58, 0xf3, 0x3d, 0x52, 0x01, 0x53, 0xac, 0x98, 0xba, 0xad, 0x83, 0xe8, 0x72, 0x3a, 0x53,
-	0x44, 0x37, 0x71, 0x40, 0x4d, 0xd1, 0x41, 0x74, 0x63, 0x07, 0x29, 0xba, 0xe0, 0x90, 0x88, 0x6e,
-	0xce, 0xd6, 0x41, 0x74, 0x39, 0x9d, 0xa9, 0xa2, 0x9b, 0xba, 0xa0, 0xb6, 0xe8, 0x20, 0xba, 0x89,
-	0x4b, 0xa4, 0x2e, 0x10, 0x26, 0x52, 0x17, 0x1d, 0xd5, 0x85, 0xce, 0x52, 0x75, 0xa1, 0xb3, 0x54,
-	0x5d, 0x74, 0x54, 0x17, 0x3a, 0x53, 0xd4, 0x05, 0xe8, 0x65, 0xdd, 0x4a, 0xb6, 0x8e, 0xea, 0x42,
-	0x67, 0x8a, 0xba, 0x40, 0xf6, 0xa8, 0x2e, 0x65, 0x5b, 0x47, 0x75, 0xa1, 0x33, 0xa9, 0xc9, 0x8f,
-	0x65, 0x69, 0xb0, 0x95, 0xc4, 0xd6, 0xb3, 0x6a, 0x41, 0x67, 0x78, 0x42, 0x9f, 0xc8, 0x08, 0x51,
-	0xe9, 0x2b, 0xb6, 0xbe, 0x7c, 0x3c, 0xe8, 0x2c, 0x2a, 0xfd, 0x29, 0x29, 0x9c, 0x7b, 0x94, 0x5d,
-	0x07, 0xa6, 0x49, 0x8c, 0x40, 0x70, 0xd9, 0xc8, 0x37, 0xb7, 0x5c, 0x18, 0x98, 0x7b, 0xc4, 0x98,
-	0x06, 0xa3, 0x15, 0xdd, 0x03, 0x87, 0x69, 0x30, 0x3a, 0x2a, 0x92, 0x7c, 0xe8, 0x8d, 0x99, 0x57,
-	0xff, 0xb3, 0x40, 0x72, 0x3f, 0xf5, 0xfd, 0xc0, 0x3c, 0x20, 0x1b, 0x28, 0xd0, 0x97, 0x82, 0x5d,
-	0x4a, 0x3e, 0x38, 0x9d, 0x3b, 0x88, 0x05, 0x87, 0x06, 0x4a, 0x7f, 0x8f, 0x75, 0x05, 0x3f, 0xf1,
-	0x04, 0x9f, 0xbb, 0x64, 0x9c, 0x4c, 0x98, 0xaf, 0x49, 0x35, 0x2e, 0x3b, 0xe0, 0x71, 0x0b, 0xe8,
-	0x48, 0xf0, 0x20, 0x25, 0x88, 0xbb, 0xd0, 0x63, 0xb0, 0x27, 0x24, 0xc9, 0x56, 0xb0, 0x30, 0x69,
-	0x1e, 0x92, 0x2d, 0x00, 0x03, 0x49, 0x74, 0x81, 0x48, 0x95, 0xd8, 0x4d, 0x69, 0xc0, 0xaf, 0xc7,
-	0xe4, 0x6d, 0x22, 0x49, 0x36, 0x06, 0xca, 0x14, 0x50, 0xc8, 0xcb, 0x05, 0x48, 0x12, 0xcd, 0x5c,
-	0xa0, 0x90, 0x77, 0x4d, 0x8f, 0x41, 0xd5, 0x23, 0x8a, 0x50, 0x99, 0x32, 0x5f, 0x92, 0xcd, 0x40,
-	0x70, 0xc0, 0x47, 0xbd, 0xc8, 0x23, 0x43, 0x4d, 0xc9, 0x45, 0xf0, 0x1e, 0x8b, 0xc5, 0x15, 0x08,
-	0x2a, 0x41, 0x3a, 0xa3, 0xe0, 0x19, 0xf6, 0x08, 0x2f, 0xb2, 0x65, 0xbc, 0x6c, 0xa0, 0x8a, 0x97,
-	0x33, 0xb5, 0x17, 0x64, 0x3b, 0x53, 0x6d, 0xb3, 0x4a, 0x8c, 0xdf, 0xe8, 0x1c, 0xbb, 0x9c, 0x77,
-	0xe1, 0xa7, 0x79, 0x97, 0xe4, 0x3f, 0xf6, 0x27, 0x21, 0x8d, 0x24, 0x4c, 0x0e, 0xbe, 0xd7, 0xf7,
-	0xb5, 0xda, 0x21, 0xb9, 0xb3, 0xa2, 0xd6, 0x2a, 0x45, 0x75, 0x05, 0x45, 0x49, 0xa5, 0x78, 0x45,
-	0x6e, 0x2f, 0xd5, 0x59, 0x25, 0x28, 0xad, 0x20, 0xd8, 0x54, 0x09, 0xde, 0x91, 0xdb, 0x4b, 0x55,
-	0x56, 0x09, 0x72, 0x92, 0x60, 0x4f, 0x25, 0x58, 0x38, 0x15, 0x0a, 0xd7, 0x7b, 0x52, 0xcd, 0xd6,
-	0x5b, 0xa5, 0x2a, 0x4b, 0xaa, 0x47, 0x2a, 0x55, 0xe6, 0xe0, 0xac, 0x20, 0x53, 0x8a, 0xff, 0xa5,
-	0x64, 0x12, 0xa2, 0x90, 0xd5, 0xff, 0x28, 0x12, 0xf2, 0xde, 0x63, 0x9f, 0xbc, 0xde, 0xdc, 0xa7,
-	0x81, 0xf9, 0x2c, 0x73, 0xeb, 0x40, 0xcb, 0xe5, 0x5b, 0xbb, 0x11, 0xbf, 0xb5, 0x71, 0xfb, 0xfe,
-	0x0c, 0x04, 0xe9, 0x8d, 0xb4, 0x9f, 0x7d, 0x48, 0xc3, 0x66, 0xcd, 0xe2, 0x70, 0x43, 0x48, 0x60,
-	0xfa, 0xca, 0xde, 0xcf, 0xbe, 0xb2, 0xd7, 0x20, 0x3b, 0xed, 0x05, 0x64, 0xa7, 0x6d, 0x1e, 0x2c,
-	0x3d, 0xc1, 0x2b, 0xad, 0xfb, 0x4b, 0xd0, 0x0b, 0x25, 0xaa, 0xf2, 0x40, 0x3f, 0x58, 0x7a, 0xa0,
-	0xaf, 0x03, 0xc7, 0x81, 0x95, 0xe7, 0xfb, 0xbe, 0xfa, 0xf0, 0x2d, 0xac, 0x59, 0x33, 0x2a, 0x72,
-	0xba, 0x66, 0x29, 0xd0, 0x07, 0x0b, 0xaf, 0xe2, 0xe2, 0x9a, 0xb0, 0x52, 0xae, 0xd3, 0xb0, 0x91,
-	0x7c, 0x1f, 0x2c, 0x5c, 0x7b, 0xa5, 0x35, 0x60, 0x29, 0xe6, 0x29, 0x38, 0x12, 0xf7, 0x7d, 0xf5,
-	0xc1, 0x5d, 0x5e, 0xb3, 0x66, 0x94, 0xfa, 0x74, 0xcd, 0x52, 0xf9, 0x9f, 0xcb, 0x2f, 0x81, 0xf8,
-	0x03, 0x0e, 0x5f, 0xfa, 0x20, 0xab, 0x4b, 0xab, 0x8e, 0x1c, 0xf0, 0x23, 0x21, 0x1e, 0x98, 0xaf,
-	0x08, 0xbc, 0x08, 0x2f, 0x93, 0x0f, 0x38, 0xfc, 0x14, 0x58, 0xb5, 0xab, 0x7a, 0xb1, 0x87, 0x0b,
-	0xe1, 0x92, 0x91, 0xd9, 0x49, 0xb2, 0x0e, 0x87, 0xc2, 0xda, 0x41, 0xf4, 0xd7, 0xab, 0xb2, 0x0e,
-	0x87, 0x22, 0x4e, 0x38, 0x1c, 0x8a, 0x78, 0x27, 0x4f, 0xc6, 0x81, 0xb0, 0x6a, 0x6b, 0x62, 0xfe,
-	0x38, 0x0e, 0x44, 0xba, 0x93, 0x61, 0x64, 0x3e, 0x95, 0x75, 0x92, 0x47, 0x67, 0x17, 0x71, 0x5f,
-	0x2d, 0xe1, 0xd2, 0x12, 0xe1, 0xaf, 0x18, 0x84, 0x1f, 0xc0, 0xd6, 0xc3, 0x35, 0xa0, 0x13, 0xb0,
-	0x22, 0x08, 0x7f, 0x99, 0xdf, 0x11, 0x08, 0x7a, 0xd9, 0xf7, 0xe6, 0x96, 0x8d, 0x90, 0xbb, 0x4b,
-	0x90, 0x43, 0x6f, 0xee, 0x16, 0x98, 0x2f, 0x0e, 0xbd, 0xf9, 0x93, 0x67, 0x24, 0x87, 0xca, 0x5e,
-	0x21, 0xc5, 0x8b, 0xb3, 0xf7, 0x67, 0xe7, 0xbf, 0x9c, 0x55, 0x6f, 0x99, 0x65, 0x92, 0x3f, 0x7d,
-	0xeb, 0x76, 0x7b, 0x55, 0xcd, 0x24, 0xa4, 0xd0, 0x3d, 0xf9, 0xe1, 0xfc, 0xec, 0xb8, 0xaa, 0xc3,
-	0x74, 0xef, 0xe4, 0xac, 0xf7, 0xa6, 0x4a, 0x8e, 0x5e, 0x7e, 0x78, 0x3e, 0x1a, 0x8b, 0x5f, 0xc3,
-	0x41, 0x63, 0xc8, 0xa6, 0xcd, 0x11, 0x9b, 0xf4, 0xbd, 0x51, 0xfa, 0xd9, 0xfc, 0xb1, 0xd5, 0xbc,
-	0xf9, 0x0f, 0x81, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0x64, 0x29, 0xf9, 0x97, 0x31, 0x10, 0x00,
-	0x00,
+	// 1614 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x58, 0xdd, 0x6e, 0xdb, 0x36,
+	0x14, 0xae, 0x24, 0xff, 0xd2, 0xf9, 0x71, 0xd5, 0x76, 0x73, 0x9c, 0xb6, 0x51, 0x8d, 0x6e, 0xd0,
+	0x0a, 0xd4, 0x06, 0x1c, 0xd7, 0x08, 0x96, 0xfe, 0x20, 0x59, 0x92, 0x36, 0xed, 0x96, 0x0c, 0xb2,
+	0xb3, 0x02, 0xbd, 0x09, 0xec, 0x84, 0x71, 0x85, 0xd9, 0xa2, 0x4c, 0x51, 0x6d, 0xfd, 0x1e, 0x7b,
+	0x86, 0xed, 0x21, 0xf6, 0x26, 0xbb, 0xd9, 0x4b, 0xec, 0x62, 0xc0, 0x6e, 0x86, 0x43, 0x8a, 0x12,
+	0x25, 0xdb, 0x41, 0xee, 0x44, 0x9e, 0xf3, 0x7d, 0xe4, 0xf9, 0xe1, 0x39, 0x3e, 0x46, 0xdf, 0x61,
+	0xef, 0x82, 0x5c, 0xba, 0xde, 0xa8, 0xc5, 0xf0, 0x17, 0xe6, 0x0f, 0x5b, 0x0c, 0x07, 0xcc, 0xa7,
+	0x84, 0x91, 0xa0, 0xe5, 0x0f, 0xdb, 0x7c, 0xd9, 0xe4, 0x6b, 0xd3, 0xf0, 0x87, 0xed, 0xfa, 0xc6,
+	0x88, 0x90, 0xd1, 0x18, 0xb7, 0xf8, 0xd6, 0x30, 0xbc, 0x6a, 0x0d, 0xbc, 0x99, 0x90, 0xd7, 0x37,
+	0xb3, 0x22, 0x3c, 0xf1, 0x99, 0x14, 0x3e, 0xcc, 0x0a, 0x2f, 0x43, 0x3a, 0x60, 0x2e, 0xf1, 0x22,
+	0xf9, 0xfd, 0xac, 0x3c, 0x60, 0x34, 0xbc, 0x88, 0x8e, 0xae, 0x6f, 0x65, 0xa5, 0xcc, 0x9d, 0xe0,
+	0x80, 0x0d, 0x26, 0xfe, 0x32, 0xfa, 0xcf, 0x74, 0xe0, 0xfb, 0x98, 0x06, 0x42, 0xde, 0xf8, 0xcb,
+	0x40, 0xc5, 0xde, 0xc5, 0x60, 0x3c, 0xa0, 0x81, 0xb9, 0x81, 0x4a, 0xc4, 0x67, 0xe7, 0x43, 0x42,
+	0xc6, 0x35, 0xcd, 0xd2, 0xec, 0x92, 0x53, 0x24, 0x3e, 0xdb, 0x27, 0x64, 0x6c, 0x6e, 0xa2, 0x32,
+	0x88, 0x5c, 0x8f, 0x6d, 0xb7, 0x6b, 0xba, 0xa5, 0xd9, 0x79, 0x07, 0x74, 0x8f, 0x61, 0xad, 0x08,
+	0xbb, 0x9d, 0x9a, 0x61, 0x69, 0xb6, 0x21, 0x85, 0xdd, 0x8e, 0xf9, 0x00, 0x21, 0x10, 0x86, 0x02,
+	0x9a, 0xb3, 0x34, 0x7b, 0xd5, 0x01, 0xf5, 0x33, 0xbe, 0xa1, 0x8a, 0xbb, 0x9d, 0x5a, 0xde, 0xd2,
+	0xec, 0x5c, 0x2c, 0x4e, 0xd0, 0x81, 0x40, 0x17, 0x2c, 0xcd, 0xbe, 0xcd, 0xc5, 0xbd, 0x14, 0x3a,
+	0x10, 0xe8, 0xa2, 0xa5, 0xd9, 0x66, 0x2c, 0xee, 0x76, 0xcc, 0x2d, 0x54, 0x01, 0xf1, 0x95, 0xfb,
+	0x05, 0x5f, 0x6e, 0xb7, 0x6b, 0x25, 0x4b, 0xb3, 0x8b, 0x0e, 0x20, 0x8e, 0xc4, 0x4e, 0x4a, 0xa1,
+	0xdb, 0xa9, 0x95, 0x2d, 0xcd, 0x2e, 0x24, 0x0a, 0xdd, 0x8e, 0xf9, 0x08, 0xad, 0xf0, 0x03, 0x24,
+	0x05, 0xb2, 0x34, 0x7b, 0xdd, 0x01, 0x50, 0x2f, 0xda, 0x4a, 0xab, 0x74, 0x3b, 0xb5, 0x8a, 0xa5,
+	0xd9, 0x55, 0x45, 0xa5, 0xdb, 0x91, 0x0e, 0xba, 0x1a, 0x93, 0x01, 0xab, 0xdd, 0xb5, 0x34, 0x5b,
+	0xe7, 0x0e, 0x3a, 0x82, 0xb5, 0xb4, 0xe1, 0x92, 0x84, 0xc3, 0x31, 0xae, 0xdd, 0xb3, 0x34, 0x5b,
+	0xe3, 0x36, 0x1c, 0xf0, 0x0d, 0x89, 0x1d, 0xce, 0x18, 0x0e, 0x6a, 0x6b, 0x96, 0x66, 0xaf, 0x70,
+	0xec, 0x3e, 0xac, 0x63, 0xfb, 0x19, 0x75, 0xbd, 0x51, 0x6d, 0xd5, 0xd2, 0xec, 0xb2, 0xb0, 0x9f,
+	0x6f, 0x34, 0x7e, 0xd3, 0x51, 0xd1, 0xc1, 0x3e, 0x1e, 0x30, 0x1e, 0x5c, 0x9a, 0x04, 0xd7, 0x80,
+	0xe0, 0xd2, 0x24, 0xb8, 0x54, 0x09, 0xae, 0x01, 0xc1, 0xa5, 0x4a, 0x70, 0xa9, 0x12, 0x5c, 0x03,
+	0x82, 0x4b, 0x95, 0xe0, 0x52, 0x35, 0xb8, 0x06, 0x04, 0x97, 0xaa, 0xc1, 0xa5, 0x6a, 0x70, 0x0d,
+	0x08, 0x2e, 0x8d, 0x83, 0x1b, 0x51, 0x0b, 0xb7, 0x14, 0x2c, 0x03, 0xdc, 0x42, 0x15, 0xb7, 0xd0,
+	0xc4, 0x2d, 0x45, 0xcb, 0x00, 0xb7, 0xd0, 0xd8, 0x2d, 0x91, 0x38, 0xb2, 0x7c, 0xdd, 0x32, 0xc0,
+	0x72, 0x2a, 0x2d, 0x97, 0xd4, 0xd2, 0x6b, 0x06, 0x78, 0x8d, 0x46, 0x5e, 0x6b, 0xfc, 0xa7, 0xa1,
+	0xfc, 0xa1, 0x17, 0x4e, 0x02, 0xf3, 0xb1, 0xc8, 0x78, 0xec, 0x85, 0x13, 0x9e, 0xf1, 0x6b, 0xed,
+	0x72, 0xd3, 0x1f, 0xb6, 0x9b, 0x20, 0xe5, 0xc9, 0x0f, 0x1f, 0xa0, 0x45, 0xa5, 0x16, 0xb8, 0x27,
+	0xad, 0x45, 0x23, 0xad, 0x17, 0x68, 0x1d, 0xb8, 0x3c, 0x1c, 0x30, 0x7c, 0x29, 0x94, 0x0d, 0x4e,
+	0x79, 0x2f, 0x56, 0x0e, 0x9a, 0x27, 0x5c, 0xca, 0x81, 0xab, 0xc4, 0x67, 0xc9, 0x12, 0xe0, 0x34,
+	0x03, 0xcf, 0xf1, 0xb3, 0x96, 0xc1, 0xa9, 0x0a, 0x6f, 0xd8, 0x08, 0x29, 0x64, 0x45, 0x64, 0x9c,
+	0x9d, 0x9c, 0x56, 0x35, 0xf8, 0x38, 0x38, 0xed, 0x55, 0x75, 0xb3, 0x84, 0x72, 0x07, 0xc7, 0x87,
+	0x1f, 0xaa, 0xa8, 0xf1, 0xb7, 0x81, 0xf2, 0xa0, 0x1a, 0x98, 0x4f, 0x44, 0xf6, 0x88, 0x23, 0xb9,
+	0xfd, 0x95, 0x76, 0x85, 0x9f, 0x26, 0xa8, 0x78, 0x2a, 0x89, 0x4f, 0xb3, 0xc5, 0x3d, 0x35, 0xa2,
+	0x24, 0xf4, 0xf9, 0xfb, 0x47, 0xed, 0x3b, 0xb1, 0x66, 0xd0, 0x3c, 0xf5, 0xd9, 0x6b, 0x10, 0x39,
+	0xb1, 0x12, 0x90, 0x27, 0xf6, 0xf0, 0xc4, 0xc9, 0x92, 0x53, 0x95, 0x9c, 0x4a, 0x72, 0x30, 0x3a,
+	0x4d, 0xee, 0xc4, 0xe4, 0x52, 0xa9, 0xfe, 0x8f, 0x86, 0x4a, 0xf2, 0xcc, 0xeb, 0xca, 0x56, 0xfa,
+	0x7d, 0xe8, 0x99, 0xf7, 0x91, 0x71, 0x80, 0x71, 0xad, 0x03, 0xde, 0xa2, 0x35, 0xe2, 0x33, 0xa1,
+	0x2a, 0x6f, 0x0a, 0x6e, 0x68, 0x2c, 0x70, 0x03, 0x7c, 0x08, 0x98, 0xb8, 0x78, 0x06, 0x59, 0xef,
+	0xa2, 0xb5, 0xb4, 0xc6, 0xcd, 0x12, 0xb1, 0xfe, 0x0d, 0x2a, 0x39, 0x8a, 0xd5, 0x4b, 0xde, 0x73,
+	0xa3, 0x87, 0x0a, 0xd1, 0xa5, 0xd3, 0xf6, 0x6b, 0xd7, 0xdb, 0xaf, 0x5f, 0x67, 0x7f, 0xe3, 0x5f,
+	0x1d, 0x95, 0x1d, 0x3c, 0x0d, 0x5d, 0x8a, 0x2f, 0x45, 0x35, 0xc1, 0x53, 0x79, 0xba, 0xce, 0x4f,
+	0xc7, 0x53, 0xee, 0xf3, 0x2d, 0x54, 0x01, 0x91, 0xac, 0x98, 0xba, 0xa5, 0x43, 0xd1, 0xa5, 0x78,
+	0xaa, 0x14, 0xdd, 0x58, 0x81, 0xd7, 0x14, 0x1d, 0x8a, 0xae, 0x54, 0x10, 0x45, 0x17, 0x14, 0xe2,
+	0xa2, 0x9b, 0xb3, 0x74, 0x28, 0xba, 0x14, 0x4f, 0xd5, 0xa2, 0x9b, 0xa8, 0xf0, 0xda, 0xa2, 0x43,
+	0xd1, 0x8d, 0x55, 0xa2, 0xea, 0x02, 0xc7, 0x44, 0xd5, 0x45, 0xe7, 0xd5, 0x05, 0x4f, 0x93, 0xea,
+	0x82, 0xa7, 0x49, 0x75, 0xd1, 0x79, 0x75, 0xc1, 0x53, 0xa5, 0xba, 0x00, 0xbd, 0xf0, 0x5b, 0xc9,
+	0xd2, 0x79, 0x75, 0xc1, 0x53, 0xa5, 0xba, 0x80, 0xf5, 0xbc, 0xba, 0x94, 0x2d, 0x9d, 0x57, 0x17,
+	0x3c, 0x15, 0x35, 0xf9, 0xb1, 0x70, 0x0d, 0x0f, 0x25, 0xb2, 0xf4, 0x6c, 0xb5, 0xc0, 0x53, 0xfe,
+	0x42, 0x9f, 0x88, 0x13, 0x22, 0xd7, 0x57, 0x2c, 0x7d, 0xfe, 0x79, 0xe0, 0x69, 0xe4, 0xfa, 0x53,
+	0xb4, 0xfe, 0xf3, 0x80, 0x32, 0x77, 0x30, 0x96, 0x01, 0xc8, 0x5c, 0x50, 0xcb, 0x5e, 0xf0, 0xfa,
+	0xbc, 0x6f, 0x1c, 0xa1, 0xc2, 0xa9, 0x87, 0xc9, 0x55, 0x60, 0x9a, 0xc8, 0x08, 0x18, 0x15, 0x99,
+	0xf1, 0xe6, 0x96, 0x03, 0x0b, 0x73, 0x0b, 0x19, 0x93, 0x60, 0xb4, 0x20, 0x1d, 0x40, 0x61, 0x12,
+	0x8c, 0xf6, 0x8b, 0x28, 0x1f, 0x7a, 0x2e, 0xf1, 0x1a, 0x7f, 0x16, 0x50, 0xee, 0xa7, 0x81, 0x1f,
+	0x98, 0xbb, 0x68, 0x85, 0x57, 0xfc, 0x73, 0x46, 0xce, 0x05, 0x1f, 0x3c, 0xf7, 0x0d, 0x8e, 0x05,
+	0x85, 0x26, 0xef, 0x25, 0x7d, 0xd2, 0x63, 0xf4, 0xd0, 0x63, 0x74, 0xe6, 0x20, 0x37, 0xde, 0x30,
+	0x5f, 0xa3, 0xaa, 0x8c, 0x23, 0xe0, 0x79, 0x4e, 0xe9, 0x9c, 0xe0, 0x41, 0x42, 0x20, 0xc3, 0xda,
+	0x27, 0x90, 0x64, 0x82, 0x64, 0x2d, 0x48, 0x6d, 0x9a, 0x7b, 0x68, 0x0d, 0xc0, 0x40, 0x12, 0x75,
+	0x24, 0x51, 0x76, 0x36, 0x13, 0x1a, 0xd0, 0xeb, 0x13, 0xd1, 0x9e, 0x04, 0xc9, 0xca, 0x50, 0xd9,
+	0x02, 0x0a, 0xd1, 0xad, 0x80, 0x24, 0x2e, 0xc2, 0x29, 0x0a, 0xd1, 0xbc, 0xfa, 0x04, 0xc2, 0x18,
+	0x51, 0x84, 0xca, 0x96, 0xf9, 0x12, 0xad, 0x06, 0x8c, 0x02, 0x3e, 0x0a, 0x6e, 0x9e, 0x33, 0xd4,
+	0x15, 0x5b, 0x18, 0xed, 0x13, 0x59, 0xad, 0x81, 0xa0, 0x12, 0x24, 0x3b, 0x0a, 0x9e, 0xf0, 0x18,
+	0xf1, 0xce, 0x38, 0x8f, 0x17, 0x01, 0x54, 0xf1, 0x62, 0xa7, 0xfe, 0x02, 0xad, 0x67, 0xbc, 0x6d,
+	0x56, 0x91, 0xf1, 0x2b, 0x9e, 0xf1, 0x28, 0xe7, 0x1d, 0xf8, 0x34, 0xef, 0xa2, 0xfc, 0xa7, 0xc1,
+	0x38, 0xc4, 0x51, 0x6e, 0x88, 0xc5, 0xf7, 0xfa, 0x8e, 0x56, 0xdf, 0x43, 0x77, 0x16, 0xf8, 0x5a,
+	0xa5, 0xa8, 0x2e, 0xa0, 0x28, 0xa9, 0x14, 0xaf, 0xd0, 0xed, 0x39, 0x3f, 0xab, 0x04, 0xa5, 0x05,
+	0x04, 0xab, 0x2a, 0xc1, 0x5b, 0x74, 0x7b, 0xce, 0xcb, 0x2a, 0x41, 0x4e, 0x10, 0x6c, 0xa9, 0x04,
+	0xa9, 0x67, 0xa6, 0x70, 0xbd, 0x43, 0xd5, 0xac, 0xbf, 0x55, 0xaa, 0xb2, 0xa0, 0x7a, 0xa4, 0x52,
+	0x65, 0x5e, 0xe2, 0x02, 0x32, 0xc5, 0xf9, 0x37, 0x25, 0x13, 0x10, 0x85, 0xac, 0xb1, 0x8d, 0x4c,
+	0x71, 0xc2, 0x7b, 0x97, 0x7d, 0xbc, 0xe1, 0xcb, 0x6e, 0xfc, 0xa1, 0xa3, 0xea, 0xb1, 0x77, 0xe9,
+	0x52, 0x7c, 0xc1, 0x62, 0x4c, 0x77, 0x41, 0x23, 0xff, 0x5a, 0x31, 0x41, 0x3d, 0x40, 0xed, 0x69,
+	0xdd, 0x54, 0x8f, 0x16, 0x6f, 0x6e, 0x39, 0x8e, 0x2a, 0xbd, 0x30, 0x93, 0xe2, 0xe2, 0x9d, 0x7d,
+	0xcb, 0xa1, 0xd9, 0xdb, 0x5d, 0x9f, 0xee, 0xf5, 0xf7, 0x37, 0x8a, 0xcf, 0xd3, 0xb4, 0x4b, 0x97,
+	0x5e, 0x52, 0x71, 0xef, 0xef, 0x45, 0x84, 0xde, 0x79, 0xe4, 0xb3, 0xd7, 0x9f, 0xf9, 0x38, 0x30,
+	0x9f, 0x65, 0x7e, 0x25, 0xc0, 0x8b, 0x12, 0xb3, 0x51, 0x53, 0xce, 0x46, 0xbc, 0x3a, 0xfc, 0x02,
+	0x04, 0xc9, 0x2f, 0x88, 0x9d, 0xec, 0xe0, 0x03, 0xb5, 0x20, 0x8b, 0xe3, 0xef, 0x4d, 0x00, 0x93,
+	0xa9, 0x68, 0x27, 0x3b, 0x15, 0x2d, 0x41, 0x76, 0x3b, 0x29, 0x64, 0xb7, 0x63, 0xee, 0xce, 0x8d,
+	0x4c, 0x95, 0xf6, 0xfd, 0x39, 0xe8, 0x99, 0x72, 0xaa, 0x32, 0x50, 0xed, 0xce, 0x0d, 0x54, 0xcb,
+	0xc0, 0xf2, 0x60, 0x65, 0xdc, 0xda, 0x51, 0x07, 0x95, 0xc2, 0x92, 0x3b, 0xf3, 0x0e, 0x9a, 0xdc,
+	0x59, 0x34, 0xd4, 0xdd, 0xd4, 0x14, 0x53, 0x5c, 0x72, 0xac, 0x68, 0xaf, 0xc9, 0xb1, 0x51, 0xbb,
+	0xdd, 0x4d, 0xb5, 0xab, 0xd2, 0x12, 0xb0, 0x78, 0x01, 0x09, 0x38, 0xea, 0x75, 0x3b, 0xea, 0x80,
+	0x54, 0x5e, 0x72, 0x67, 0xde, 0x9a, 0x93, 0x3b, 0x8b, 0x4e, 0xfd, 0x5c, 0x4c, 0x6e, 0x72, 0xe0,
+	0xe6, 0x93, 0x19, 0x74, 0xad, 0xb9, 0x5b, 0x47, 0x0a, 0x7c, 0xa8, 0x93, 0x0b, 0xf3, 0x15, 0x82,
+	0x5f, 0xf0, 0xe7, 0xf1, 0xc0, 0xcd, 0x47, 0xb7, 0x45, 0x59, 0xd5, 0x97, 0x1a, 0x0e, 0x1c, 0x17,
+	0xaf, 0xe4, 0xab, 0x15, 0xf3, 0x7c, 0x6d, 0x23, 0x4a, 0xec, 0x05, 0x56, 0x87, 0x17, 0x4c, 0x1a,
+	0x1c, 0x5e, 0x30, 0x99, 0xc9, 0x63, 0x37, 0x60, 0xb5, 0xfa, 0x92, 0x33, 0x7f, 0x74, 0x03, 0x96,
+	0x64, 0x32, 0xac, 0xcc, 0x6d, 0xe1, 0x27, 0xf1, 0x8c, 0x36, 0x39, 0xee, 0xab, 0x39, 0x5c, 0xe2,
+	0x22, 0xfe, 0x25, 0x41, 0xfc, 0x0f, 0x8b, 0xda, 0xc3, 0x25, 0xa0, 0x43, 0x90, 0x72, 0x10, 0xff,
+	0x32, 0x9f, 0x22, 0x38, 0xf4, 0x7c, 0xe0, 0xcd, 0x6a, 0x16, 0x87, 0xdc, 0x9d, 0x83, 0xec, 0x79,
+	0x33, 0xa7, 0x40, 0x7c, 0xb6, 0xe7, 0xcd, 0x9e, 0x3c, 0x43, 0x39, 0xde, 0x38, 0x2b, 0xa8, 0x78,
+	0x76, 0xf2, 0xee, 0xe4, 0xf4, 0xfd, 0x49, 0xf5, 0x96, 0x59, 0x46, 0xf9, 0xa3, 0x63, 0xa7, 0xd7,
+	0xaf, 0x6a, 0x26, 0x42, 0x85, 0xde, 0xe1, 0x0f, 0xa7, 0x27, 0x07, 0x55, 0x1d, 0xb6, 0xfb, 0x87,
+	0x27, 0xfd, 0x37, 0x55, 0xb4, 0xff, 0xf2, 0xc3, 0xf3, 0x91, 0xcb, 0x3e, 0x86, 0xc3, 0xe6, 0x05,
+	0x99, 0xb4, 0x46, 0x64, 0x3c, 0xf0, 0x46, 0xc9, 0xdf, 0x1c, 0x9f, 0xda, 0xad, 0xeb, 0xff, 0xc0,
+	0xf9, 0x3f, 0x00, 0x00, 0xff, 0xff, 0x7b, 0xf3, 0x0d, 0xb6, 0xe1, 0x11, 0x00, 0x00,
 }
diff --git a/encoding/textpb/testprotos/pb2/test.proto b/encoding/textpb/testprotos/pb2/test.proto
index 21edaca..cd20cc5 100644
--- a/encoding/textpb/testprotos/pb2/test.proto
+++ b/encoding/textpb/testprotos/pb2/test.proto
@@ -111,6 +111,12 @@
   required Nested req_nested = 11;
 }
 
+// Message contains both required and optional fields.
+message PartialRequired {
+  required string req_string = 1;
+  optional string opt_string = 2;
+}
+
 // Message contains oneof field.
 message Oneofs {
   oneof union {
@@ -129,6 +135,17 @@
   map<string, Oneofs> str_to_oneofs = 6;
 }
 
+// Following messages are for testing required field nested in optional, repeated and map fields.
+message NestedWithRequired {
+  required string req_string = 1;
+}
+
+message IndirectRequired {
+  optional NestedWithRequired opt_nested = 1;
+  repeated NestedWithRequired rpt_nested = 2;
+  map<string, NestedWithRequired> str_to_nested = 3;
+}
+
 // Message contains well-known type fields.
 message KnownTypes {
   optional google.protobuf.BoolValue opt_bool = 1;
diff --git a/encoding/textpb/testprotos/pb3/test.pb.go b/encoding/textpb/testprotos/pb3/test.pb.go
index c5c2fb2..808a8ba 100644
--- a/encoding/textpb/testprotos/pb3/test.pb.go
+++ b/encoding/textpb/testprotos/pb3/test.pb.go
@@ -6,7 +6,6 @@
 import (
 	fmt "fmt"
 	proto "github.com/golang/protobuf/proto"
-	_ "github.com/golang/protobuf/ptypes/struct"
 	math "math"
 )
 
@@ -237,13 +236,11 @@
 
 // Message contains enum fields.
 type Enums struct {
-	SEnum                Enum               `protobuf:"varint,1,opt,name=s_enum,json=sEnum,proto3,enum=pb3.Enum" json:"s_enum,omitempty"`
-	RptEnum              []Enum             `protobuf:"varint,2,rep,packed,name=rpt_enum,json=rptEnum,proto3,enum=pb3.Enum" json:"rpt_enum,omitempty"`
-	SNestedEnum          Enums_NestedEnum   `protobuf:"varint,3,opt,name=s_nested_enum,json=sNestedEnum,proto3,enum=pb3.Enums_NestedEnum" json:"s_nested_enum,omitempty"`
-	RptNestedEnum        []Enums_NestedEnum `protobuf:"varint,4,rep,packed,name=rpt_nested_enum,json=rptNestedEnum,proto3,enum=pb3.Enums_NestedEnum" json:"rpt_nested_enum,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}           `json:"-"`
-	XXX_unrecognized     []byte             `json:"-"`
-	XXX_sizecache        int32              `json:"-"`
+	SEnum                Enum             `protobuf:"varint,1,opt,name=s_enum,json=sEnum,proto3,enum=pb3.Enum" json:"s_enum,omitempty"`
+	SNestedEnum          Enums_NestedEnum `protobuf:"varint,3,opt,name=s_nested_enum,json=sNestedEnum,proto3,enum=pb3.Enums_NestedEnum" json:"s_nested_enum,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}         `json:"-"`
+	XXX_unrecognized     []byte           `json:"-"`
+	XXX_sizecache        int32            `json:"-"`
 }
 
 func (m *Enums) Reset()         { *m = Enums{} }
@@ -278,13 +275,6 @@
 	return Enum_ZERO
 }
 
-func (m *Enums) GetRptEnum() []Enum {
-	if m != nil {
-		return m.RptEnum
-	}
-	return nil
-}
-
 func (m *Enums) GetSNestedEnum() Enums_NestedEnum {
 	if m != nil {
 		return m.SNestedEnum
@@ -292,20 +282,12 @@
 	return Enums_CERO
 }
 
-func (m *Enums) GetRptNestedEnum() []Enums_NestedEnum {
-	if m != nil {
-		return m.RptNestedEnum
-	}
-	return nil
-}
-
-// Message contains message and group fields.
+// Message contains nested message field.
 type Nests struct {
-	SNested              *Nested   `protobuf:"bytes,1,opt,name=s_nested,json=sNested,proto3" json:"s_nested,omitempty"`
-	RptNested            []*Nested `protobuf:"bytes,2,rep,name=rpt_nested,json=rptNested,proto3" json:"rpt_nested,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}  `json:"-"`
-	XXX_unrecognized     []byte    `json:"-"`
-	XXX_sizecache        int32     `json:"-"`
+	SNested              *Nested  `protobuf:"bytes,1,opt,name=s_nested,json=sNested,proto3" json:"s_nested,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
 }
 
 func (m *Nests) Reset()         { *m = Nests{} }
@@ -340,13 +322,6 @@
 	return nil
 }
 
-func (m *Nests) GetRptNested() []*Nested {
-	if m != nil {
-		return m.RptNested
-	}
-	return nil
-}
-
 // Message type used as submessage.
 type Nested struct {
 	SString              string   `protobuf:"bytes,1,opt,name=s_string,json=sString,proto3" json:"s_string,omitempty"`
@@ -409,41 +384,37 @@
 }
 
 var fileDescriptor_0854715c5b41c422 = []byte{
-	// 566 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x53, 0x5f, 0x6b, 0xdb, 0x3e,
-	0x14, 0xfd, 0x29, 0x4e, 0xec, 0xe4, 0xe6, 0x97, 0xd6, 0x13, 0x2b, 0xf3, 0xfe, 0x81, 0x08, 0x63,
-	0x68, 0x1d, 0xc4, 0x90, 0x18, 0xc3, 0x60, 0xdb, 0x43, 0xd7, 0x14, 0xca, 0x20, 0x01, 0x67, 0x65,
-	0xd0, 0x3d, 0x84, 0x38, 0x71, 0xbd, 0x80, 0x6b, 0x19, 0x5f, 0x79, 0x74, 0x5f, 0x66, 0xdf, 0x74,
-	0x30, 0x24, 0x39, 0x89, 0x3b, 0xe8, 0x9e, 0xac, 0xe3, 0x73, 0x8e, 0xee, 0x3d, 0xf7, 0x22, 0x78,
-	0x93, 0xe4, 0x6b, 0xb1, 0xd9, 0xe6, 0xa9, 0x2f, 0x93, 0x3b, 0x59, 0xc4, 0xbe, 0x4c, 0x50, 0x16,
-	0xa5, 0x90, 0x02, 0xfd, 0x22, 0x9e, 0x68, 0x38, 0xd2, 0x98, 0x5a, 0x45, 0x3c, 0x79, 0xf6, 0x22,
-	0x15, 0x22, 0xcd, 0x12, 0x5f, 0xff, 0x8a, 0xab, 0x1b, 0x1f, 0x65, 0x59, 0xad, 0x6b, 0xc9, 0xf0,
-	0x97, 0x05, 0xce, 0x62, 0xbd, 0xca, 0x56, 0x25, 0xd2, 0x13, 0xb0, 0x71, 0x19, 0x0b, 0x91, 0x79,
-	0x84, 0x11, 0xde, 0x8d, 0x3a, 0x78, 0x26, 0x44, 0x46, 0x9f, 0x80, 0x83, 0xcb, 0x6d, 0x2e, 0x27,
-	0x63, 0xaf, 0xc5, 0x08, 0xef, 0x44, 0x36, 0x5e, 0x2a, 0xb4, 0x27, 0xc2, 0xc0, 0xb3, 0x18, 0xe1,
-	0x96, 0x21, 0xc2, 0x80, 0x3e, 0x85, 0x2e, 0x2e, 0x2b, 0x63, 0x69, 0x33, 0xc2, 0x07, 0x91, 0x83,
-	0x57, 0x1a, 0x1e, 0xa8, 0x30, 0xf0, 0x3a, 0x8c, 0xf0, 0x76, 0x4d, 0xed, 0x5c, 0x68, 0x5c, 0x36,
-	0x23, 0xfc, 0x51, 0xe4, 0xe0, 0xa2, 0xe1, 0x42, 0xe3, 0x72, 0x18, 0xe1, 0xb4, 0xa6, 0xc2, 0x80,
-	0x3e, 0x87, 0x1e, 0x2e, 0x6f, 0xb6, 0x77, 0xc9, 0x66, 0x32, 0xf6, 0xba, 0x8c, 0x70, 0x27, 0xea,
-	0xe2, 0x85, 0xc1, 0x0d, 0x32, 0x0c, 0xbc, 0x1e, 0x23, 0xdc, 0xde, 0x91, 0x61, 0x40, 0x5f, 0x02,
-	0xe0, 0x12, 0x77, 0x56, 0x60, 0x84, 0x1f, 0x47, 0x3d, 0x5c, 0xd4, 0x3f, 0x9a, 0x74, 0x18, 0x78,
-	0x7d, 0x46, 0xb8, 0xbb, 0xa7, 0xc3, 0xc0, 0x84, 0xbf, 0xc9, 0xc4, 0x4a, 0x7a, 0x8f, 0x19, 0xe1,
-	0xad, 0xc8, 0xc6, 0x0b, 0x85, 0x4c, 0xaf, 0x1b, 0x51, 0xc5, 0x59, 0xe2, 0x9d, 0x30, 0xc2, 0x49,
-	0xe4, 0xe0, 0xb9, 0x86, 0xc6, 0x13, 0xff, 0x94, 0x09, 0x7a, 0x47, 0x8c, 0xf0, 0xff, 0x23, 0x1b,
-	0xcf, 0x14, 0xaa, 0xf3, 0xc9, 0x72, 0x9b, 0xa7, 0xde, 0x80, 0x11, 0xde, 0x53, 0xf9, 0x34, 0x1c,
-	0xfe, 0x26, 0xd0, 0x99, 0xe6, 0xd5, 0x2d, 0x52, 0xa6, 0xd6, 0x93, 0xe4, 0xd5, 0xad, 0x5e, 0xcf,
-	0xd1, 0xb8, 0x37, 0x2a, 0xe2, 0xc9, 0x48, 0x71, 0x51, 0x07, 0xd5, 0x87, 0xbe, 0x82, 0x6e, 0x59,
-	0x48, 0xa3, 0x69, 0x31, 0xeb, 0xbe, 0xc6, 0x29, 0x0b, 0xa9, 0x55, 0xef, 0x60, 0x80, 0xcb, 0x3c,
-	0x41, 0x99, 0x6c, 0x8c, 0xd4, 0xd2, 0xd7, 0x9d, 0xec, 0xa5, 0x38, 0x9a, 0x69, 0x56, 0xdb, 0xfa,
-	0x78, 0x00, 0xf4, 0x03, 0x1c, 0xab, 0x02, 0x4d, 0x73, 0x5b, 0xd7, 0x79, 0xc0, 0x3c, 0x28, 0x0b,
-	0x79, 0x80, 0xc3, 0x31, 0x40, 0xe3, 0xb2, 0x2e, 0xb4, 0x3f, 0x4d, 0xa3, 0xb9, 0xfb, 0x1f, 0x75,
-	0xc0, 0xba, 0x9a, 0xcd, 0x5d, 0xa2, 0x0e, 0xe7, 0xf3, 0x85, 0xdb, 0x52, 0xdc, 0xf9, 0xe5, 0xf4,
-	0xda, 0x85, 0xe1, 0x37, 0xe8, 0x28, 0x0f, 0xd2, 0xd7, 0x6a, 0x46, 0xa6, 0xb2, 0x1e, 0x40, 0x7f,
-	0xdc, 0xd7, 0x45, 0xcd, 0x8d, 0x91, 0x53, 0xf7, 0x49, 0x4f, 0x01, 0x0e, 0x3d, 0xea, 0x31, 0xfc,
-	0xa5, 0xec, 0xed, 0x9b, 0x1a, 0x7e, 0x06, 0xbb, 0x76, 0x35, 0x37, 0x40, 0xee, 0x6d, 0xe0, 0x5e,
-	0xe1, 0xd6, 0xc3, 0x85, 0x4f, 0xdf, 0x42, 0x7b, 0x97, 0xeb, 0x7a, 0x9f, 0x6b, 0x3e, 0x9b, 0x9a,
-	0x5c, 0x5f, 0xbe, 0xce, 0xdd, 0x96, 0x3e, 0x4c, 0x67, 0x2e, 0x9c, 0x7d, 0xbc, 0x7e, 0x9f, 0x6e,
-	0xe5, 0xf7, 0x2a, 0x1e, 0xad, 0xc5, 0xad, 0x9f, 0x8a, 0x6c, 0x95, 0xa7, 0x87, 0x27, 0xfa, 0x63,
-	0xec, 0xff, 0xfb, 0x95, 0xc7, 0xb6, 0x3e, 0x4f, 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, 0xef, 0xcf,
-	0x95, 0x7a, 0x0e, 0x04, 0x00, 0x00,
+	// 503 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x93, 0xdf, 0xab, 0xd3, 0x30,
+	0x14, 0xc7, 0xcd, 0xba, 0xb6, 0x5b, 0xe6, 0xbd, 0xd6, 0xe0, 0x30, 0x22, 0x42, 0xd8, 0x83, 0x44,
+	0x85, 0x15, 0xda, 0x52, 0x10, 0xc4, 0x87, 0xb9, 0x5d, 0xb8, 0x08, 0x1b, 0x64, 0x5e, 0x84, 0xbd,
+	0x94, 0x75, 0xeb, 0xe6, 0xa0, 0x6b, 0xc6, 0x4e, 0x2a, 0xd7, 0xff, 0xc4, 0x27, 0xff, 0x56, 0x49,
+	0xb2, 0x9f, 0x0f, 0xfa, 0xd4, 0xf3, 0xed, 0x27, 0xdf, 0x73, 0xbe, 0x87, 0x10, 0xfc, 0xae, 0xa8,
+	0x16, 0x72, 0xb9, 0xa9, 0xd6, 0xa1, 0x2a, 0x1e, 0xd5, 0x2e, 0x0f, 0x55, 0x01, 0x6a, 0xb7, 0x97,
+	0x4a, 0x42, 0xb8, 0xcb, 0x63, 0x23, 0xfb, 0x46, 0x13, 0x67, 0x97, 0xc7, 0xbd, 0x3f, 0x0e, 0xf6,
+	0xa7, 0x8b, 0x79, 0x39, 0xdf, 0x03, 0xe9, 0x62, 0x0f, 0xb2, 0x5c, 0xca, 0x92, 0x22, 0x86, 0x78,
+	0x4b, 0xb8, 0x30, 0x90, 0xb2, 0x24, 0x2f, 0xb1, 0x0f, 0xd9, 0xa6, 0x52, 0x71, 0x44, 0x1b, 0x0c,
+	0x71, 0x57, 0x78, 0x70, 0xaf, 0xd5, 0x09, 0xa4, 0x09, 0x75, 0x18, 0xe2, 0x8e, 0x05, 0x69, 0x42,
+	0x5e, 0xe1, 0x16, 0x64, 0xb5, 0xb5, 0x34, 0x19, 0xe2, 0x37, 0xc2, 0x87, 0x07, 0x23, 0xcf, 0x28,
+	0x4d, 0xa8, 0xcb, 0x10, 0x6f, 0x1e, 0xd0, 0xd1, 0x05, 0xd6, 0xe5, 0x31, 0xc4, 0x9f, 0x0b, 0x1f,
+	0xa6, 0x17, 0x2e, 0xb0, 0x2e, 0x9f, 0x21, 0x4e, 0x0e, 0x28, 0x4d, 0xc8, 0x6b, 0xdc, 0x86, 0x6c,
+	0xb5, 0x79, 0x2c, 0x96, 0x71, 0x44, 0x5b, 0x0c, 0x71, 0x5f, 0xb4, 0xe0, 0xce, 0xea, 0x0b, 0x98,
+	0x26, 0xb4, 0xcd, 0x10, 0xf7, 0x8e, 0x30, 0x4d, 0xc8, 0x1b, 0x8c, 0x21, 0x83, 0xa3, 0x15, 0x33,
+	0xc4, 0x9f, 0x89, 0x36, 0x4c, 0x0f, 0x3f, 0x2e, 0x71, 0x9a, 0xd0, 0x0e, 0x43, 0x3c, 0x38, 0xe1,
+	0x34, 0xb1, 0xcb, 0xaf, 0x4a, 0x39, 0x57, 0xf4, 0x05, 0x43, 0xbc, 0x21, 0x3c, 0xb8, 0xd3, 0xca,
+	0x66, 0x5d, 0xca, 0x3a, 0x2f, 0x0b, 0xda, 0x65, 0x88, 0x23, 0xe1, 0xc3, 0xd0, 0x48, 0xeb, 0xc9,
+	0x7f, 0xa9, 0x02, 0xe8, 0x2d, 0x43, 0xfc, 0xa9, 0xf0, 0x60, 0xa0, 0xd5, 0x61, 0x3f, 0xb5, 0xdf,
+	0x54, 0x6b, 0x7a, 0xc3, 0x10, 0x6f, 0xeb, 0xfd, 0x8c, 0xec, 0xfd, 0x46, 0xd8, 0x1d, 0x55, 0xf5,
+	0x16, 0x08, 0xd3, 0xd7, 0x53, 0x54, 0xf5, 0xd6, 0x5c, 0xcf, 0x6d, 0xd4, 0xee, 0xef, 0xf2, 0xb8,
+	0xaf, 0x99, 0x70, 0x41, 0x7f, 0xc8, 0x47, 0x7c, 0x03, 0x59, 0x55, 0x80, 0x2a, 0x96, 0xf6, 0xa0,
+	0x63, 0x0e, 0x76, 0x4f, 0x07, 0xa1, 0x3f, 0x36, 0xd4, 0x98, 0x3a, 0x70, 0x16, 0xbd, 0x08, 0xe3,
+	0xb3, 0x22, 0x2d, 0xdc, 0xfc, 0x32, 0x12, 0x93, 0xe0, 0x09, 0xf1, 0xb1, 0xf3, 0x30, 0x9e, 0x04,
+	0x48, 0x17, 0xc3, 0xc9, 0x34, 0x68, 0x68, 0x36, 0xbc, 0x1f, 0xcd, 0x02, 0xdc, 0x0b, 0xb1, 0xab,
+	0x3d, 0x40, 0xde, 0xea, 0xf8, 0x76, 0xae, 0xc9, 0xd6, 0x89, 0x3a, 0x66, 0xa4, 0xed, 0x28, 0xfc,
+	0xc3, 0xa0, 0xde, 0x57, 0xec, 0xd9, 0xea, 0x6a, 0x61, 0x74, 0xb5, 0xf0, 0x55, 0xb3, 0xc6, 0xbf,
+	0x9b, 0xbd, 0xff, 0x80, 0x9b, 0xc7, 0xac, 0xb3, 0x53, 0xd6, 0xc9, 0x78, 0x64, 0xb3, 0x7e, 0xfb,
+	0x3e, 0x09, 0x1a, 0xa6, 0x18, 0x8d, 0x03, 0x3c, 0xf8, 0x3c, 0xfb, 0xb4, 0xde, 0xa8, 0x1f, 0x75,
+	0xde, 0x5f, 0xc8, 0x6d, 0xb8, 0x96, 0xe5, 0xbc, 0x5a, 0x87, 0xe6, 0x19, 0xe4, 0xf5, 0x2a, 0xfc,
+	0x19, 0x85, 0xff, 0x7f, 0x36, 0xb9, 0x67, 0xea, 0xf8, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd9,
+	0x7d, 0x94, 0x3a, 0x5f, 0x03, 0x00, 0x00,
 }
diff --git a/encoding/textpb/testprotos/pb3/test.proto b/encoding/textpb/testprotos/pb3/test.proto
index 9825b75..d672816 100644
--- a/encoding/textpb/testprotos/pb3/test.proto
+++ b/encoding/textpb/testprotos/pb3/test.proto
@@ -23,6 +23,10 @@
   sfixed32 s_sfixed32 = 10;
   sfixed64 s_sfixed64 = 11;
 
+  // Textproto marshal outputs fields in the same order as this proto
+  // definition regardless of field number. Following fields are intended to
+  // test that assumption.
+
   float s_float = 20;
   double s_double = 21;
 
@@ -40,7 +44,6 @@
 // Message contains enum fields.
 message Enums {
   Enum s_enum = 1;
-  repeated Enum rpt_enum = 2;
 
   enum NestedEnum {
   	CERO = 0;
@@ -49,13 +52,11 @@
 	DIEZ = 10;
   }
   NestedEnum s_nested_enum = 3;
-  repeated NestedEnum rpt_nested_enum = 4;
 }
 
-// Message contains message and group fields.
+// Message contains nested message field.
 message Nests {
   Nested s_nested = 1;
-  repeated Nested rpt_nested = 2;
 }
 
 // Message type used as submessage.
diff --git a/regenerate.bash b/regenerate.bash
index acccb2b..d8a7e5e 100755
--- a/regenerate.bash
+++ b/regenerate.bash
@@ -46,3 +46,8 @@
   --go_out=paths=source_relative:$tmpdir/src \
   $tmpdir/src/google/protobuf/compiler/plugin.proto
 cp $tmpdir/src/google/protobuf/compiler/plugin.pb.go ./types/plugin/plugin.pb.go
+
+# TODO: Uncomment when test protos are ready.
+# echo "# encoding/textpb/testprotos/pb?/test.proto"
+# PROTOC_GEN_GO_ENABLE_REFLECT=1 protoc --go_out=paths=source_relative:. \
+#  encoding/textpb/testprotos/pb?/test.proto