summaryrefslogtreecommitdiffstats
path: root/vendor/google.golang.org/protobuf/internal/filedesc/build.go
blob: 7cac1c19016f9c4cd6c51ece7939a081aa2eda18 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright 2019 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 filedesc provides functionality for constructing descriptors.
//
// The types in this package implement interfaces in the protoreflect package
// related to protobuf descripriptors.
package filedesc

import (
	"google.golang.org/protobuf/encoding/protowire"
	"google.golang.org/protobuf/internal/genid"
	"google.golang.org/protobuf/reflect/protoreflect"
	"google.golang.org/protobuf/reflect/protoregistry"
)

// Builder construct a protoreflect.FileDescriptor from the raw descriptor.
type Builder struct {
	// GoPackagePath is the Go package path that is invoking this builder.
	GoPackagePath string

	// RawDescriptor is the wire-encoded bytes of FileDescriptorProto
	// and must be populated.
	RawDescriptor []byte

	// NumEnums is the total number of enums declared in the file.
	NumEnums int32
	// NumMessages is the total number of messages declared in the file.
	// It includes the implicit message declarations for map entries.
	NumMessages int32
	// NumExtensions is the total number of extensions declared in the file.
	NumExtensions int32
	// NumServices is the total number of services declared in the file.
	NumServices int32

	// TypeResolver resolves extension field types for descriptor options.
	// If nil, it uses protoregistry.GlobalTypes.
	TypeResolver interface {
		protoregistry.ExtensionTypeResolver
	}

	// FileRegistry is use to lookup file, enum, and message dependencies.
	// Once constructed, the file descriptor is registered here.
	// If nil, it uses protoregistry.GlobalFiles.
	FileRegistry interface {
		FindFileByPath(string) (protoreflect.FileDescriptor, error)
		FindDescriptorByName(protoreflect.FullName) (protoreflect.Descriptor, error)
		RegisterFile(protoreflect.FileDescriptor) error
	}
}

// resolverByIndex is an interface Builder.FileRegistry may implement.
// If so, it permits looking up an enum or message dependency based on the
// sub-list and element index into filetype.Builder.DependencyIndexes.
type resolverByIndex interface {
	FindEnumByIndex(int32, int32, []Enum, []Message) protoreflect.EnumDescriptor
	FindMessageByIndex(int32, int32, []Enum, []Message) protoreflect.MessageDescriptor
}

// Indexes of each sub-list in filetype.Builder.DependencyIndexes.
const (
	listFieldDeps int32 = iota
	listExtTargets
	listExtDeps
	listMethInDeps
	listMethOutDeps
)

// Out is the output of the Builder.
type Out struct {
	File protoreflect.FileDescriptor

	// Enums is all enum descriptors in "flattened ordering".
	Enums []Enum
	// Messages is all message descriptors in "flattened ordering".
	// It includes the implicit message declarations for map entries.
	Messages []Message
	// Extensions is all extension descriptors in "flattened ordering".
	Extensions []Extension
	// Service is all service descriptors in "flattened ordering".
	Services []Service
}

// Build constructs a FileDescriptor given the parameters set in Builder.
// It assumes that the inputs are well-formed and panics if any inconsistencies
// are encountered.
//
// If NumEnums+NumMessages+NumExtensions+NumServices is zero,
// then Build automatically derives them from the raw descriptor.
func (db Builder) Build() (out Out) {
	// Populate the counts if uninitialized.
	if db.NumEnums+db.NumMessages+db.NumExtensions+db.NumServices == 0 {
		db.unmarshalCounts(db.RawDescriptor, true)
	}

	// Initialize resolvers and registries if unpopulated.
	if db.TypeResolver == nil {
		db.TypeResolver = protoregistry.GlobalTypes
	}
	if db.FileRegistry == nil {
		db.FileRegistry = protoregistry.GlobalFiles
	}

	fd := newRawFile(db)
	out.File = fd
	out.Enums = fd.allEnums
	out.Messages = fd.allMessages
	out.Extensions = fd.allExtensions
	out.Services = fd.allServices

	if err := db.FileRegistry.RegisterFile(fd); err != nil {
		panic(err)
	}
	return out
}

// unmarshalCounts counts the number of enum, message, extension, and service
// declarations in the raw message, which is either a FileDescriptorProto
// or a MessageDescriptorProto depending on whether isFile is set.
func (db *Builder) unmarshalCounts(b []byte, isFile bool) {
	for len(b) > 0 {
		num, typ, n := protowire.ConsumeTag(b)
		b = b[n:]
		switch typ {
		case protowire.BytesType:
			v, m := protowire.ConsumeBytes(b)
			b = b[m:]
			if isFile {
				switch num {
				case genid.FileDescriptorProto_EnumType_field_number:
					db.NumEnums++
				case genid.FileDescriptorProto_MessageType_field_number:
					db.unmarshalCounts(v, false)
					db.NumMessages++
				case genid.FileDescriptorProto_Extension_field_number:
					db.NumExtensions++
				case genid.FileDescriptorProto_Service_field_number:
					db.NumServices++
				}
			} else {
				switch num {
				case genid.DescriptorProto_EnumType_field_number:
					db.NumEnums++
				case genid.DescriptorProto_NestedType_field_number:
					db.unmarshalCounts(v, false)
					db.NumMessages++
				case genid.DescriptorProto_Extension_field_number:
					db.NumExtensions++
				}
			}
		default:
			m := protowire.ConsumeFieldValue(num, typ, b)
			b = b[m:]
		}
	}
}