internal/fileinit: generate reflect data structures from raw descriptors

This CL takes a significantly different approach to generating support
for protobuf reflection. The previous approach involved generating a
large number of Go literals to represent the reflection information.
While that approach was correct, it resulted in too much binary bloat.

The approach taken here initializes the reflection information from
the raw descriptor proto, which is a relatively dense representation
of the protobuf reflection information. In order to keep initialization
cost low, several measures were taken:
* At program init, the bare minimum is parsed in order to initialize
naming information for enums, messages, extensions, and services declared
in the file. This is done because those top-level declarations are often
relevant for registration.
* Only upon first are most of the other data structures for protobuf
reflection actually initialized.
* Instead of using proto.Unmarshal, a hand-written unmarshaler is used.
This allows us to avoid a dependendency on the descriptor proto and also
because the API for the descriptor proto is fundamentally non-performant
since it requires an allocation for every primitive field.

At a high-level, the new implementation lives in internal/fileinit.

Several changes were made to other parts of the repository:
* cmd/protoc-gen-go:
  * Stop compressing the raw descriptors. While compression does reduce
the size of the descriptors by approximately 2x, it is a pre-mature
optimization since the descriptors themselves are around 1% of the total
binary bloat that is due to generated protobufs.
  * Seeding protobuf reflection from the raw descriptor significantly
simplifies the generator implementation since it is no longer responsible
for constructing a tree of Go literals to represent the same information.
  * We remove the generation of the shadow types and instead call
protoimpl.MessageType.MessageOf. Unfortunately, this incurs an allocation
for every call to ProtoReflect since we need to allocate a tuple that wraps
a pointer to the message value, and a pointer to message type.
* internal/impl:
  * We add a MessageType.GoType field and make it required that it is
set prior to first use. This is done so that we can avoid calling
MessageType.init except for when it is actually needed. The allows code
to call (*FooMessage)(nil).ProtoReflect().Type() without fearing that the
init code will run, possibly triggering a recursive deadlock (where the
init code depends on getting the Type of some dependency which may be
declared within the same file).
* internal/cmd/generate-types:
  * The code to generate reflect/prototype/protofile_list_gen.go was copied
and altered to generated internal/fileinit.desc_list_gen.go.

At a high-level this CL adds significant technical complexity.
However, this is offset by several possible future changes:
* The prototype package can be drastically simplified. We can probably
reimplement internal/legacy to use internal/fileinit instead, allowing us
to drop another dependency on the prototype package. As a result, we can
probably delete most of the constructor types in that package.
* With the prototype package significantly pruned, and the fact that generated
code no longer depend on depends on that package, we can consider merging
what's left of prototype into protodesc.

