blob: bd706fc012f804364446b6863e1588795eb211ac [file] [log] [blame]
// 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 (
descfield "github.com/golang/protobuf/v2/internal/descfield"
wire "github.com/golang/protobuf/v2/internal/encoding/wire"
pref "github.com/golang/protobuf/v2/reflect/protoreflect"
)
func newFileDesc(fb FileBuilder) *fileDesc {
file := &fileDesc{fileInit: fileInit{
RawDescriptor: fb.RawDescriptor,
GoTypes: fb.GoTypes,
DependencyIndexes: fb.DependencyIndexes,
}}
file.initDecls(len(fb.EnumOutputTypes), len(fb.MessageOutputTypes), len(fb.ExtensionOutputTypes))
file.unmarshalSeed(fb.RawDescriptor)
// Extended message dependencies are eagerly handled since registration
// needs this information at program init time.
for i := range file.allExtensions {
xd := &file.allExtensions[i]
xd.extendedType = file.popMessageDependency()
}
file.checkDecls()
return file
}
// initDecls pre-allocates slices for the exact number of enums, messages
// (excluding map entries), and extensions declared in the proto file.
// This is done to avoid regrowing the slice, which would change the address
// for any previously seen declaration.
//
// The alloc methods "allocates" slices by pulling from the capacity.
func (fd *fileDecls) initDecls(numEnums, numMessages, numExtensions int) {
*fd = fileDecls{
allEnums: make([]enumDesc, 0, numEnums),
allMessages: make([]messageDesc, 0, numMessages),
allExtensions: make([]extensionDesc, 0, numExtensions),
}
}
func (fd *fileDecls) allocEnums(n int) []enumDesc {
total := len(fd.allEnums)
es := fd.allEnums[total : total+n]
fd.allEnums = fd.allEnums[:total+n]
return es
}
func (fd *fileDecls) allocMessages(n int) []messageDesc {
total := len(fd.allMessages)
ms := fd.allMessages[total : total+n]
fd.allMessages = fd.allMessages[:total+n]
return ms
}
func (fd *fileDecls) allocExtensions(n int) []extensionDesc {
total := len(fd.allExtensions)
xs := fd.allExtensions[total : total+n]
fd.allExtensions = fd.allExtensions[:total+n]
return xs
}
// checkDecls performs a sanity check that the expected number of expected
// declarations matches the number that were found in the descriptor proto.
func (fd *fileDecls) checkDecls() {
if len(fd.allEnums) != cap(fd.allEnums) ||
len(fd.allMessages) != cap(fd.allMessages) ||
len(fd.allExtensions) != cap(fd.allExtensions) {
panic("mismatching cardinality")
}
}
func (fd *fileDesc) unmarshalSeed(b []byte) {
nb := getNameBuilder()
defer putNameBuilder(nb)
var prevField pref.FieldNumber
var numEnums, numMessages, numExtensions, numServices int
var posEnums, posMessages, posExtensions, posServices int
b0 := b
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 descfield.FileDescriptorProto_Name:
fd.path = nb.MakeString(v)
case descfield.FileDescriptorProto_Package:
fd.protoPackage = pref.FullName(nb.MakeString(v))
case descfield.FileDescriptorProto_EnumType:
if prevField != descfield.FileDescriptorProto_EnumType {
if numEnums > 0 {
panic("non-contiguous repeated field")
}
posEnums = len(b0) - len(b) - n - m
}
numEnums++
case descfield.FileDescriptorProto_MessageType:
if prevField != descfield.FileDescriptorProto_MessageType {
if numMessages > 0 {
panic("non-contiguous repeated field")
}
posMessages = len(b0) - len(b) - n - m
}
numMessages++
case descfield.FileDescriptorProto_Extension:
if prevField != descfield.FileDescriptorProto_Extension {
if numExtensions > 0 {
panic("non-contiguous repeated field")
}
posExtensions = len(b0) - len(b) - n - m
}
numExtensions++
case descfield.FileDescriptorProto_Service:
if prevField != descfield.FileDescriptorProto_Service {
if numServices > 0 {
panic("non-contiguous repeated field")
}
posServices = len(b0) - len(b) - n - m
}
numServices++
}
prevField = num
default:
m := wire.ConsumeFieldValue(num, typ, b)
b = b[m:]
prevField = -1 // ignore known field numbers of unknown wire type
}
}
// Must allocate all declarations before parsing each descriptor type
// to ensure we handled all descriptors in "flattened ordering".
if numEnums > 0 {
fd.enums.list = fd.allocEnums(numEnums)
}
if numMessages > 0 {
fd.messages.list = fd.allocMessages(numMessages)
}
if numExtensions > 0 {
fd.extensions.list = fd.allocExtensions(numExtensions)
}
if numServices > 0 {
fd.services.list = make([]serviceDesc, numServices)
}
if numEnums > 0 {
b := b0[posEnums:]
for i := range fd.enums.list {
_, n := wire.ConsumeVarint(b)
v, m := wire.ConsumeBytes(b[n:])
fd.enums.list[i].unmarshalSeed(v, nb, fd, fd, i)
b = b[n+m:]
}
}
if numMessages > 0 {
b := b0[posMessages:]
for i := range fd.messages.list {
_, n := wire.ConsumeVarint(b)
v, m := wire.ConsumeBytes(b[n:])
fd.messages.list[i].unmarshalSeed(v, nb, fd, fd, i)
b = b[n+m:]
}
}
if numExtensions > 0 {
b := b0[posExtensions:]
for i := range fd.extensions.list {
_, n := wire.ConsumeVarint(b)
v, m := wire.ConsumeBytes(b[n:])
fd.extensions.list[i].unmarshalSeed(v, nb, fd, fd, i)
b = b[n+m:]
}
}
if numServices > 0 {
b := b0[posServices:]
for i := range fd.services.list {
_, n := wire.ConsumeVarint(b)
v, m := wire.ConsumeBytes(b[n:])
fd.services.list[i].unmarshalSeed(v, nb, fd, fd, i)
b = b[n+m:]
}
}
}
func (ed *enumDesc) unmarshalSeed(b []byte, nb *nameBuilder, pf *fileDesc, pd pref.Descriptor, i int) {
ed.parentFile = pf
ed.parent = pd
ed.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 descfield.EnumDescriptorProto_Name:
ed.fullName = nb.AppendFullName(pd.FullName(), v)
}
default:
m := wire.ConsumeFieldValue(num, typ, b)
b = b[m:]
}
}
}
func (md *messageDesc) unmarshalSeed(b []byte, nb *nameBuilder, pf *fileDesc, pd pref.Descriptor, i int) {
md.parentFile = pf
md.parent = pd
md.index = i
var prevField pref.FieldNumber
var numEnums, numMessages, numExtensions int
var posEnums, posMessages, posExtensions int
b0 := b
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 descfield.DescriptorProto_Name:
md.fullName = nb.AppendFullName(pd.FullName(), v)
case descfield.DescriptorProto_EnumType:
if prevField != descfield.DescriptorProto_EnumType {
if numEnums > 0 {
panic("non-contiguous repeated field")
}
posEnums = len(b0) - len(b) - n - m
}
numEnums++
case descfield.DescriptorProto_NestedType:
if prevField != descfield.DescriptorProto_NestedType {
if numMessages > 0 {
panic("non-contiguous repeated field")
}
posMessages = len(b0) - len(b) - n - m
}
numMessages++
case descfield.DescriptorProto_Extension:
if prevField != descfield.DescriptorProto_Extension {
if numExtensions > 0 {
panic("non-contiguous repeated field")
}
posExtensions = len(b0) - len(b) - n - m
}
numExtensions++
}
prevField = num
default:
m := wire.ConsumeFieldValue(num, typ, b)
b = b[m:]
prevField = -1 // ignore known field numbers of unknown wire type
}
}
// Must allocate all declarations before parsing each descriptor type
// to ensure we handled all descriptors in "flattened ordering".
if numEnums > 0 {
md.enums.list = md.parentFile.allocEnums(numEnums)
}
if numMessages > 0 {
md.messages.list = md.parentFile.allocMessages(numMessages)
}
if numExtensions > 0 {
md.extensions.list = md.parentFile.allocExtensions(numExtensions)
}
if numEnums > 0 {
b := b0[posEnums:]
for i := range md.enums.list {
_, n := wire.ConsumeVarint(b)
v, m := wire.ConsumeBytes(b[n:])
md.enums.list[i].unmarshalSeed(v, nb, pf, md, i)
b = b[n+m:]
}
}
if numMessages > 0 {
b := b0[posMessages:]
for i := range md.messages.list {
_, n := wire.ConsumeVarint(b)
v, m := wire.ConsumeBytes(b[n:])
md.messages.list[i].unmarshalSeed(v, nb, pf, md, i)
b = b[n+m:]
}
}
if numExtensions > 0 {
b := b0[posExtensions:]
for i := range md.extensions.list {
_, n := wire.ConsumeVarint(b)
v, m := wire.ConsumeBytes(b[n:])
md.extensions.list[i].unmarshalSeed(v, nb, pf, md, i)
b = b[n+m:]
}
}
}
func (xd *extensionDesc) unmarshalSeed(b []byte, nb *nameBuilder, pf *fileDesc, pd pref.Descriptor, i int) {
xd.parentFile = pf
xd.parent = pd
xd.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 descfield.FieldDescriptorProto_Number:
xd.number = pref.FieldNumber(v)
}
case wire.BytesType:
v, m := wire.ConsumeBytes(b)
b = b[m:]
switch num {
case descfield.FieldDescriptorProto_Name:
xd.fullName = nb.AppendFullName(pd.FullName(), v)
}
default:
m := wire.ConsumeFieldValue(num, typ, b)
b = b[m:]
}
}
}
func (sd *serviceDesc) unmarshalSeed(b []byte, nb *nameBuilder, pf *fileDesc, pd pref.Descriptor, i int) {
sd.parentFile = pf
sd.parent = pd
sd.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 descfield.ServiceDescriptorProto_Name:
sd.fullName = nb.AppendFullName(pd.FullName(), v)
}
default:
m := wire.ConsumeFieldValue(num, typ, b)
b = b[m:]
}
}
}