internal/encoding/tag: centralize logic for protobuf struct tag serialization

The bespoke text-serialization of field descriptors in protoc-gen-go is also
used in the legacy implementation of protobuf reflection to derive a
protoreflect.FieldDescriptor from legacy messages and also to convert to/from
protoreflect.ExtensionDescriptor and protoV1.ExtensionDesc.

Centralize this logic in a single place:
* to avoid reimplementing the same logic in internal/impl
* to keep the marshal and unmarshal logic co-located

Change-Id: I634c5afbb9dc6eda91d6cb6b0e68dbd724cb1ccb
Reviewed-on: https://go-review.googlesource.com/c/146758
Reviewed-by: Herbie Ong <herbie@google.com>
Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/internal/impl/legacy_message.go b/internal/impl/legacy_message.go
index 12c1832..a617249 100644
--- a/internal/impl/legacy_message.go
+++ b/internal/impl/legacy_message.go
@@ -6,16 +6,14 @@
 
 import (
 	"fmt"
-	"math"
 	"reflect"
-	"strconv"
 	"strings"
 	"sync"
 	"unicode"
 
 	protoV1 "github.com/golang/protobuf/proto"
 	descriptorV1 "github.com/golang/protobuf/protoc-gen-go/descriptor"
-	"github.com/golang/protobuf/v2/internal/encoding/text"
+	ptag "github.com/golang/protobuf/v2/internal/encoding/tag"
 	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
 	ptype "github.com/golang/protobuf/v2/reflect/prototype"
 )
@@ -174,156 +172,14 @@
 	return ptype.PlaceholderMessage(m.FullName)
 }
 