Change-Id: I6090f023f2e1b6afaf62bd3ae883566242e30715
Reviewed-on: https://go-review.googlesource.com/c/158539
Reviewed-by: Herbie Ong <herbie@google.com>
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
diff --git a/internal/fileinit/desc_lazy.go b/internal/fileinit/desc_lazy.go
new file mode 100644
index 0000000..fc2235c
--- /dev/null
+++ b/internal/fileinit/desc_lazy.go
@@ -0,0 +1,878 @@
+// 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 fileinit
+
+import (
+	"bytes"
+	"fmt"
+	"reflect"
+
+	defval "github.com/golang/protobuf/v2/internal/encoding/defval"
+	wire "github.com/golang/protobuf/v2/internal/encoding/wire"
+	pimpl "github.com/golang/protobuf/v2/internal/impl"
+	pvalue "github.com/golang/protobuf/v2/internal/value"
+	pref "github.com/golang/protobuf/v2/reflect/protoreflect"
+	ptype "github.com/golang/protobuf/v2/reflect/prototype"
+)
+
+func (file *fileDesc) lazyInit() *fileLazy {
+	file.once.Do(func() {
+		file.unmarshalFull(file.RawDescriptor)
+		file.resolveImports()
+		file.resolveEnums()
+		file.resolveMessages()
+		file.resolveExtensions()
+		file.resolveServices()
+		file.finishInit()
+	})
+	return file.lazy
+}
+
+func (file *fileDesc) resolveImports() {
+	// TODO: Resolve file dependencies.
+}
+
+func (file *fileDesc) resolveEnums() {
+	enumDecls := file.GoTypes[:len(file.allEnums)]
+	for i := range file.allEnums {
+		ed := &file.allEnums[i]
+
+		// Associate the EnumType with a concrete Go type.
+		enumCache := map[pref.EnumNumber]pref.Enum{}
+		ed.lazy.typ = reflect.TypeOf(enumDecls[i])
+		ed.lazy.new = func(n pref.EnumNumber) pref.Enum {
+			if v, ok := enumCache[n]; ok {
+				return v
+			}
+			v := reflect.New(ed.lazy.typ).Elem()
+			v.SetInt(int64(n))
+			return v.Interface().(pref.Enum)
+		}
+		for i := range ed.lazy.values.list {
+			n := ed.lazy.values.list[i].number
+			enumCache[n] = ed.lazy.new(n)
+		}
+	}
+}
+
+func (file *fileDesc) resolveMessages() {
+	messageDecls := file.GoTypes[len(file.allEnums):]
+	for i := range file.allMessages {
+		md := &file.allMessages[i]
+
+		// Associate the MessageType with a concrete Go type.
+		//
+		// Note that descriptors for map entries, which have no associated
+		// Go type, also implement the protoreflect.MessageType interface,
+		// but have a GoType accessor that reports nil. Calling New results
+		// in a panic, which is sensible behavior.
+		md.lazy.typ = reflect.TypeOf(messageDecls[i])
+		md.lazy.new = func() pref.Message {
+			t := md.lazy.typ.Elem()
+			return reflect.New(t).Interface().(pref.ProtoMessage).ProtoReflect()
+		}
+
+		// Resolve message field dependencies.
+		for j := range md.lazy.fields.list {
+			fd := &md.lazy.fields.list[j]
+			if fd.isWeak {
+				continue
+			}
+
+			switch fd.kind {
+			case pref.EnumKind:
+				fd.enumType = file.popEnumDependency()
+			case pref.MessageKind, pref.GroupKind:
+				fd.messageType = file.popMessageDependency()
+			}
+			fd.isMap = file.isMapEntry(fd.messageType)
+			if !fd.hasPacked && file.lazy.syntax != pref.Proto2 && fd.cardinality == pref.Repeated {
+				switch fd.kind {
+				case pref.StringKind, pref.BytesKind, pref.MessageKind, pref.GroupKind:
+					fd.isPacked = false
+				default:
+					fd.isPacked = true
+				}
+			}
+			fd.defVal.lazyInit(fd.kind, file.enumValuesOf(fd.enumType))
+		}
+	}
+}
+
+func (file *fileDesc) resolveExtensions() {
+	for i := range file.allExtensions {
+		xd := &file.allExtensions[i]
+
+		// Associate the ExtensionType with a concrete Go type.
+		var typ reflect.Type
+		switch xd.lazy.kind {
+		case pref.EnumKind, pref.MessageKind, pref.GroupKind:
+			typ = reflect.TypeOf(file.GoTypes[file.DependencyIndexes[0]])
+		default:
+			typ = goTypeForPBKind[xd.lazy.kind]
+		}
+		switch xd.lazy.cardinality {
+		case pref.Optional:
+			switch xd.lazy.kind {
+			case pref.EnumKind:
+				xd.lazy.typ = typ
+				xd.lazy.new = func() pref.Value {
+					return xd.lazy.defVal.get()
+				}
+				xd.lazy.valueOf = func(v interface{}) pref.Value {
+					ev := v.(pref.Enum)
+					return pref.ValueOf(ev.Number())
+				}
+				xd.lazy.interfaceOf = func(pv pref.Value) interface{} {
+					return xd.lazy.enumType.New(pv.Enum())
+				}
+			case pref.MessageKind, pref.GroupKind:
+				xd.lazy.typ = typ
+				xd.lazy.new = func() pref.Value {
+					return pref.ValueOf(xd.lazy.messageType.New())
+				}
+				xd.lazy.valueOf = func(v interface{}) pref.Value {
+					mv := v.(pref.ProtoMessage).ProtoReflect()
+					return pref.ValueOf(mv)
+				}
+				xd.lazy.interfaceOf = func(pv pref.Value) interface{} {
+					return pv.Message().Interface()
+				}
+			default:
+				xd.lazy.typ = goTypeForPBKind[xd.lazy.kind]
+				xd.lazy.new = func() pref.Value {
+					return xd.lazy.defVal.get()
+				}
+				xd.lazy.valueOf = func(v interface{}) pref.Value {
+					return pref.ValueOf(v)
+				}
+				xd.lazy.interfaceOf = func(pv pref.Value) interface{} {
+					return pv.Interface()
+				}
+			}
+		case pref.Repeated:
+			c := pvalue.NewConverter(typ, xd.lazy.kind)
+			xd.lazy.typ = reflect.PtrTo(reflect.SliceOf(typ))
+			xd.lazy.new = func() pref.Value {
+				v := reflect.New(xd.lazy.typ.Elem()).Interface()
+				return pref.ValueOf(pvalue.ListOf(v, c))
+			}
+			xd.lazy.valueOf = func(v interface{}) pref.Value {
+				return pref.ValueOf(pvalue.ListOf(v, c))
+			}
+			xd.lazy.interfaceOf = func(pv pref.Value) interface{} {
+				return pv.List().(pvalue.Unwrapper).ProtoUnwrap()
+			}
+		default:
+			panic(fmt.Sprintf("invalid cardinality: %v", xd.lazy.cardinality))
+		}
+
+		// Resolve extension field dependency.
+		switch xd.lazy.kind {
+		case pref.EnumKind:
+			xd.lazy.enumType = file.popEnumDependency()
+		case pref.MessageKind, pref.GroupKind:
+			xd.lazy.messageType = file.popMessageDependency()
+		}
+		xd.lazy.defVal.lazyInit(xd.lazy.kind, file.enumValuesOf(xd.lazy.enumType))
+	}
+}
+
+var goTypeForPBKind = map[pref.Kind]reflect.Type{
+	pref.BoolKind:     reflect.TypeOf(bool(false)),
+	pref.Int32Kind:    reflect.TypeOf(int32(0)),
+	pref.Sint32Kind:   reflect.TypeOf(int32(0)),
+	pref.Sfixed32Kind: reflect.TypeOf(int32(0)),
+	pref.Int64Kind:    reflect.TypeOf(int64(0)),
+	pref.Sint64Kind:   reflect.TypeOf(int64(0)),
+	pref.Sfixed64Kind: reflect.TypeOf(int64(0)),
+	pref.Uint32Kind:   reflect.TypeOf(uint32(0)),
+	pref.Fixed32Kind:  reflect.TypeOf(uint32(0)),
+	pref.Uint64Kind:   reflect.TypeOf(uint64(0)),
+	pref.Fixed64Kind:  reflect.TypeOf(uint64(0)),
+	pref.FloatKind:    reflect.TypeOf(float32(0)),
+	pref.DoubleKind:   reflect.TypeOf(float64(0)),
+	pref.StringKind:   reflect.TypeOf(string("")),
+	pref.BytesKind:    reflect.TypeOf([]byte(nil)),
+}
+
+func (file *fileDesc) resolveServices() {
+	for i := range file.services.list {
+		sd := &file.services.list[i]
+
+		// Resolve method dependencies.
+		for j := range sd.lazy.methods.list {
+			md := &sd.lazy.methods.list[j]
+			md.inputType = file.popMessageDependency()
+			md.outputType = file.popMessageDependency()
+		}
+	}
+}
+
+// isMapEntry reports whether the message is a map entry, being careful to
+// avoid calling the IsMapEntry method if the message is declared
+// within the same file (which would cause a recursive init deadlock).
+func (fd *fileDesc) isMapEntry(md pref.MessageDescriptor) bool {
+	if md == nil {
+		return false
+	}
+	if md, ok := md.(*messageDesc); ok && md.parentFile == fd {
+		return md.lazy.isMapEntry
+	}
+	return md.IsMapEntry()
+}
+
+// enumValuesOf retrieves the list of enum values for the given enum,
+// being careful to avoid calling the Values method if the enum is declared
+// within the same file (which would cause a recursive init deadlock).
+func (fd *fileDesc) enumValuesOf(ed pref.EnumDescriptor) pref.EnumValueDescriptors {
+	if ed == nil {
+		return nil
+	}
+	if ed, ok := ed.(*enumDesc); ok && ed.parentFile == fd {
+		return &ed.lazy.values
+	}
+	return ed.Values()
+}
+
+func (fd *fileDesc) popEnumDependency() pref.EnumType {
+	depIdx := fd.popDependencyIndex()
+	if depIdx < len(fd.allEnums)+len(fd.allMessages) {
+		return &fd.allEnums[depIdx]
+	} else {
+		return pimpl.Export{}.EnumTypeOf(fd.GoTypes[depIdx])
+	}
+}
+
+func (fd *fileDesc) popMessageDependency() pref.MessageType {
+	depIdx := fd.popDependencyIndex()
+	if depIdx < len(fd.allEnums)+len(fd.allMessages) {
+		return &fd.allMessages[depIdx-len(fd.allEnums)]
+	} else {
+		return pimpl.Export{}.MessageTypeOf(fd.GoTypes[depIdx])
+	}
+}
+
+func (fi *fileInit) popDependencyIndex() int {
+	depIdx := fi.DependencyIndexes[0]
+	fi.DependencyIndexes = fi.DependencyIndexes[1:]
+	return int(depIdx)
+}
+
+func (fi *fileInit) finishInit() {
+	if len(fi.DependencyIndexes) > 0 {
+		panic("unused dependencies")
+	}
+	*fi = fileInit{} // clear fileInit for GC to reclaim resources
+}
+
+type defaultValue struct {
+	has   bool
+	val   pref.Value
+	enum  pref.EnumValueDescriptor
+	check func() // only set for non-empty bytes
+}
+
+func (dv *defaultValue) get() pref.Value {
+	if dv.check != nil {
+		dv.check()
+	}
+	return dv.val
+}
+
+func (dv *defaultValue) lazyInit(k pref.Kind, eds pref.EnumValueDescriptors) {
+	if dv.has {
+		switch k {
+		case pref.EnumKind:
+			// File descriptors always store default enums by name.
+			dv.enum = eds.ByName(pref.Name(dv.val.String()))
+			dv.val = pref.ValueOf(dv.enum.Number())
+		case pref.BytesKind:
+			// Store a copy of the default bytes, so that we can detect
+			// accidental mutations of the original value.
+			b := append([]byte(nil), dv.val.Bytes()...)
+			dv.check = func() {
+				if !bytes.Equal(b, dv.val.Bytes()) {
+					// TODO: Avoid panic if we're running with the race detector
+					// and instead spawn a goroutine that periodically resets
+					// this value back to the original to induce a race.
+					panic("detected mutation on the default bytes")
+				}
+			}
+		}
+	} else {
+		switch k {
+		case pref.BoolKind:
+			dv.val = pref.ValueOf(false)
+		case pref.Int32Kind, pref.Sint32Kind, pref.Sfixed32Kind:
+			dv.val = pref.ValueOf(int32(0))
+		case pref.Int64Kind, pref.Sint64Kind, pref.Sfixed64Kind:
+			dv.val = pref.ValueOf(int64(0))
+		case pref.Uint32Kind, pref.Fixed32Kind:
+			dv.val = pref.ValueOf(uint32(0))
+		case pref.Uint64Kind, pref.Fixed64Kind:
+			dv.val = pref.ValueOf(uint64(0))
+		case pref.FloatKind:
+			dv.val = pref.ValueOf(float32(0))
+		case pref.DoubleKind:
+			dv.val = pref.ValueOf(float64(0))
+		case pref.StringKind:
+			dv.val = pref.ValueOf(string(""))
+		case pref.BytesKind:
+			dv.val = pref.ValueOf([]byte(nil))
+		case pref.EnumKind:
+			dv.enum = eds.Get(0)
+			dv.val = pref.ValueOf(dv.enum.Number())
+		}
+	}
+}
+
+func (fd *fileDesc) unmarshalFull(b []byte) {
+	nb := getNameBuilder()
+	defer putNameBuilder(nb)
+
+	var hasSyntax bool
+	var enumIdx, messageIdx, extensionIdx, serviceIdx int
+	fd.lazy = &fileLazy{byName: make(map[pref.FullName]pref.Descriptor)}
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fileDesc_PublicImports:
+				fd.lazy.imports[v].IsPublic = true
+			case fileDesc_WeakImports:
+				fd.lazy.imports[v].IsWeak = true
+			}
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fileDesc_Syntax:
+				hasSyntax = true
+				switch string(v) {
+				case "proto2":
+					fd.lazy.syntax = pref.Proto2
+				case "proto3":
+					fd.lazy.syntax = pref.Proto3
+				default:
+					panic("invalid syntax")
+				}
+			case fileDesc_Imports:
+				fd.lazy.imports = append(fd.lazy.imports, pref.FileImport{
+					FileDescriptor: ptype.PlaceholderFile(nb.MakeString(v), ""),
+				})
+			case fileDesc_Enums:
+				fd.enums.list[enumIdx].unmarshalFull(v, nb)
+				enumIdx++
+			case fileDesc_Messages:
+				fd.messages.list[messageIdx].unmarshalFull(v, nb)
+				messageIdx++
+			case fileDesc_Extensions:
+				fd.extensions.list[extensionIdx].unmarshalFull(v, nb)
+				extensionIdx++
+			case fileDesc_Services:
+				fd.services.list[serviceIdx].unmarshalFull(v, nb)
+				serviceIdx++
+			case fileDesc_Options:
+				fd.lazy.options = append(fd.lazy.options, v...)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+
+	// If syntax is missing, it is assumed to be proto2.
+	if !hasSyntax {
+		fd.lazy.syntax = pref.Proto2
+	}
+}
+
+func (ed *enumDesc) unmarshalFull(b []byte, nb *nameBuilder) {
+	var rawValues [][]byte
+	ed.lazy = new(enumLazy)
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case enumDesc_Values:
+				rawValues = append(rawValues, v)
+			case enumDesc_ReservedNames:
+				ed.lazy.resvNames.list = append(ed.lazy.resvNames.list, pref.Name(nb.MakeString(v)))
+			case enumDesc_ReservedRanges:
+				ed.lazy.resvRanges.list = append(ed.lazy.resvRanges.list, unmarshalEnumReservedRange(v))
+			case enumDesc_Options:
+				ed.lazy.options = append(ed.lazy.options, v...)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+
+	if len(rawValues) > 0 {
+		ed.lazy.values.list = make([]enumValueDesc, len(rawValues))
+		for i, b := range rawValues {
+			ed.lazy.values.list[i].unmarshalFull(b, nb, ed.parentFile, ed, i)
+		}
+	}
+
+	ed.parentFile.lazy.byName[ed.FullName()] = ed
+}
+
+func unmarshalEnumReservedRange(b []byte) (r [2]pref.EnumNumber) {
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case enumReservedRange_Start:
+				r[0] = pref.EnumNumber(v)
+			case enumReservedRange_End:
+				r[1] = pref.EnumNumber(v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	return r
+}
+
+func (vd *enumValueDesc) unmarshalFull(b []byte, nb *nameBuilder, pf *fileDesc, pd pref.Descriptor, i int) {
+	vd.parentFile = pf
+	vd.parent = pd
+	vd.index = i
+
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case enumValueDesc_Number:
+				vd.number = pref.EnumNumber(v)
+			}
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case enumValueDesc_Name:
+				vd.fullName = nb.AppendFullName(pd.FullName(), v)
+			case enumValueDesc_Options:
+				vd.options = append(vd.options, v...)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+
+	vd.parentFile.lazy.byName[vd.FullName()] = vd
+}
+
+func (md *messageDesc) unmarshalFull(b []byte, nb *nameBuilder) {
+	var rawFields, rawOneofs [][]byte
+	var enumIdx, messageIdx, extensionIdx int
+	md.lazy = new(messageLazy)
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case messageDesc_Fields:
+				rawFields = append(rawFields, v)
+			case messageDesc_Oneofs:
+				rawOneofs = append(rawOneofs, v)
+			case messageDesc_ReservedNames:
+				md.lazy.resvNames.list = append(md.lazy.resvNames.list, pref.Name(nb.MakeString(v)))
+			case messageDesc_ReservedRanges:
+				md.lazy.resvRanges.list = append(md.lazy.resvRanges.list, unmarshalMessageReservedRange(v))
+			case messageDesc_ExtensionRanges:
+				r, opts := unmarshalMessageExtensionRange(v)
+				md.lazy.extRanges.list = append(md.lazy.extRanges.list, r)
+				md.lazy.extRangeOptions = append(md.lazy.extRangeOptions, opts)
+			case messageDesc_Enums:
+				md.enums.list[enumIdx].unmarshalFull(v, nb)
+				enumIdx++
+			case messageDesc_Messages:
+				md.messages.list[messageIdx].unmarshalFull(v, nb)
+				messageIdx++
+			case messageDesc_Extensions:
+				md.extensions.list[extensionIdx].unmarshalFull(v, nb)
+				extensionIdx++
+			case messageDesc_Options:
+				md.unmarshalOptions(v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+
+	if len(rawFields) > 0 || len(rawOneofs) > 0 {
+		md.lazy.fields.list = make([]fieldDesc, len(rawFields))
+		md.lazy.oneofs.list = make([]oneofDesc, len(rawOneofs))
+		for i, b := range rawFields {
+			fd := &md.lazy.fields.list[i]
+			fd.unmarshalFull(b, nb, md.parentFile, md, i)
+			if fd.cardinality == pref.Required {
+				md.lazy.reqNumbers.list = append(md.lazy.reqNumbers.list, fd.number)
+			}
+		}
+		for i, b := range rawOneofs {
+			od := &md.lazy.oneofs.list[i]
+			od.unmarshalFull(b, nb, md.parentFile, md, i)
+		}
+	}
+
+	md.parentFile.lazy.byName[md.FullName()] = md
+}
+
+func (md *messageDesc) unmarshalOptions(b []byte) {
+	md.lazy.options = append(md.lazy.options, b...)
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case messageOptions_IsMapEntry:
+				md.lazy.isMapEntry = wire.DecodeBool(v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+}
+
+func unmarshalMessageReservedRange(b []byte) (r [2]pref.FieldNumber) {
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case messageReservedRange_Start:
+				r[0] = pref.FieldNumber(v)
+			case messageReservedRange_End:
+				r[1] = pref.FieldNumber(v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	return r
+}
+
+func unmarshalMessageExtensionRange(b []byte) (r [2]pref.FieldNumber, opts []byte) {
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case messageExtensionRange_Start:
+				r[0] = pref.FieldNumber(v)
+			case messageExtensionRange_End:
+				r[1] = pref.FieldNumber(v)
+			}
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case messageExtensionRange_Options:
+				opts = append(opts, v...)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+	return r, opts
+}
+
+func (fd *fieldDesc) unmarshalFull(b []byte, nb *nameBuilder, pf *fileDesc, pd pref.Descriptor, i int) {
+	fd.parentFile = pf
+	fd.parent = pd
+	fd.index = i
+
+	var rawDefVal []byte
+	var rawTypeName []byte
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldDesc_Number:
+				fd.number = pref.FieldNumber(v)
+			case fieldDesc_Cardinality:
+				fd.cardinality = pref.Cardinality(v)
+			case fieldDesc_Kind:
+				fd.kind = pref.Kind(v)
+			case fieldDesc_OneofIndex:
+				// In messageDesc.UnmarshalFull, we allocate slices for both
+				// the field and oneof descriptors before unmarshaling either
+				// of them. This ensures pointers to slice elements are stable.
+				od := &pd.(*messageDesc).lazy.oneofs.list[v]
+				od.fields.list = append(od.fields.list, fd)
+				if fd.oneofType != nil {
+					panic("oneof type already set")
+				}
+				fd.oneofType = od
+			}
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldDesc_Name:
+				fd.fullName = nb.AppendFullName(pd.FullName(), v)
+			case fieldDesc_JSONName:
+				fd.hasJSONName = true
+				fd.jsonName = nb.MakeString(v)
+			case fieldDesc_Default:
+				fd.defVal.has = true
+				rawDefVal = v
+			case fieldDesc_TypeName:
+				rawTypeName = v
+			case fieldDesc_Options:
+				fd.unmarshalOptions(v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+
+	if !fd.hasJSONName {
+		fd.jsonName = nb.MakeJSONName(fd.Name())
+	}
+	if rawDefVal != nil {
+		var err error
+		fd.defVal.val, err = defval.Unmarshal(string(rawDefVal), fd.kind, defval.Descriptor)
+		if err != nil {
+			panic(err)
+		}
+	}
+	if fd.isWeak {
+		if len(rawTypeName) == 0 || rawTypeName[0] != '.' {
+			panic("weak target name must be fully qualified")
+		}
+		fd.messageType = ptype.PlaceholderMessage(pref.FullName(rawTypeName[1:]))
+	}
+
+	fd.parentFile.lazy.byName[fd.FullName()] = fd
+}
+
+func (fd *fieldDesc) unmarshalOptions(b []byte) {
+	fd.options = append(fd.options, b...)
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldOptions_IsPacked:
+				fd.hasPacked = true
+				fd.isPacked = wire.DecodeBool(v)
+			case fieldOptions_IsWeak:
+				fd.isWeak = wire.DecodeBool(v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+}
+
+func (od *oneofDesc) unmarshalFull(b []byte, nb *nameBuilder, pf *fileDesc, pd pref.Descriptor, i int) {
+	od.parentFile = pf
+	od.parent = pd
+	od.index = i
+
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case oneofDesc_Name:
+				od.fullName = nb.AppendFullName(pd.FullName(), v)
+			case oneofDesc_Options:
+				od.options = append(od.options, v...)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+
+	od.parentFile.lazy.byName[od.FullName()] = od
+}
+
+func (xd *extensionDesc) unmarshalFull(b []byte, nb *nameBuilder) {
+	var rawDefVal []byte
+	xd.lazy = new(extensionLazy)
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldDesc_Cardinality:
+				xd.lazy.cardinality = pref.Cardinality(v)
+			case fieldDesc_Kind:
+				xd.lazy.kind = pref.Kind(v)
+			}
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case fieldDesc_JSONName:
+				xd.lazy.hasJSONName = true
+				xd.lazy.jsonName = nb.MakeString(v)
+			case fieldDesc_Default:
+				xd.lazy.defVal.has = true
+				rawDefVal = v
+			case fieldDesc_Options:
+				xd.unmarshalOptions(v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+
+	if rawDefVal != nil {
+		var err error
+		xd.lazy.defVal.val, err = defval.Unmarshal(string(rawDefVal), xd.lazy.kind, defval.Descriptor)
+		if err != nil {
+			panic(err)
+		}
+	}
+
+	xd.parentFile.lazy.byName[xd.FullName()] = xd
+}
+
+func (xd *extensionDesc) unmarshalOptions(b []byte) {
+	xd.lazy.options = append(xd.lazy.options, b...)
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case fieldOptions_IsPacked:
+				xd.lazy.isPacked = wire.DecodeBool(v)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+}
+
+func (sd *serviceDesc) unmarshalFull(b []byte, nb *nameBuilder) {
+	var rawMethods [][]byte
+	sd.lazy = new(serviceLazy)
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case serviceDesc_Methods:
+				rawMethods = append(rawMethods, v)
+			case serviceDesc_Options:
+				sd.lazy.options = append(sd.lazy.options, v...)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+
+	if len(rawMethods) > 0 {
+		sd.lazy.methods.list = make([]methodDesc, len(rawMethods))
+		for i, b := range rawMethods {
+			sd.lazy.methods.list[i].unmarshalFull(b, nb, sd.parentFile, sd, i)
+		}
+	}
+
+	sd.parentFile.lazy.byName[sd.FullName()] = sd
+}
+
+func (md *methodDesc) unmarshalFull(b []byte, nb *nameBuilder, pf *fileDesc, pd pref.Descriptor, i int) {
+	md.parentFile = pf
+	md.parent = pd
+	md.index = i
+
+	for len(b) > 0 {
+		num, typ, n := wire.ConsumeTag(b)
+		b = b[n:]
+		switch typ {
+		case wire.VarintType:
+			v, m := wire.ConsumeVarint(b)
+			b = b[m:]
+			switch num {
+			case methodDesc_IsStreamingClient:
+				md.isStreamingClient = wire.DecodeBool(v)
+			case methodDesc_IsStreamingServer:
+				md.isStreamingServer = wire.DecodeBool(v)
+			}
+		case wire.BytesType:
+			v, m := wire.ConsumeBytes(b)
+			b = b[m:]
+			switch num {
+			case methodDesc_Name:
+				md.fullName = nb.AppendFullName(pd.FullName(), v)
+			case methodDesc_Options:
+				md.options = append(md.options, v...)
+			}
+		default:
+			m := wire.ConsumeFieldValue(num, typ, b)
+			b = b[m:]
+		}
+	}
+
+	md.parentFile.lazy.byName[md.FullName()] = md
+}