-func (ms *messageDescSet) parseField(tag, tagKey, tagVal string, t reflect.Type, parent *ptype.StandaloneMessage) (f ptype.Field) {
+func (ms *messageDescSet) parseField(tag, tagKey, tagVal string, goType reflect.Type, parent *ptype.StandaloneMessage) ptype.Field {
+	t := goType
 	isOptional := t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct
 	isRepeated := t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8
 	if isOptional || isRepeated {
 		t = t.Elem()
 	}
-
-	f.Options = &descriptorV1.FieldOptions{
-		Packed: protoV1.Bool(false),
-	}
-	for len(tag) > 0 {
-		i := strings.IndexByte(tag, ',')
-		if i < 0 {
-			i = len(tag)
-		}
-		switch s := tag[:i]; {
-		case strings.HasPrefix(s, "name="):
-			f.Name = pref.Name(s[len("name="):])
-		case strings.Trim(s, "0123456789") == "":
-			n, _ := strconv.ParseUint(s, 10, 32)
-			f.Number = pref.FieldNumber(n)
-		case s == "opt":
-			f.Cardinality = pref.Optional
-		case s == "req":
-			f.Cardinality = pref.Required
-		case s == "rep":
-			f.Cardinality = pref.Repeated
-		case s == "varint":
-			switch t.Kind() {
-			case reflect.Bool:
-				f.Kind = pref.BoolKind
-			case reflect.Int32:
-				f.Kind = pref.Int32Kind
-			case reflect.Int64:
-				f.Kind = pref.Int64Kind
-			case reflect.Uint32:
-				f.Kind = pref.Uint32Kind
-			case reflect.Uint64:
-				f.Kind = pref.Uint64Kind
-			}
-		case s == "zigzag32":
-			if t.Kind() == reflect.Int32 {
-				f.Kind = pref.Sint32Kind
-			}
-		case s == "zigzag64":
-			if t.Kind() == reflect.Int64 {
-				f.Kind = pref.Sint64Kind
-			}
-		case s == "fixed32":
-			switch t.Kind() {
-			case reflect.Int32:
-				f.Kind = pref.Sfixed32Kind
-			case reflect.Uint32:
-				f.Kind = pref.Fixed32Kind
-			case reflect.Float32:
-				f.Kind = pref.FloatKind
-			}
-		case s == "fixed64":
-			switch t.Kind() {
-			case reflect.Int64:
-				f.Kind = pref.Sfixed64Kind
-			case reflect.Uint64:
-				f.Kind = pref.Fixed64Kind
-			case reflect.Float64:
-				f.Kind = pref.DoubleKind
-			}
-		case s == "bytes":
-			switch {
-			case t.Kind() == reflect.String:
-				f.Kind = pref.StringKind
-			case t.Kind() == reflect.Slice && t.Elem() == byteType:
-				f.Kind = pref.BytesKind
-			default:
-				f.Kind = pref.MessageKind
-			}
-		case s == "group":
-			f.Kind = pref.GroupKind
-		case strings.HasPrefix(s, "enum="):
-			f.Kind = pref.EnumKind
-		case strings.HasPrefix(s, "json="):
-			f.JSONName = s[len("json="):]
-		case s == "packed":
-			*f.Options.Packed = true
-		case strings.HasPrefix(s, "weak="):
-			f.Options.Weak = protoV1.Bool(true)
-			f.MessageType = ptype.PlaceholderMessage(pref.FullName(s[len("weak="):]))
-		case strings.HasPrefix(s, "def="):
-			// The default tag is special in that everything afterwards is the
-			// default regardless of the presence of commas.
-			s, i = tag[len("def="):], len(tag)
-
-			// Defaults are parsed last, so Kind is populated.
-			switch f.Kind {
-			case pref.BoolKind:
-				switch s {
-				case "1":
-					f.Default = pref.ValueOf(true)
-				case "0":
-					f.Default = pref.ValueOf(false)
-				}
-			case pref.EnumKind:
-				n, _ := strconv.ParseInt(s, 10, 32)
-				f.Default = pref.ValueOf(pref.EnumNumber(n))
-			case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
-				n, _ := strconv.ParseInt(s, 10, 32)
-				f.Default = pref.ValueOf(int32(n))
-			case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
-				n, _ := strconv.ParseInt(s, 10, 64)
-				f.Default = pref.ValueOf(int64(n))
-			case pref.Uint32Kind, pref.Fixed32Kind:
-				n, _ := strconv.ParseUint(s, 10, 32)
-				f.Default = pref.ValueOf(uint32(n))
-			case pref.Uint64Kind, pref.Fixed64Kind:
-				n, _ := strconv.ParseUint(s, 10, 64)
-				f.Default = pref.ValueOf(uint64(n))
-			case pref.FloatKind, pref.DoubleKind:
-				n, _ := strconv.ParseFloat(s, 64)
-				switch s {
-				case "nan":
-					n = math.NaN()
-				case "inf":
-					n = math.Inf(+1)
-				case "-inf":
-					n = math.Inf(-1)
-				}
-				if f.Kind == pref.FloatKind {
-					f.Default = pref.ValueOf(float32(n))
-				} else {
-					f.Default = pref.ValueOf(float64(n))
-				}
-			case pref.StringKind:
-				f.Default = pref.ValueOf(string(s))
-			case pref.BytesKind:
-				// The default value is in escaped form (C-style).
-				// TODO: Export unmarshalString in the text package to avoid this hack.
-				v, err := text.Unmarshal([]byte(`["` + s + `"]:0`))
-				if err == nil && len(v.Message()) == 1 {
-					s := v.Message()[0][0].String()
-					f.Default = pref.ValueOf([]byte(s))
-				}
-			}
-		}
-		tag = strings.TrimPrefix(tag[i:], ",")
-	}
-
-	// The generator uses the group message name instead of the field name.
-	// We obtain the real field name by lowercasing the group name.
-	if f.Kind == pref.GroupKind {
-		f.Name = pref.Name(strings.ToLower(string(f.Name)))
-	}
+	f := ptag.Unmarshal(tag, t)
 
 	// Populate EnumType and MessageType.
 	if f.EnumType == nil && f.Kind == pref.EnumKind {