EmbeddedProto 4.0.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- EmbeddedProto/EmbeddedProto.py +122 -0
- EmbeddedProto/Features.py +255 -0
- EmbeddedProto/Field.py +1295 -0
- EmbeddedProto/Oneof.py +74 -0
- EmbeddedProto/ProtoFile.py +210 -0
- EmbeddedProto/TypeDefinitions.py +367 -0
- EmbeddedProto/__init__.py +0 -0
- EmbeddedProto/__main__.py +28 -0
- EmbeddedProto/config.py +173 -0
- EmbeddedProto/custom_header.py +513 -0
- EmbeddedProto/embedded_proto_options.proto +62 -0
- EmbeddedProto/embedded_proto_options_pb2.py +37 -0
- EmbeddedProto/field_options.py +344 -0
- EmbeddedProto/main.py +316 -0
- EmbeddedProto/src/EmbeddedProto/BytesStringCallback.h +476 -0
- EmbeddedProto/src/EmbeddedProto/Defines.h +118 -0
- EmbeddedProto/src/EmbeddedProto/EmptyArray.h +81 -0
- EmbeddedProto/src/EmbeddedProto/Errors.h +53 -0
- EmbeddedProto/src/EmbeddedProto/FieldStringBytes.h +793 -0
- EmbeddedProto/src/EmbeddedProto/Fields.h +832 -0
- EmbeddedProto/src/EmbeddedProto/Functional.h +412 -0
- EmbeddedProto/src/EmbeddedProto/MessageCallback.h +688 -0
- EmbeddedProto/src/EmbeddedProto/MessageInterface.h +792 -0
- EmbeddedProto/src/EmbeddedProto/MessageSizeCalculator.h +109 -0
- EmbeddedProto/src/EmbeddedProto/MessageState.h +207 -0
- EmbeddedProto/src/EmbeddedProto/ReadBufferFixedSize.h +204 -0
- EmbeddedProto/src/EmbeddedProto/ReadBufferInterface.h +181 -0
- EmbeddedProto/src/EmbeddedProto/ReadBufferSection.h +232 -0
- EmbeddedProto/src/EmbeddedProto/RepeatedField.h +1053 -0
- EmbeddedProto/src/EmbeddedProto/RepeatedFieldCallback.h +432 -0
- EmbeddedProto/src/EmbeddedProto/RepeatedFieldFixedSize.h +367 -0
- EmbeddedProto/src/EmbeddedProto/Version.h +39 -0
- EmbeddedProto/src/EmbeddedProto/WireFormatter.h +852 -0
- EmbeddedProto/src/EmbeddedProto/WriteBufferFixedSize.h +114 -0
- EmbeddedProto/src/EmbeddedProto/WriteBufferInterface.h +134 -0
- EmbeddedProto/src/EmbeddedProto.h +55 -0
- EmbeddedProto/templates/FieldBasic_Deserialize.h.jinja2 +26 -0
- EmbeddedProto/templates/FieldBasic_GetSet.h.jinja2 +89 -0
- EmbeddedProto/templates/FieldBytes_GetSet.h.jinja2 +86 -0
- EmbeddedProto/templates/FieldEnum_Deserialize.h.jinja2 +50 -0
- EmbeddedProto/templates/FieldEnum_GetSet.h.jinja2 +93 -0
- EmbeddedProto/templates/FieldErrorRecursive_GetSet.h.jinja2 +29 -0
- EmbeddedProto/templates/FieldMap_GetSet.h.jinja2 +203 -0
- EmbeddedProto/templates/FieldMsg_Deserialize.h.jinja2 +44 -0
- EmbeddedProto/templates/FieldMsg_GetSet.h.jinja2 +93 -0
- EmbeddedProto/templates/FieldRepeated_GetSet.h.jinja2 +94 -0
- EmbeddedProto/templates/FieldString_GetSet.h.jinja2 +86 -0
- EmbeddedProto/templates/Field_DeserializePartial.h.jinja2 +33 -0
- EmbeddedProto/templates/Field_Serialize.h.jinja2 +112 -0
- EmbeddedProto/templates/Field_SerializePartial.h.jinja2 +119 -0
- EmbeddedProto/templates/Header.h.jinja2 +108 -0
- EmbeddedProto/templates/TypeDefEnum.h.jinja2 +45 -0
- EmbeddedProto/templates/TypeDefMsg.h.jinja2 +791 -0
- EmbeddedProto/templates/TypeOneof.h.jinja2 +178 -0
- EmbeddedProto/version.json +3 -0
- embeddedproto-4.0.0b1.dist-info/METADATA +141 -0
- embeddedproto-4.0.0b1.dist-info/RECORD +63 -0
- embeddedproto-4.0.0b1.dist-info/WHEEL +5 -0
- embeddedproto-4.0.0b1.dist-info/entry_points.txt +3 -0
- embeddedproto-4.0.0b1.dist-info/licenses/LICENSE +30 -0
- embeddedproto-4.0.0b1.dist-info/licenses/LICENSES/GPL-3.0-only.txt +674 -0
- embeddedproto-4.0.0b1.dist-info/licenses/LICENSES/LicenseRef-EmbeddedProto-Commercial.txt +12 -0
- embeddedproto-4.0.0b1.dist-info/top_level.txt +1 -0
EmbeddedProto/Field.py
ADDED
|
@@ -0,0 +1,1295 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Copyright (C) 2020-2026 Embedded AMS B.V. - All Rights Reserved
|
|
3
|
+
#
|
|
4
|
+
# This file is part of Embedded Proto.
|
|
5
|
+
#
|
|
6
|
+
# Embedded Proto is dual licensed. You may use it under the terms of the
|
|
7
|
+
# GNU General Public License version 3 (GPLv3) as published by the Free
|
|
8
|
+
# Software Foundation, or under a commercial license from Embedded AMS B.V.
|
|
9
|
+
#
|
|
10
|
+
# Under the GPLv3 you must release the source code of
|
|
11
|
+
# any product you distribute that includes Embedded Proto or code
|
|
12
|
+
# generated by it. A commercial license removes that obligation.
|
|
13
|
+
# See <https://embeddedproto.com/pricing/>.
|
|
14
|
+
#
|
|
15
|
+
# Embedded Proto is distributed WITHOUT ANY WARRANTY; without even the
|
|
16
|
+
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
17
|
+
# See the LICENSE file in the root of the repository for details.
|
|
18
|
+
#
|
|
19
|
+
# SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-EmbeddedProto-Commercial
|
|
20
|
+
#
|
|
21
|
+
# Embedded AMS B.V., Hoorn, the Netherlands - info at EmbeddedProto dot com
|
|
22
|
+
#
|
|
23
|
+
|
|
24
|
+
from google.protobuf.descriptor_pb2 import FieldDescriptorProto
|
|
25
|
+
from . import embedded_proto_options_pb2
|
|
26
|
+
from . import field_options
|
|
27
|
+
from .Features import FieldPresence
|
|
28
|
+
import copy
|
|
29
|
+
import sys
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# This class is the base class for any kind of field used in protobuf messages.
|
|
33
|
+
class Field:
|
|
34
|
+
def __init__(self, proto_descriptor, parent_msg, template_filename, oneof=None):
|
|
35
|
+
# A reference to the FieldDescriptorProto object which defines this field.
|
|
36
|
+
self.descriptor = proto_descriptor
|
|
37
|
+
|
|
38
|
+
# A reference to the parent message in which this field is defined.
|
|
39
|
+
self.parent = parent_msg
|
|
40
|
+
|
|
41
|
+
# Editions feature resolution. The parent message holds the resolver and
|
|
42
|
+
# its own resolved feature set; this field merges its own explicit
|
|
43
|
+
# overrides on top to obtain the resolved feature set for this field. The
|
|
44
|
+
# resolved values are consumed by the individual feature tickets; ED-1 only
|
|
45
|
+
# makes them available.
|
|
46
|
+
self.feature_resolver = getattr(parent_msg, "feature_resolver", None)
|
|
47
|
+
if self.feature_resolver is not None:
|
|
48
|
+
self.resolved_features = self.feature_resolver.merge(
|
|
49
|
+
parent_msg.features, self.descriptor.options, self.descriptor.name)
|
|
50
|
+
else:
|
|
51
|
+
self.resolved_features = None
|
|
52
|
+
|
|
53
|
+
# Determine field presence from the resolved editions feature.
|
|
54
|
+
# EXPLICIT -> track presence with a bit and generate has_*()
|
|
55
|
+
# (reuse the existing proto3_optional infrastructure).
|
|
56
|
+
# IMPLICIT -> proto3 behavior (serialize only when != default).
|
|
57
|
+
# LEGACY_REQUIRED -> always serialize, no presence bit, no absence check.
|
|
58
|
+
# Repeated fields and real oneof members never carry a presence bit
|
|
59
|
+
# (presence comes from the repetition / the oneof discriminator) so they
|
|
60
|
+
# keep the proto3 behavior regardless of the resolved feature.
|
|
61
|
+
self.always_serialize = False
|
|
62
|
+
is_repeated = (FieldDescriptorProto.LABEL_REPEATED == self.descriptor.label)
|
|
63
|
+
in_real_oneof = oneof is not None
|
|
64
|
+
if (self.resolved_features is not None) and (not is_repeated) and (not in_real_oneof):
|
|
65
|
+
presence = self.resolved_features["field_presence"]
|
|
66
|
+
if FieldPresence.LEGACY_REQUIRED == presence:
|
|
67
|
+
self.optional = self.descriptor.proto3_optional
|
|
68
|
+
self.always_serialize = True
|
|
69
|
+
else:
|
|
70
|
+
self.optional = (FieldPresence.EXPLICIT == presence) or self.descriptor.proto3_optional
|
|
71
|
+
else:
|
|
72
|
+
self.optional = self.descriptor.proto3_optional
|
|
73
|
+
|
|
74
|
+
# If this field is part of an oneof this is the reference to it.
|
|
75
|
+
self.oneof = oneof
|
|
76
|
+
|
|
77
|
+
self.name = self.descriptor.name
|
|
78
|
+
self.variable_name = self.name + "_"
|
|
79
|
+
self.variable_id_name = self.name.upper()
|
|
80
|
+
self.variable_id = self.descriptor.number
|
|
81
|
+
self.template_file = template_filename
|
|
82
|
+
|
|
83
|
+
self.of_type_enum = FieldDescriptorProto.TYPE_ENUM == proto_descriptor.type
|
|
84
|
+
|
|
85
|
+
# The Embedded Proto options of this field, written inline in the .proto, supplied by an external options
|
|
86
|
+
# file, or both. Resolved once here so every option read below uses one and the same effective set.
|
|
87
|
+
self.embedded_proto_options = self.resolve_embedded_proto_options()
|
|
88
|
+
|
|
89
|
+
# Whether the user wants to supply the storage type for this field via a template parameter.
|
|
90
|
+
self.custom_storage = False
|
|
91
|
+
# Whether the field streams its elements through user callbacks (the callbackStorage option).
|
|
92
|
+
self.callback_storage = False
|
|
93
|
+
if self.embedded_proto_options is not None:
|
|
94
|
+
self.custom_storage = self.embedded_proto_options.customStorage
|
|
95
|
+
self.callback_storage = self.embedded_proto_options.callbackStorage
|
|
96
|
+
|
|
97
|
+
# Remember whether this is a repeated field; it selects the callback storage kind
|
|
98
|
+
# (repeated scalar/enum -> RepeatedFieldCallback, singular bytes/string -> BytesStringCallback).
|
|
99
|
+
self.is_repeated = is_repeated
|
|
100
|
+
|
|
101
|
+
# callbackStorage supports repeated scalar/enum fields and singular bytes/string fields. Reject
|
|
102
|
+
# every other placement with a generator error so unsupported configurations never emit wrong code.
|
|
103
|
+
if self.callback_storage:
|
|
104
|
+
self.reject_unsupported_callback_storage(is_repeated, in_real_oneof)
|
|
105
|
+
|
|
106
|
+
# Combine the options written inline in the .proto with those from an external options file. An entry in the
|
|
107
|
+
# options file wins over the same option inline: the .proto is a shared contract and may not be yours to edit,
|
|
108
|
+
# which is the reason the file exists. Returns None when neither source sets anything.
|
|
109
|
+
def resolve_embedded_proto_options(self):
|
|
110
|
+
inline = None
|
|
111
|
+
if self.descriptor.options.HasExtension(embedded_proto_options_pb2.options):
|
|
112
|
+
inline = self.descriptor.options.Extensions[embedded_proto_options_pb2.options]
|
|
113
|
+
|
|
114
|
+
# The options file reaches a field through its parent message, the same way the feature resolver does.
|
|
115
|
+
options_file = getattr(self.parent, "options_file", None)
|
|
116
|
+
if not options_file:
|
|
117
|
+
return inline
|
|
118
|
+
|
|
119
|
+
# The scope of the parent message, the package parts and message names leading up to this field.
|
|
120
|
+
scope = self.parent.scope.get_list_of_scope_str()
|
|
121
|
+
from_file = options_file.resolve(scope, self.descriptor.name)
|
|
122
|
+
if not from_file:
|
|
123
|
+
return inline
|
|
124
|
+
|
|
125
|
+
result = embedded_proto_options_pb2.Options()
|
|
126
|
+
if inline is not None:
|
|
127
|
+
result.CopyFrom(inline)
|
|
128
|
+
|
|
129
|
+
for name, value in from_file.items():
|
|
130
|
+
# The Options message has no field presence, an option still at its default was never set inline. Report
|
|
131
|
+
# an override rather than let the .proto and the options file disagree silently.
|
|
132
|
+
previous = getattr(result, name)
|
|
133
|
+
if previous:
|
|
134
|
+
field_options.warn(options_file.describe_sources() + ": " + ".".join(scope) + "."
|
|
135
|
+
+ self.descriptor.name + ": " + name + " = " + str(value)
|
|
136
|
+
+ " overrides " + str(previous) + " set in the proto file.")
|
|
137
|
+
setattr(result, name, value)
|
|
138
|
+
|
|
139
|
+
return result
|
|
140
|
+
|
|
141
|
+
# Returns true when the user supplies the storage type for this field (the customStorage option).
|
|
142
|
+
def has_custom_storage(self):
|
|
143
|
+
return self.custom_storage
|
|
144
|
+
|
|
145
|
+
# Returns true when the field streams its elements through user callbacks (the callbackStorage option).
|
|
146
|
+
def has_callback_storage(self):
|
|
147
|
+
return self.callback_storage
|
|
148
|
+
|
|
149
|
+
# Returns true when a callbackStorage field serializes EXPANDED (one tag per element). This holds for
|
|
150
|
+
# repeated scalar/enum callback storage; a singular bytes/string callback stays LEN framed instead.
|
|
151
|
+
def callback_is_expanded(self):
|
|
152
|
+
return False
|
|
153
|
+
|
|
154
|
+
# Returns true when a callbackStorage field streams a repeated message (through MessageCallback).
|
|
155
|
+
def callback_is_message(self):
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
# Returns true when a callbackStorage field streams its elements length delimited instead of as
|
|
159
|
+
# DELIMITED groups. Only a map does; its entries have to stay readable by a standard protoc peer.
|
|
160
|
+
def callback_is_len_expanded(self):
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
# Raise a generator error when callbackStorage is set on a field kind that is not yet supported.
|
|
164
|
+
def reject_unsupported_callback_storage(self, is_repeated, in_real_oneof):
|
|
165
|
+
# A map streams its entries length delimited instead of as DELIMITED groups, so the rules
|
|
166
|
+
# below do not apply to it. This is checked on the descriptor rather than on the class
|
|
167
|
+
# because a repeated field builds its element from that very same descriptor, which would
|
|
168
|
+
# otherwise run these checks a second time for the entry.
|
|
169
|
+
if Field.find_map_entry_descriptor(self.descriptor, self.parent) is not None:
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
location = self.parent.name + "." + self.descriptor.name
|
|
173
|
+
is_message = FieldDescriptorProto.TYPE_MESSAGE == self.descriptor.type
|
|
174
|
+
is_string_or_bytes = self.descriptor.type in (FieldDescriptorProto.TYPE_STRING,
|
|
175
|
+
FieldDescriptorProto.TYPE_BYTES)
|
|
176
|
+
if in_real_oneof:
|
|
177
|
+
raise Exception(location + ": callbackStorage cannot be used on oneof members.")
|
|
178
|
+
elif is_repeated:
|
|
179
|
+
if is_message:
|
|
180
|
+
# Repeated message streaming uses MessageCallback, which frames each element
|
|
181
|
+
# DELIMITED (a group carries no size prefix, so there is no size pre-pass). Require
|
|
182
|
+
# the field to be DELIMITED-encoded; a LEN element would need the pre-pass a stream
|
|
183
|
+
# cannot provide.
|
|
184
|
+
from .Features import MessageEncoding
|
|
185
|
+
is_delimited = (self.resolved_features is not None) and \
|
|
186
|
+
(MessageEncoding.DELIMITED == self.resolved_features["message_encoding"])
|
|
187
|
+
if not is_delimited:
|
|
188
|
+
raise Exception(location + ": callbackStorage on a repeated message field requires "
|
|
189
|
+
"features.message_encoding = DELIMITED.")
|
|
190
|
+
elif is_string_or_bytes:
|
|
191
|
+
# Repeated string/bytes streaming is not built yet.
|
|
192
|
+
raise Exception(location + ": callbackStorage on repeated string or bytes fields is not "
|
|
193
|
+
"yet supported; only repeated scalar, enum and message fields are.")
|
|
194
|
+
elif is_string_or_bytes:
|
|
195
|
+
# Singular bytes/string streams through BytesStringCallback. Presence-on-bind for an
|
|
196
|
+
# explicit-presence (optional) field is a later extension, so reject it for now.
|
|
197
|
+
if self.optional:
|
|
198
|
+
raise Exception(location + ": callbackStorage on an explicit-presence (optional) bytes "
|
|
199
|
+
"or string field is not yet supported; use an implicit-presence field.")
|
|
200
|
+
elif is_message:
|
|
201
|
+
raise Exception(location + ": callbackStorage on message fields is not yet supported.")
|
|
202
|
+
else:
|
|
203
|
+
raise Exception(location + ": callbackStorage on a singular scalar field is not supported; "
|
|
204
|
+
"use a repeated scalar/enum field or a bytes/string field.")
|
|
205
|
+
|
|
206
|
+
# The name of the C++ template parameter exposing the user supplied storage type.
|
|
207
|
+
def get_storage_type_param_str(self):
|
|
208
|
+
return self.parent.name + "_" + self.variable_name + "STORAGE"
|
|
209
|
+
|
|
210
|
+
# The C++ base class a customStorage type must derive from. Returns an empty string for field kinds that do not
|
|
211
|
+
# support customStorage. Used to emit a static_assert in the generated code.
|
|
212
|
+
def get_storage_base_type(self):
|
|
213
|
+
return ""
|
|
214
|
+
|
|
215
|
+
@staticmethod
|
|
216
|
+
# Return the descriptor of the synthetic entry message when this field is a map, None otherwise.
|
|
217
|
+
#
|
|
218
|
+
# Protoc rewrites "map<K,V> foo = 1;" into a repeated message field whose element is a nested
|
|
219
|
+
# message flagged map_entry, holding the key as field one and the value as field two. That entry
|
|
220
|
+
# is always nested directly in the message declaring the map, so it can be found by name among
|
|
221
|
+
# the raw nested types of the parent. The raw descriptors are used on purpose: this runs from
|
|
222
|
+
# MessageDefinition.__init__ while the fields are built, before the type definitions are matched,
|
|
223
|
+
# so the generated entry class can not be resolved through the usual type lookup yet.
|
|
224
|
+
def find_map_entry_descriptor(proto_descriptor, parent_msg):
|
|
225
|
+
result = None
|
|
226
|
+
if (FieldDescriptorProto.LABEL_REPEATED == proto_descriptor.label) and \
|
|
227
|
+
(FieldDescriptorProto.TYPE_MESSAGE == proto_descriptor.type):
|
|
228
|
+
entry_name = proto_descriptor.type_name.rsplit(".", 1)[-1]
|
|
229
|
+
for nested in parent_msg.descriptor.nested_type:
|
|
230
|
+
if (nested.name == entry_name) and nested.options.map_entry:
|
|
231
|
+
result = nested
|
|
232
|
+
return result
|
|
233
|
+
|
|
234
|
+
@staticmethod
|
|
235
|
+
# This function create the appropriate field object for a variable defined in the message.
|
|
236
|
+
# The descriptor and parent message parameters are required parameters, all field need them to be created. The oneof
|
|
237
|
+
# parameter is only required for fields which are part of a oneof. The parameter is the reference to the oneof
|
|
238
|
+
# object.
|
|
239
|
+
# The last parameter is not to be used manually. It is set when we are already in a FieldNested class.
|
|
240
|
+
def factory(proto_descriptor, parent_msg, oneof=None, already_nested=False):
|
|
241
|
+
|
|
242
|
+
# If this field has the same type as the parent we got a recursive inclusion. We can not solve the template
|
|
243
|
+
# parameters in this case. This field is thus replaced by a dummy with a warning. Toposort is used to find more
|
|
244
|
+
# complex recursive inclusions which we can not solve like this.
|
|
245
|
+
parent_msg_type = "." + parent_msg.scope.get_scope_str().replace("::", ".")
|
|
246
|
+
if proto_descriptor.type_name == parent_msg_type:
|
|
247
|
+
result = FieldErrorRecursive(proto_descriptor, parent_msg, oneof)
|
|
248
|
+
# Now continue constructing the normal fields.
|
|
249
|
+
elif (FieldDescriptorProto.LABEL_REPEATED == proto_descriptor.label) and not already_nested:
|
|
250
|
+
# A map is stored and encoded as a repeated message field of entries, so it has to be
|
|
251
|
+
# recognised before the plain repeated field is constructed.
|
|
252
|
+
if Field.find_map_entry_descriptor(proto_descriptor, parent_msg) is not None:
|
|
253
|
+
result = FieldMap(proto_descriptor, parent_msg, oneof)
|
|
254
|
+
else:
|
|
255
|
+
result = FieldRepeated(proto_descriptor, parent_msg, oneof)
|
|
256
|
+
elif FieldDescriptorProto.TYPE_MESSAGE == proto_descriptor.type:
|
|
257
|
+
result = FieldMessage(proto_descriptor, parent_msg, oneof)
|
|
258
|
+
elif FieldDescriptorProto.TYPE_ENUM == proto_descriptor.type:
|
|
259
|
+
result = FieldEnum(proto_descriptor, parent_msg, oneof)
|
|
260
|
+
elif FieldDescriptorProto.TYPE_STRING == proto_descriptor.type:
|
|
261
|
+
result = FieldString(proto_descriptor, parent_msg, oneof)
|
|
262
|
+
elif FieldDescriptorProto.TYPE_BYTES == proto_descriptor.type:
|
|
263
|
+
result = FieldBytes(proto_descriptor, parent_msg, oneof)
|
|
264
|
+
else:
|
|
265
|
+
result = FieldBasic(proto_descriptor, parent_msg, oneof)
|
|
266
|
+
return result
|
|
267
|
+
|
|
268
|
+
def get_wire_type_str(self):
|
|
269
|
+
return ""
|
|
270
|
+
|
|
271
|
+
def get_type(self):
|
|
272
|
+
return ""
|
|
273
|
+
|
|
274
|
+
def get_short_type(self):
|
|
275
|
+
return ""
|
|
276
|
+
|
|
277
|
+
def get_default_value(self):
|
|
278
|
+
return ""
|
|
279
|
+
|
|
280
|
+
# Returns True when the field carries a custom (editions) default value, e.g.
|
|
281
|
+
# int32 x = 1 [default = 42];. Custom defaults are only legal on fields with
|
|
282
|
+
# explicit presence; protoc rejects them on implicit-presence fields.
|
|
283
|
+
def has_default(self):
|
|
284
|
+
return self.descriptor.HasField("default_value")
|
|
285
|
+
|
|
286
|
+
def get_name(self):
|
|
287
|
+
return self.name
|
|
288
|
+
|
|
289
|
+
def get_variable_name(self):
|
|
290
|
+
var_name = ""
|
|
291
|
+
if self.oneof:
|
|
292
|
+
var_name = self.oneof.get_variable_name() + "."
|
|
293
|
+
var_name += self.variable_name
|
|
294
|
+
return var_name
|
|
295
|
+
|
|
296
|
+
def get_variable_id_name(self):
|
|
297
|
+
return self.variable_id_name
|
|
298
|
+
|
|
299
|
+
# Returns a list with a dictionaries for each template parameter this field had. The dictionary holds the parameter
|
|
300
|
+
# name and its type.
|
|
301
|
+
def get_template_parameters(self):
|
|
302
|
+
# For the field that do not have any templates return an empty list.
|
|
303
|
+
return []
|
|
304
|
+
|
|
305
|
+
def match_field_with_definitions(self, all_types_definitions):
|
|
306
|
+
pass
|
|
307
|
+
|
|
308
|
+
def register_template_parameters(self):
|
|
309
|
+
return True
|
|
310
|
+
|
|
311
|
+
# Returns true if in oneof.init the new& function needs to be call to initialize already allocated memory.
|
|
312
|
+
def oneof_allocation_required(self):
|
|
313
|
+
return type(self) is not FieldEnum
|
|
314
|
+
|
|
315
|
+
def get_oneof_name(self):
|
|
316
|
+
return self.oneof.get_name()
|
|
317
|
+
|
|
318
|
+
def get_which_oneof(self):
|
|
319
|
+
return self.oneof.get_which_oneof()
|
|
320
|
+
|
|
321
|
+
# Get the scope relevant compared to the scope this field is used in.
|
|
322
|
+
def get_reduced_scope(self):
|
|
323
|
+
parent_scope = self.parent.scope.get()
|
|
324
|
+
def_scope = self.definition.scope.get()
|
|
325
|
+
start_index = 0
|
|
326
|
+
for ds, ps in zip(def_scope[:-1], parent_scope):
|
|
327
|
+
if ds == ps:
|
|
328
|
+
start_index += 1
|
|
329
|
+
else:
|
|
330
|
+
break
|
|
331
|
+
reduced_scope = def_scope[start_index:]
|
|
332
|
+
return reduced_scope
|
|
333
|
+
|
|
334
|
+
def render(self, filename, jinja_environment):
|
|
335
|
+
template = jinja_environment.get_template(filename)
|
|
336
|
+
rendered_str = template.render(field=self, environment=jinja_environment)
|
|
337
|
+
return rendered_str
|
|
338
|
+
|
|
339
|
+
# Returns True if this field type uses serialize_len() instead of serialize_with_id()
|
|
340
|
+
def uses_serialize_len(self):
|
|
341
|
+
return False
|
|
342
|
+
|
|
343
|
+
# Returns the C++ expression for the size parameter of serialize_len()
|
|
344
|
+
# Only used when uses_serialize_len() returns True
|
|
345
|
+
def get_size_expression(self):
|
|
346
|
+
return ""
|
|
347
|
+
|
|
348
|
+
# Returns True if this field is a nested message type (not string/bytes)
|
|
349
|
+
def is_message_type(self):
|
|
350
|
+
return False
|
|
351
|
+
|
|
352
|
+
# Returns True when this message field uses DELIMITED (group) message encoding
|
|
353
|
+
# (editions message_encoding feature). Only message fields can be delimited.
|
|
354
|
+
def is_delimited(self):
|
|
355
|
+
return False
|
|
356
|
+
|
|
357
|
+
def render_serialize(self, jinja_env):
|
|
358
|
+
return self.render("Field_Serialize.h.jinja2", jinja_environment=jinja_env)
|
|
359
|
+
|
|
360
|
+
def render_serialize_partial(self, jinja_env):
|
|
361
|
+
return self.render("Field_SerializePartial.h.jinja2", jinja_environment=jinja_env)
|
|
362
|
+
|
|
363
|
+
def render_deserialize_partial(self, jinja_env):
|
|
364
|
+
return self.render("Field_DeserializePartial.h.jinja2", jinja_environment=jinja_env)
|
|
365
|
+
|
|
366
|
+
# -----------------------------------------------------------------------------
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
# This class is used to define any type of basic field.
|
|
370
|
+
class FieldBasic(Field):
|
|
371
|
+
# A dictionary to convert the wire type into a default value.
|
|
372
|
+
type_to_default_value = {FieldDescriptorProto.TYPE_DOUBLE: "0.0",
|
|
373
|
+
FieldDescriptorProto.TYPE_FLOAT: "0.0f",
|
|
374
|
+
FieldDescriptorProto.TYPE_INT64: "0",
|
|
375
|
+
FieldDescriptorProto.TYPE_UINT64: "0U",
|
|
376
|
+
FieldDescriptorProto.TYPE_INT32: "0",
|
|
377
|
+
FieldDescriptorProto.TYPE_FIXED64: "0U",
|
|
378
|
+
FieldDescriptorProto.TYPE_FIXED32: "0U",
|
|
379
|
+
FieldDescriptorProto.TYPE_BOOL: "false",
|
|
380
|
+
FieldDescriptorProto.TYPE_UINT32: "0U",
|
|
381
|
+
FieldDescriptorProto.TYPE_SFIXED32: "0",
|
|
382
|
+
FieldDescriptorProto.TYPE_SFIXED64: "0",
|
|
383
|
+
FieldDescriptorProto.TYPE_SINT32: "0",
|
|
384
|
+
FieldDescriptorProto.TYPE_SINT64: "0"}
|
|
385
|
+
|
|
386
|
+
# A dictionary to convert the protobuf wire type into an Embedded Proto C++ type.
|
|
387
|
+
type_to_cpp_type = {FieldDescriptorProto.TYPE_DOUBLE: "EmbeddedProto::doublefixed",
|
|
388
|
+
FieldDescriptorProto.TYPE_FLOAT: "EmbeddedProto::floatfixed",
|
|
389
|
+
FieldDescriptorProto.TYPE_INT64: "EmbeddedProto::int64",
|
|
390
|
+
FieldDescriptorProto.TYPE_UINT64: "EmbeddedProto::uint64",
|
|
391
|
+
FieldDescriptorProto.TYPE_INT32: "EmbeddedProto::int32",
|
|
392
|
+
FieldDescriptorProto.TYPE_FIXED64: "EmbeddedProto::fixed64",
|
|
393
|
+
FieldDescriptorProto.TYPE_FIXED32: "EmbeddedProto::fixed32",
|
|
394
|
+
FieldDescriptorProto.TYPE_BOOL: "EmbeddedProto::boolean",
|
|
395
|
+
FieldDescriptorProto.TYPE_UINT32: "EmbeddedProto::uint32",
|
|
396
|
+
FieldDescriptorProto.TYPE_SFIXED32: "EmbeddedProto::sfixed32",
|
|
397
|
+
FieldDescriptorProto.TYPE_SFIXED64: "EmbeddedProto::sfixed64",
|
|
398
|
+
FieldDescriptorProto.TYPE_SINT32: "EmbeddedProto::sint32",
|
|
399
|
+
FieldDescriptorProto.TYPE_SINT64: "EmbeddedProto::sint64"}
|
|
400
|
+
|
|
401
|
+
# A dictionary to convert the protobuf wire type into a C++ type.
|
|
402
|
+
type_to_cstdint = {FieldDescriptorProto.TYPE_DOUBLE: "double",
|
|
403
|
+
FieldDescriptorProto.TYPE_FLOAT: "float",
|
|
404
|
+
FieldDescriptorProto.TYPE_INT64: "int64_t",
|
|
405
|
+
FieldDescriptorProto.TYPE_UINT64: "uint64_t",
|
|
406
|
+
FieldDescriptorProto.TYPE_INT32: "int32_t",
|
|
407
|
+
FieldDescriptorProto.TYPE_FIXED64: "uint64_t",
|
|
408
|
+
FieldDescriptorProto.TYPE_FIXED32: "uint32_t",
|
|
409
|
+
FieldDescriptorProto.TYPE_BOOL: "bool",
|
|
410
|
+
FieldDescriptorProto.TYPE_UINT32: "uint32_t",
|
|
411
|
+
FieldDescriptorProto.TYPE_SFIXED32: "int32_t",
|
|
412
|
+
FieldDescriptorProto.TYPE_SFIXED64: "int64_t",
|
|
413
|
+
FieldDescriptorProto.TYPE_SINT32: "int32_t",
|
|
414
|
+
FieldDescriptorProto.TYPE_SINT64: "int64_t"}
|
|
415
|
+
|
|
416
|
+
# A dictionary to convert the wire type number into a wire type string.
|
|
417
|
+
type_to_wire_type = {FieldDescriptorProto.TYPE_INT32: "VARINT",
|
|
418
|
+
FieldDescriptorProto.TYPE_INT64: "VARINT",
|
|
419
|
+
FieldDescriptorProto.TYPE_UINT32: "VARINT",
|
|
420
|
+
FieldDescriptorProto.TYPE_UINT64: "VARINT",
|
|
421
|
+
FieldDescriptorProto.TYPE_SINT32: "VARINT",
|
|
422
|
+
FieldDescriptorProto.TYPE_SINT64: "VARINT",
|
|
423
|
+
FieldDescriptorProto.TYPE_BOOL: "VARINT",
|
|
424
|
+
FieldDescriptorProto.TYPE_FIXED64: "FIXED64",
|
|
425
|
+
FieldDescriptorProto.TYPE_SFIXED64: "FIXED64",
|
|
426
|
+
FieldDescriptorProto.TYPE_DOUBLE: "FIXED64",
|
|
427
|
+
FieldDescriptorProto.TYPE_FIXED32: "FIXED32",
|
|
428
|
+
FieldDescriptorProto.TYPE_FLOAT: "FIXED32",
|
|
429
|
+
FieldDescriptorProto.TYPE_SFIXED32: "FIXED32"}
|
|
430
|
+
|
|
431
|
+
def __init__(self, proto_descriptor, parent_msg, oneof=None):
|
|
432
|
+
super().__init__(proto_descriptor, parent_msg, "FieldBasic.h.jinja2", oneof)
|
|
433
|
+
|
|
434
|
+
def get_wire_type_str(self):
|
|
435
|
+
return self.type_to_wire_type[self.descriptor.type]
|
|
436
|
+
|
|
437
|
+
def get_type(self):
|
|
438
|
+
return self.type_to_cpp_type[self.descriptor.type]
|
|
439
|
+
|
|
440
|
+
def get_short_type(self):
|
|
441
|
+
return self.get_type().split("::")[-1]
|
|
442
|
+
|
|
443
|
+
def get_cstdint_type(self):
|
|
444
|
+
return self.type_to_cstdint[self.descriptor.type]
|
|
445
|
+
|
|
446
|
+
# A suffix to make a numeric literal the correct C++ type for the field's
|
|
447
|
+
# storage type. Only used when emitting a custom (editions) default value; the
|
|
448
|
+
# zero-default path keeps its historical formatting unchanged.
|
|
449
|
+
type_to_literal_suffix = {FieldDescriptorProto.TYPE_INT64: "LL",
|
|
450
|
+
FieldDescriptorProto.TYPE_SINT64: "LL",
|
|
451
|
+
FieldDescriptorProto.TYPE_SFIXED64: "LL",
|
|
452
|
+
FieldDescriptorProto.TYPE_UINT64: "ULL",
|
|
453
|
+
FieldDescriptorProto.TYPE_FIXED64: "ULL",
|
|
454
|
+
FieldDescriptorProto.TYPE_UINT32: "U",
|
|
455
|
+
FieldDescriptorProto.TYPE_FIXED32: "U",
|
|
456
|
+
FieldDescriptorProto.TYPE_INT32: "",
|
|
457
|
+
FieldDescriptorProto.TYPE_SINT32: "",
|
|
458
|
+
FieldDescriptorProto.TYPE_SFIXED32: ""}
|
|
459
|
+
|
|
460
|
+
def get_default_value(self):
|
|
461
|
+
# Without a custom default keep the historical zero literal unchanged so
|
|
462
|
+
# proto3 generation is byte-for-byte identical.
|
|
463
|
+
if not self.has_default():
|
|
464
|
+
return self.type_to_default_value[self.descriptor.type]
|
|
465
|
+
return self._format_default_literal(self.descriptor.default_value)
|
|
466
|
+
|
|
467
|
+
def _format_default_literal(self, value):
|
|
468
|
+
field_type = self.descriptor.type
|
|
469
|
+
if FieldDescriptorProto.TYPE_BOOL == field_type:
|
|
470
|
+
return value # protobuf gives "true" / "false"
|
|
471
|
+
if field_type in (FieldDescriptorProto.TYPE_FLOAT, FieldDescriptorProto.TYPE_DOUBLE):
|
|
472
|
+
return self._format_floating_literal(value, field_type)
|
|
473
|
+
# Integer types: append the suffix matching the storage type.
|
|
474
|
+
return value + self.type_to_literal_suffix[field_type]
|
|
475
|
+
|
|
476
|
+
@staticmethod
|
|
477
|
+
def _format_floating_literal(value, field_type):
|
|
478
|
+
is_float = (FieldDescriptorProto.TYPE_FLOAT == field_type)
|
|
479
|
+
cpp_type = "float" if is_float else "double"
|
|
480
|
+
# protobuf encodes non-finite defaults as inf / -inf / nan.
|
|
481
|
+
if "inf" == value:
|
|
482
|
+
return "std::numeric_limits<" + cpp_type + ">::infinity()"
|
|
483
|
+
if "-inf" == value:
|
|
484
|
+
return "-std::numeric_limits<" + cpp_type + ">::infinity()"
|
|
485
|
+
if "nan" == value:
|
|
486
|
+
return "std::numeric_limits<" + cpp_type + ">::quiet_NaN()"
|
|
487
|
+
# Ensure a valid C++ floating point literal (e.g. "2" -> "2.0").
|
|
488
|
+
if not any(ch in value for ch in (".", "e", "E")):
|
|
489
|
+
value += ".0"
|
|
490
|
+
return value + "F" if is_float else value
|
|
491
|
+
|
|
492
|
+
def render_get_set(self, jinja_env):
|
|
493
|
+
return self.render("FieldBasic_GetSet.h.jinja2", jinja_environment=jinja_env)
|
|
494
|
+
|
|
495
|
+
def render_deserialize(self, jinja_env):
|
|
496
|
+
str = self.render("FieldBasic_Deserialize.h.jinja2", jinja_environment=jinja_env)
|
|
497
|
+
return str.rstrip()
|
|
498
|
+
|
|
499
|
+
# -----------------------------------------------------------------------------
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
# A base class for both the String and Bytes type field
|
|
503
|
+
class BaseStringBytes(Field):
|
|
504
|
+
def __init__(self, proto_descriptor, parent_msg, oneof=None):
|
|
505
|
+
super().__init__(proto_descriptor, parent_msg, "FieldString.h.jinja2", oneof)
|
|
506
|
+
|
|
507
|
+
# Custom default values for string and bytes fields are not supported: the
|
|
508
|
+
# fixed-size storage has no literal constructor to initialise from. Warn so
|
|
509
|
+
# the silently-ignored default is not mistaken for working behavior.
|
|
510
|
+
if self.has_default():
|
|
511
|
+
print(parent_msg.name + "." + self.name + ": Warning: custom default values "
|
|
512
|
+
"for string and bytes fields are not supported and will be ignored.",
|
|
513
|
+
file=sys.stderr)
|
|
514
|
+
|
|
515
|
+
# This is the name given to the template parameter for the length.
|
|
516
|
+
self.template_param_str = self.parent.name + "_" + self.variable_name + "LENGTH"
|
|
517
|
+
|
|
518
|
+
# Find options we know and use in this type of field.
|
|
519
|
+
self.MaxLength = None
|
|
520
|
+
|
|
521
|
+
if self.embedded_proto_options is not None:
|
|
522
|
+
options = self.embedded_proto_options
|
|
523
|
+
|
|
524
|
+
# Determine which maxLength to use based on context
|
|
525
|
+
# If we're in a repeated field, use nestedMaxLength, if not present create a C++ template parameter.
|
|
526
|
+
# If we're not in a repeated field, use maxLength, if not present create a C++ template parameter.
|
|
527
|
+
# Check if this field is part of a repeated field by looking at the parent's descriptor
|
|
528
|
+
is_in_repeated = proto_descriptor.label == FieldDescriptorProto.LABEL_REPEATED
|
|
529
|
+
|
|
530
|
+
if is_in_repeated:
|
|
531
|
+
if options.nestedMaxLength:
|
|
532
|
+
self.MaxLength = options.nestedMaxLength
|
|
533
|
+
else:
|
|
534
|
+
self.MaxLength = None
|
|
535
|
+
else:
|
|
536
|
+
# For non-repeated fields, just use maxLength
|
|
537
|
+
self.MaxLength = options.maxLength
|
|
538
|
+
|
|
539
|
+
def get_wire_type_str(self):
|
|
540
|
+
return "LENGTH_DELIMITED"
|
|
541
|
+
|
|
542
|
+
def get_template_parameters(self):
|
|
543
|
+
result = []
|
|
544
|
+
# A callbackStorage field is emitted as a concrete BytesStringCallback, so it exposes no
|
|
545
|
+
# template parameter (neither a storage type nor a length).
|
|
546
|
+
if self.has_callback_storage():
|
|
547
|
+
pass
|
|
548
|
+
# When the user supplies the storage type, expose a single plain type parameter without a default. The user
|
|
549
|
+
# must supply a type derived from ::EmbeddedProto::internal::BaseStringBytes. The maximum length is ignored.
|
|
550
|
+
elif self.has_custom_storage():
|
|
551
|
+
result.append({"name": self.get_storage_type_param_str(), "type": "class"})
|
|
552
|
+
|
|
553
|
+
# When no maximum length is specified, expose the length as a template parameter.
|
|
554
|
+
elif not self.MaxLength:
|
|
555
|
+
result.append({"name": self.template_param_str, "type": "uint32_t"})
|
|
556
|
+
|
|
557
|
+
return result
|
|
558
|
+
|
|
559
|
+
def register_template_parameters(self):
|
|
560
|
+
# A callbackStorage field is a concrete type with no template parameter to register.
|
|
561
|
+
if self.has_callback_storage():
|
|
562
|
+
return True
|
|
563
|
+
# A user supplied storage type contributes a storage type parameter; an unspecified maximum length contributes
|
|
564
|
+
# a length parameter. In either case this field has a template parameter to register with the parent.
|
|
565
|
+
if self.has_custom_storage() or not self.MaxLength:
|
|
566
|
+
self.parent.register_child_with_template(self)
|
|
567
|
+
return True
|
|
568
|
+
|
|
569
|
+
def get_storage_base_type(self):
|
|
570
|
+
return "::EmbeddedProto::internal::BaseStringBytes"
|
|
571
|
+
|
|
572
|
+
def render_deserialize(self, jinja_env):
|
|
573
|
+
str = self.render("FieldBasic_Deserialize.h.jinja2", jinja_environment=jinja_env)
|
|
574
|
+
return str.rstrip()
|
|
575
|
+
|
|
576
|
+
def uses_serialize_len(self):
|
|
577
|
+
return True
|
|
578
|
+
|
|
579
|
+
def get_size_expression(self):
|
|
580
|
+
return self.get_variable_name() + ".get_length()"
|
|
581
|
+
|
|
582
|
+
# -----------------------------------------------------------------------------
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
# This class defines a string field
|
|
586
|
+
class FieldString(BaseStringBytes):
|
|
587
|
+
def __init__(self, proto_descriptor, parent_msg, oneof=None):
|
|
588
|
+
super().__init__(proto_descriptor, parent_msg, oneof)
|
|
589
|
+
|
|
590
|
+
def get_type(self):
|
|
591
|
+
# A callbackStorage field streams through a concrete BytesStringCallback for char elements.
|
|
592
|
+
if self.has_callback_storage():
|
|
593
|
+
return "::EmbeddedProto::BytesStringCallback<char>"
|
|
594
|
+
|
|
595
|
+
# When the user supplies the storage type, use the plain template parameter as the field type.
|
|
596
|
+
if self.has_custom_storage():
|
|
597
|
+
return self.get_storage_type_param_str()
|
|
598
|
+
|
|
599
|
+
str_type = "::EmbeddedProto::FieldString<"
|
|
600
|
+
if self.MaxLength:
|
|
601
|
+
str_type += str(self.MaxLength) + ">"
|
|
602
|
+
else:
|
|
603
|
+
str_type += self.template_param_str + ">"
|
|
604
|
+
return str_type
|
|
605
|
+
|
|
606
|
+
def get_short_type(self):
|
|
607
|
+
return "FieldString"
|
|
608
|
+
|
|
609
|
+
def render_get_set(self, jinja_env):
|
|
610
|
+
return self.render("FieldString_GetSet.h.jinja2", jinja_environment=jinja_env)
|
|
611
|
+
|
|
612
|
+
# -----------------------------------------------------------------------------
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
# This class defines a bytes array field
|
|
616
|
+
class FieldBytes(BaseStringBytes):
|
|
617
|
+
def __init__(self, proto_descriptor, parent_msg, oneof=None):
|
|
618
|
+
super().__init__(proto_descriptor, parent_msg, oneof)
|
|
619
|
+
|
|
620
|
+
def get_type(self):
|
|
621
|
+
# A callbackStorage field streams through a concrete BytesStringCallback for uint8_t elements.
|
|
622
|
+
if self.has_callback_storage():
|
|
623
|
+
return "::EmbeddedProto::BytesStringCallback<uint8_t>"
|
|
624
|
+
|
|
625
|
+
# When the user supplies the storage type, use the plain template parameter as the field type.
|
|
626
|
+
if self.has_custom_storage():
|
|
627
|
+
return self.get_storage_type_param_str()
|
|
628
|
+
|
|
629
|
+
str_type = "::EmbeddedProto::FieldBytes<"
|
|
630
|
+
if self.MaxLength:
|
|
631
|
+
str_type += str(self.MaxLength) + ">"
|
|
632
|
+
else:
|
|
633
|
+
str_type += self.template_param_str + ">"
|
|
634
|
+
return str_type
|
|
635
|
+
|
|
636
|
+
def get_short_type(self):
|
|
637
|
+
return "FieldBytes"
|
|
638
|
+
|
|
639
|
+
def render_get_set(self, jinja_env):
|
|
640
|
+
return self.render("FieldBytes_GetSet.h.jinja2", jinja_environment=jinja_env)
|
|
641
|
+
|
|
642
|
+
# -----------------------------------------------------------------------------
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
# This class is used to wrap around any enum used as a field.
|
|
646
|
+
class FieldEnum(Field):
|
|
647
|
+
def __init__(self, proto_descriptor, parent_msg, oneof=None):
|
|
648
|
+
super().__init__(proto_descriptor, parent_msg, "FieldEnum.h.jinja2", oneof)
|
|
649
|
+
|
|
650
|
+
# Reserve a member variable for the reference to the enum definition used for this field.
|
|
651
|
+
self.definition = None
|
|
652
|
+
|
|
653
|
+
def get_wire_type_str(self):
|
|
654
|
+
return "VARINT"
|
|
655
|
+
|
|
656
|
+
def get_type_as_defined(self):
|
|
657
|
+
if not self.definition:
|
|
658
|
+
# When the actual definition is unknown use the protobuf type.
|
|
659
|
+
type_name = self.descriptor.type_name if "." != self.descriptor.type_name[0] else self.descriptor.type_name[1:]
|
|
660
|
+
type_name = type_name.replace(".", "::")
|
|
661
|
+
else:
|
|
662
|
+
scopes = self.get_reduced_scope()
|
|
663
|
+
type_name = ""
|
|
664
|
+
for scope in scopes:
|
|
665
|
+
if scope["templates"]:
|
|
666
|
+
raise Exception("You are trying to use a field with the type: \"" + self.descriptor.type_name +
|
|
667
|
+
"\". It is defined in different scope as where you are using it. But the scope of "
|
|
668
|
+
"definition includes template parameters for repeated, string or byte fields. It "
|
|
669
|
+
"is there for not possible to define the field where you are using it as we do not "
|
|
670
|
+
"know the template value. Try defining the field in the main scope or the one you "
|
|
671
|
+
"are using it in.")
|
|
672
|
+
|
|
673
|
+
type_name += scope["name"] + "::"
|
|
674
|
+
# Remove the last ::
|
|
675
|
+
type_name = type_name[:-2]
|
|
676
|
+
|
|
677
|
+
return type_name
|
|
678
|
+
|
|
679
|
+
def get_max_enum_value(self):
|
|
680
|
+
# Return the maximum value used by any of the enum items as a string. This is used to calculate the maximum serialized size.
|
|
681
|
+
max_number = self.definition.descriptor.value[0].number
|
|
682
|
+
for value in self.definition.descriptor.value[1:]:
|
|
683
|
+
if max_number < value.number:
|
|
684
|
+
max_number = value.number
|
|
685
|
+
return str(max_number)
|
|
686
|
+
def get_type(self):
|
|
687
|
+
return "EmbeddedProto::enumeration<" + self.get_type_as_defined() + ", EmbeddedProto::WireFormatter::VarintSize(" + self.get_max_enum_value() + ")>"
|
|
688
|
+
|
|
689
|
+
def get_short_type(self):
|
|
690
|
+
return "EmbeddedProto::enumeration<" + self.get_type_as_defined().split("::")[-1] + ", EmbeddedProto::WireFormatter::VarintSize(" + self.get_max_enum_value() + ")>"
|
|
691
|
+
|
|
692
|
+
def get_cstdint_type(self):
|
|
693
|
+
# For enums, use the underlying type (uint32_t for the serialized form)
|
|
694
|
+
return "uint32_t"
|
|
695
|
+
|
|
696
|
+
def get_default_value(self):
|
|
697
|
+
# A custom (editions) default is the qualified enumerator, e.g.
|
|
698
|
+
# Color::BLUE.
|
|
699
|
+
if self.has_default():
|
|
700
|
+
return self.get_type_as_defined() + "::" + self.descriptor.default_value
|
|
701
|
+
# A CLOSED enum defaults to its first declared enumerator, which may be
|
|
702
|
+
# non-zero (and a raw zero is not necessarily a member of the enum). OPEN
|
|
703
|
+
# enums keep the historical zero-initialised default.
|
|
704
|
+
if self.is_closed() and (self.definition is not None):
|
|
705
|
+
first_enumerator = self.definition.descriptor.value[0].name
|
|
706
|
+
return self.get_type_as_defined() + "::" + first_enumerator
|
|
707
|
+
return "static_cast<" + self.get_type_as_defined() + ">(0)"
|
|
708
|
+
|
|
709
|
+
# True when clearing this enum field must assign an explicit default value
|
|
710
|
+
# rather than a plain .clear() (which resets to zero): a custom default or a
|
|
711
|
+
# CLOSED enum whose default is its first (possibly non-zero) enumerator.
|
|
712
|
+
def assign_default_on_clear(self):
|
|
713
|
+
return self.has_default() or self.is_closed()
|
|
714
|
+
|
|
715
|
+
def match_field_with_definitions(self, all_types_definitions):
|
|
716
|
+
found = False
|
|
717
|
+
my_type = self.get_type_as_defined()
|
|
718
|
+
for enum_defs in all_types_definitions["enums"]:
|
|
719
|
+
other_scope = enum_defs.scope.get_scope_str()
|
|
720
|
+
if my_type == other_scope:
|
|
721
|
+
self.definition = enum_defs
|
|
722
|
+
found = True
|
|
723
|
+
break
|
|
724
|
+
|
|
725
|
+
if not found:
|
|
726
|
+
raise Exception("Unable to find the definition of this enum: " + self.name)
|
|
727
|
+
|
|
728
|
+
# Whether the referenced enum is CLOSED (editions enum_type feature). Closedness
|
|
729
|
+
# is a property of the enum definition's scope, not of the field, so it is read
|
|
730
|
+
# from the resolved enum definition.
|
|
731
|
+
def is_closed(self):
|
|
732
|
+
from .Features import EnumType
|
|
733
|
+
if (self.definition is not None) and (self.definition.features is not None):
|
|
734
|
+
return EnumType.CLOSED == self.definition.features["enum_type"]
|
|
735
|
+
return False
|
|
736
|
+
|
|
737
|
+
def render_get_set(self, jinja_env):
|
|
738
|
+
return self.render("FieldEnum_GetSet.h.jinja2", jinja_environment=jinja_env)
|
|
739
|
+
|
|
740
|
+
def render_deserialize(self, jinja_env):
|
|
741
|
+
rendered = self.render("FieldEnum_Deserialize.h.jinja2", jinja_environment=jinja_env)
|
|
742
|
+
return rendered.rstrip()
|
|
743
|
+
|
|
744
|
+
# -----------------------------------------------------------------------------
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
# This class is used to wrap around any type of message used as a field.
|
|
748
|
+
class FieldMessage(Field):
|
|
749
|
+
def __init__(self, proto_descriptor, parent_msg, oneof=None):
|
|
750
|
+
super().__init__(proto_descriptor, parent_msg, "FieldMsg.h.jinja2", oneof)
|
|
751
|
+
|
|
752
|
+
# Reserve a member variable for the reference to the message definition used for this field.
|
|
753
|
+
self.definition = None
|
|
754
|
+
|
|
755
|
+
def get_wire_type_str(self):
|
|
756
|
+
return "LENGTH_DELIMITED"
|
|
757
|
+
|
|
758
|
+
def get_type(self):
|
|
759
|
+
# When the user supplies the storage type, use the plain template parameter as the field type.
|
|
760
|
+
if self.has_custom_storage():
|
|
761
|
+
return self.get_storage_type_param_str()
|
|
762
|
+
|
|
763
|
+
return self.get_message_type()
|
|
764
|
+
|
|
765
|
+
# Returns the actual nested message C++ type, independent of the customStorage option. Used to resolve the message
|
|
766
|
+
# definition and as the documented base type for the customStorage static_assert.
|
|
767
|
+
def get_message_type(self):
|
|
768
|
+
if not self.definition:
|
|
769
|
+
# When the actual definition is unknown use the protobuf type.
|
|
770
|
+
type_name = self.descriptor.type_name if "." != self.descriptor.type_name[0] else self.descriptor.type_name[1:]
|
|
771
|
+
type_name = type_name.replace(".", "::")
|
|
772
|
+
else:
|
|
773
|
+
scopes = self.get_reduced_scope()
|
|
774
|
+
type_name = ""
|
|
775
|
+
for scope in scopes:
|
|
776
|
+
type_name += scope["name"] + "::"
|
|
777
|
+
# Remove the last ::
|
|
778
|
+
type_name = type_name[:-2]
|
|
779
|
+
|
|
780
|
+
tmpl_param = self.get_message_template_parameters()
|
|
781
|
+
if tmpl_param:
|
|
782
|
+
type_name += "<"
|
|
783
|
+
for param in tmpl_param:
|
|
784
|
+
type_name += param["name"] + ", "
|
|
785
|
+
type_name = type_name[:-2] + ">"
|
|
786
|
+
|
|
787
|
+
return type_name
|
|
788
|
+
|
|
789
|
+
def get_short_type(self):
|
|
790
|
+
return self.get_type().split("::")[-1]
|
|
791
|
+
|
|
792
|
+
def get_default_value(self):
|
|
793
|
+
# Just call the default constructor.
|
|
794
|
+
return ""
|
|
795
|
+
|
|
796
|
+
def get_template_parameters(self):
|
|
797
|
+
# When the user supplies the storage type, expose a single plain type parameter without a default. The user
|
|
798
|
+
# must supply a type derived from ::EmbeddedProto::MessageInterface. The nested message template parameters are
|
|
799
|
+
# not exposed because the user owns the complete type.
|
|
800
|
+
if self.has_custom_storage():
|
|
801
|
+
return [{"name": self.get_storage_type_param_str(), "type": "class"}]
|
|
802
|
+
|
|
803
|
+
return self.get_message_template_parameters()
|
|
804
|
+
|
|
805
|
+
def get_message_template_parameters(self):
|
|
806
|
+
# Get the template parameters from the nested message definition. A deep copy is made because we will rename
|
|
807
|
+
# them to be unique within the parent message scope.
|
|
808
|
+
templates = copy.deepcopy(self.definition.get_templates())
|
|
809
|
+
|
|
810
|
+
# Prefix each parameter name with the parent message name and field variable name to avoid collisions when
|
|
811
|
+
# the same message type is used in multiple fields. Track the old-to-new mapping for updating defaults.
|
|
812
|
+
rename_map = {}
|
|
813
|
+
for tmp in templates:
|
|
814
|
+
old_name = tmp["name"]
|
|
815
|
+
new_name = self.parent.name + "_" + self.variable_name + old_name
|
|
816
|
+
rename_map[old_name] = new_name
|
|
817
|
+
tmp["name"] = new_name
|
|
818
|
+
|
|
819
|
+
# Default values may reference other template parameter names (e.g. a repeated storage type parameter whose
|
|
820
|
+
# default is a fixed-size type). These references must be updated to use the renamed parameter names.
|
|
821
|
+
for tmp in templates:
|
|
822
|
+
if "default" in tmp:
|
|
823
|
+
default_value = tmp["default"]
|
|
824
|
+
for old_name, new_name in rename_map.items():
|
|
825
|
+
default_value = default_value.replace(old_name, new_name)
|
|
826
|
+
tmp["default"] = default_value
|
|
827
|
+
|
|
828
|
+
return templates
|
|
829
|
+
|
|
830
|
+
def match_field_with_definitions(self, all_types_definitions):
|
|
831
|
+
found = False
|
|
832
|
+
# Use the real message type (not the customStorage parameter) to resolve the definition.
|
|
833
|
+
my_type = self.get_message_type()
|
|
834
|
+
for msg_defs in all_types_definitions["messages"]:
|
|
835
|
+
other_scope = msg_defs.scope.get_scope_str()
|
|
836
|
+
if my_type == other_scope:
|
|
837
|
+
self.definition = msg_defs
|
|
838
|
+
found = True
|
|
839
|
+
break
|
|
840
|
+
|
|
841
|
+
if not found:
|
|
842
|
+
if self.descriptor.type_name.startswith(".google.protobuf."):
|
|
843
|
+
raise Exception("The field " + self.name + " uses the type " + self.descriptor.type_name + ". No code "
|
|
844
|
+
"is generated for google/protobuf/descriptor.proto, that file may only be imported to "
|
|
845
|
+
"declare custom options. Please do not use its types as a field type.")
|
|
846
|
+
raise Exception("Unable to find the definition of this message: " + self.name)
|
|
847
|
+
|
|
848
|
+
def register_template_parameters(self):
|
|
849
|
+
# When the user supplies the storage type, this field contributes a single plain storage type parameter. The
|
|
850
|
+
# user owns the complete type so the nested message parameters are not propagated.
|
|
851
|
+
if self.has_custom_storage():
|
|
852
|
+
self.parent.register_child_with_template(self)
|
|
853
|
+
return True
|
|
854
|
+
|
|
855
|
+
if self.definition.all_parameters_registered:
|
|
856
|
+
if self.definition.contains_template_parameters:
|
|
857
|
+
self.parent.register_child_with_template(self)
|
|
858
|
+
return True
|
|
859
|
+
else:
|
|
860
|
+
return False
|
|
861
|
+
|
|
862
|
+
# Get the whole scope of the definition of this field.
|
|
863
|
+
def get_scope(self):
|
|
864
|
+
return self.definition.scope.get()
|
|
865
|
+
|
|
866
|
+
def render_get_set(self, jinja_env):
|
|
867
|
+
return self.render("FieldMsg_GetSet.h.jinja2", jinja_environment=jinja_env)
|
|
868
|
+
|
|
869
|
+
def render_deserialize(self, jinja_env):
|
|
870
|
+
rendered = self.render("FieldMsg_Deserialize.h.jinja2", jinja_environment=jinja_env)
|
|
871
|
+
return rendered.rstrip()
|
|
872
|
+
|
|
873
|
+
def uses_serialize_len(self):
|
|
874
|
+
return True
|
|
875
|
+
|
|
876
|
+
def get_size_expression(self):
|
|
877
|
+
return self.get_variable_name() + ".serialized_size()"
|
|
878
|
+
|
|
879
|
+
def is_message_type(self):
|
|
880
|
+
"""Returns True if this field is a nested message type (not string/bytes)."""
|
|
881
|
+
return True
|
|
882
|
+
|
|
883
|
+
def is_delimited(self):
|
|
884
|
+
# Honor the resolved editions message_encoding feature for this field.
|
|
885
|
+
from .Features import MessageEncoding
|
|
886
|
+
if self.resolved_features is not None:
|
|
887
|
+
return MessageEncoding.DELIMITED == self.resolved_features["message_encoding"]
|
|
888
|
+
return False
|
|
889
|
+
|
|
890
|
+
def get_storage_base_type(self):
|
|
891
|
+
return "::EmbeddedProto::MessageInterface"
|
|
892
|
+
|
|
893
|
+
# -----------------------------------------------------------------------------
|
|
894
|
+
|
|
895
|
+
|
|
896
|
+
# This class wraps around any other type of field which is repeated.
|
|
897
|
+
class FieldRepeated(Field):
|
|
898
|
+
def __init__(self, proto_descriptor, parent_msg, oneof=None):
|
|
899
|
+
super().__init__(proto_descriptor, parent_msg, "FieldRepeated.h", oneof)
|
|
900
|
+
|
|
901
|
+
# To make use of the field object actual type create one of their objects.
|
|
902
|
+
self.actual_type = Field.factory(proto_descriptor, parent_msg, oneof, already_nested=True)
|
|
903
|
+
|
|
904
|
+
# This is the name given to the template parameter for the length.
|
|
905
|
+
self.template_param_str = self.parent.name + "_" + self.variable_name + "REP_LENGTH"
|
|
906
|
+
|
|
907
|
+
# Find options we know and use in this type of field.
|
|
908
|
+
self.MaxLength = None
|
|
909
|
+
if self.embedded_proto_options is not None:
|
|
910
|
+
self.MaxLength = self.embedded_proto_options.maxLength
|
|
911
|
+
|
|
912
|
+
def get_wire_type_str(self):
|
|
913
|
+
return "LENGTH_DELIMITED"
|
|
914
|
+
|
|
915
|
+
def get_type(self):
|
|
916
|
+
# When callbackStorage is set, emit a concrete streaming storage type parameterised on the
|
|
917
|
+
# element type. Unlike customStorage this is not a template parameter; the element type is
|
|
918
|
+
# known at generation time and no maximum length applies.
|
|
919
|
+
if self.has_callback_storage():
|
|
920
|
+
# A repeated message streams through MessageCallback (DELIMITED groups); scalar/enum
|
|
921
|
+
# elements stream through RepeatedFieldCallback (EXPANDED tag+value per element).
|
|
922
|
+
if self.callback_is_message():
|
|
923
|
+
return "::EmbeddedProto::MessageCallback<" + self.actual_type.get_type() + ">"
|
|
924
|
+
return "::EmbeddedProto::RepeatedFieldCallback<" + self.actual_type.get_type() + ">"
|
|
925
|
+
|
|
926
|
+
# When the user supplies the storage type, use the plain template parameter as the field type. No size is
|
|
927
|
+
# appended; a user supplied storage type takes precedence over a maximum length.
|
|
928
|
+
if self.has_custom_storage():
|
|
929
|
+
return self.get_storage_type_param_str()
|
|
930
|
+
|
|
931
|
+
# Otherwise use the built-in fixed-size storage. The size is hard coded when a maximum length is set or exposed
|
|
932
|
+
# as a template parameter when it is not.
|
|
933
|
+
type_str = "::EmbeddedProto::RepeatedFieldFixedSize<" + self.actual_type.get_type() + ", "
|
|
934
|
+
if self.MaxLength:
|
|
935
|
+
type_str += str(self.MaxLength) + ">"
|
|
936
|
+
else:
|
|
937
|
+
type_str += self.template_param_str + ">"
|
|
938
|
+
return type_str
|
|
939
|
+
|
|
940
|
+
def get_short_type(self):
|
|
941
|
+
# Delegates to get_type() because the storage type may be a template parameter whose name cannot be shortened.
|
|
942
|
+
return self.get_type()
|
|
943
|
+
|
|
944
|
+
# As this is a repeated field we need a function to get the type we are repeating.
|
|
945
|
+
def get_base_type(self):
|
|
946
|
+
return self.actual_type.get_type()
|
|
947
|
+
|
|
948
|
+
def get_template_parameters(self):
|
|
949
|
+
result = []
|
|
950
|
+
|
|
951
|
+
# A callbackStorage field has a concrete, fully specified storage type; it exposes no template
|
|
952
|
+
# parameters (no length, no element parameters).
|
|
953
|
+
if self.has_callback_storage():
|
|
954
|
+
return result
|
|
955
|
+
|
|
956
|
+
# When the user supplies the storage type, expose a single plain type parameter without a default. The user
|
|
957
|
+
# must supply a type derived from ::EmbeddedProto::RepeatedField. The maximum length is ignored.
|
|
958
|
+
if self.has_custom_storage():
|
|
959
|
+
result.append({"name": self.get_storage_type_param_str(), "type": "class"})
|
|
960
|
+
return result
|
|
961
|
+
|
|
962
|
+
# When no maximum length is set, expose the array length as a C++ template parameter so the user specifies it
|
|
963
|
+
# at compile time.
|
|
964
|
+
if not self.MaxLength:
|
|
965
|
+
result.append({"name": self.template_param_str, "type": "uint32_t"})
|
|
966
|
+
|
|
967
|
+
# Include any template parameters required by the element type (e.g. nested message templates).
|
|
968
|
+
result.extend(self.actual_type.get_template_parameters())
|
|
969
|
+
|
|
970
|
+
return result
|
|
971
|
+
|
|
972
|
+
def match_field_with_definitions(self, all_types_definitions):
|
|
973
|
+
self.actual_type.match_field_with_definitions(all_types_definitions)
|
|
974
|
+
|
|
975
|
+
def register_template_parameters(self):
|
|
976
|
+
result = True
|
|
977
|
+
|
|
978
|
+
# A callbackStorage field has a concrete storage type and contributes no template parameters.
|
|
979
|
+
if self.has_callback_storage():
|
|
980
|
+
return result
|
|
981
|
+
|
|
982
|
+
# When the user supplies the storage type, this field contributes a single plain storage type parameter. The
|
|
983
|
+
# user owns the complete type so the element parameters are not propagated.
|
|
984
|
+
if self.has_custom_storage():
|
|
985
|
+
self.parent.register_child_with_template(self)
|
|
986
|
+
return result
|
|
987
|
+
|
|
988
|
+
# Check for the special case where a string or bytes field is nested in the repeated field. Such an element
|
|
989
|
+
# contributes its own length template parameter when no nested maximum length is defined.
|
|
990
|
+
string_or_bytes_field = (FieldDescriptorProto.TYPE_STRING == self.actual_type.descriptor.type) or \
|
|
991
|
+
(FieldDescriptorProto.TYPE_BYTES == self.actual_type.descriptor.type)
|
|
992
|
+
|
|
993
|
+
# When the array size or a nested string/bytes length is not fixed, register the template parameter(s).
|
|
994
|
+
if not self.MaxLength or (string_or_bytes_field and not self.actual_type.MaxLength):
|
|
995
|
+
self.parent.register_child_with_template(self)
|
|
996
|
+
|
|
997
|
+
# If the array element is a message type it may have its own template parameters (e.g. nested repeated fields
|
|
998
|
+
# or string lengths) that also need to be registered up the chain.
|
|
999
|
+
elif FieldDescriptorProto.TYPE_MESSAGE == self.actual_type.descriptor.type:
|
|
1000
|
+
result = self.actual_type.register_template_parameters()
|
|
1001
|
+
|
|
1002
|
+
return result
|
|
1003
|
+
|
|
1004
|
+
def get_storage_base_type(self):
|
|
1005
|
+
return "::EmbeddedProto::RepeatedField<" + self.actual_type.get_type() + ">"
|
|
1006
|
+
|
|
1007
|
+
def render_get_set(self, jinja_env):
|
|
1008
|
+
return self.render("FieldRepeated_GetSet.h.jinja2", jinja_environment=jinja_env)
|
|
1009
|
+
|
|
1010
|
+
def render_deserialize(self, jinja_env):
|
|
1011
|
+
# A repeated message callback arrives as a START_GROUP framed element; route it through the
|
|
1012
|
+
# message template (which dispatches on the group wire type to deserialize()) rather than the
|
|
1013
|
+
# basic deserialize_check_type path, which is LEN-only for a message and would reject a group.
|
|
1014
|
+
if self.callback_is_message():
|
|
1015
|
+
str = self.render("FieldMsg_Deserialize.h.jinja2", jinja_environment=jinja_env)
|
|
1016
|
+
return str.rstrip()
|
|
1017
|
+
else:
|
|
1018
|
+
str = self.render("FieldBasic_Deserialize.h.jinja2", jinja_environment=jinja_env)
|
|
1019
|
+
return str.rstrip()
|
|
1020
|
+
|
|
1021
|
+
def uses_serialize_len(self):
|
|
1022
|
+
return True
|
|
1023
|
+
|
|
1024
|
+
def get_size_expression(self):
|
|
1025
|
+
# Repeated fields use serialized_size_packed() for packed mode
|
|
1026
|
+
# Unpacked mode is handled separately in the template
|
|
1027
|
+
return self.get_variable_name() + ".serialized_size_packed()"
|
|
1028
|
+
|
|
1029
|
+
# Repeated scalar/enum/message callback storage serializes EXPANDED (one tag or group per element).
|
|
1030
|
+
def callback_is_expanded(self):
|
|
1031
|
+
return self.has_callback_storage()
|
|
1032
|
+
|
|
1033
|
+
# True when this is a repeated message field streamed through MessageCallback.
|
|
1034
|
+
def callback_is_message(self):
|
|
1035
|
+
return self.has_callback_storage() and \
|
|
1036
|
+
(FieldDescriptorProto.TYPE_MESSAGE == self.actual_type.descriptor.type)
|
|
1037
|
+
|
|
1038
|
+
# A repeated message callback streams each element as a DELIMITED group; every other repeated
|
|
1039
|
+
# field keeps the length-prefixed / expanded encoding selected elsewhere.
|
|
1040
|
+
def is_delimited(self):
|
|
1041
|
+
if self.callback_is_message():
|
|
1042
|
+
return self.actual_type.is_delimited()
|
|
1043
|
+
return False
|
|
1044
|
+
|
|
1045
|
+
def is_packed(self):
|
|
1046
|
+
# A callback field streams one element at a time and cannot run the length
|
|
1047
|
+
# prefix / size pass a packed block needs, so it is always serialized EXPANDED
|
|
1048
|
+
# (one tag per element).
|
|
1049
|
+
if self.has_callback_storage():
|
|
1050
|
+
return False
|
|
1051
|
+
|
|
1052
|
+
# Message, string and bytes elements are length-delimited and can never be
|
|
1053
|
+
# packed, regardless of the resolved feature.
|
|
1054
|
+
if self.element_is_length_delimited():
|
|
1055
|
+
return False
|
|
1056
|
+
|
|
1057
|
+
# For packable scalar / enum element types honor the resolved editions
|
|
1058
|
+
# repeated_field_encoding feature. proto3 maps to the proto3 profile whose
|
|
1059
|
+
# default is PACKED, preserving the historical behavior.
|
|
1060
|
+
if self.resolved_features is not None:
|
|
1061
|
+
from .Features import RepeatedFieldEncoding
|
|
1062
|
+
return RepeatedFieldEncoding.PACKED == self.resolved_features["repeated_field_encoding"]
|
|
1063
|
+
|
|
1064
|
+
return True
|
|
1065
|
+
|
|
1066
|
+
# True when the repeated element type is serialized as a length-delimited field
|
|
1067
|
+
# (message, string, bytes). Such elements always use the expanded (one
|
|
1068
|
+
# tag+length per element) form.
|
|
1069
|
+
def element_is_length_delimited(self):
|
|
1070
|
+
return self.actual_type.descriptor.type in (FieldDescriptorProto.TYPE_MESSAGE,
|
|
1071
|
+
FieldDescriptorProto.TYPE_STRING,
|
|
1072
|
+
FieldDescriptorProto.TYPE_BYTES)
|
|
1073
|
+
|
|
1074
|
+
# -----------------------------------------------------------------------------
|
|
1075
|
+
|
|
1076
|
+
|
|
1077
|
+
# This class wraps a protobuf map field.
|
|
1078
|
+
#
|
|
1079
|
+
# Protoc rewrites "map<K,V> foo = 1;" into a repeated message field whose element is a synthetic
|
|
1080
|
+
# entry message holding the key as field one and the value as field two. On the wire the two are
|
|
1081
|
+
# identical, so this class reuses the storage and the whole (de)serialization of a repeated message
|
|
1082
|
+
# field and only adds the map shaped accessors on top of it.
|
|
1083
|
+
class FieldMap(FieldRepeated):
|
|
1084
|
+
|
|
1085
|
+
# Protobuf fixes the field numbers of the entry message.
|
|
1086
|
+
KEY_FIELD_NUMBER = 1
|
|
1087
|
+
VALUE_FIELD_NUMBER = 2
|
|
1088
|
+
|
|
1089
|
+
def __init__(self, proto_descriptor, parent_msg, oneof=None):
|
|
1090
|
+
# Resolved before the base constructor runs because the callback storage validation it
|
|
1091
|
+
# performs already has to know that this field is a map.
|
|
1092
|
+
self.entry_descriptor = Field.find_map_entry_descriptor(proto_descriptor, parent_msg)
|
|
1093
|
+
|
|
1094
|
+
super().__init__(proto_descriptor, parent_msg, oneof)
|
|
1095
|
+
|
|
1096
|
+
# The generated entry class. A message definition creates its nested definitions before its
|
|
1097
|
+
# fields, so the entry is already available while this field is constructed.
|
|
1098
|
+
self.entry_definition = None
|
|
1099
|
+
for nested in parent_msg.nested_msg_definitions:
|
|
1100
|
+
if nested.descriptor is self.entry_descriptor:
|
|
1101
|
+
self.entry_definition = nested
|
|
1102
|
+
|
|
1103
|
+
if self.entry_definition is None:
|
|
1104
|
+
raise Exception(self.get_location() + ": unable to find the generated map entry class.")
|
|
1105
|
+
|
|
1106
|
+
self.apply_key_value_max_length()
|
|
1107
|
+
|
|
1108
|
+
# The message and field name, used to point the user at the offending field in an error message.
|
|
1109
|
+
def get_location(self):
|
|
1110
|
+
return self.parent.name + "." + self.descriptor.name
|
|
1111
|
+
|
|
1112
|
+
# Return the field of the entry class with the given number. The key and the value are looked up
|
|
1113
|
+
# by their protobuf field number rather than by their position in the entry.
|
|
1114
|
+
def get_entry_field(self, number):
|
|
1115
|
+
result = None
|
|
1116
|
+
for field in self.entry_definition.fields:
|
|
1117
|
+
if number == field.variable_id:
|
|
1118
|
+
result = field
|
|
1119
|
+
return result
|
|
1120
|
+
|
|
1121
|
+
def get_key_field(self):
|
|
1122
|
+
return self.get_entry_field(FieldMap.KEY_FIELD_NUMBER)
|
|
1123
|
+
|
|
1124
|
+
def get_value_field(self):
|
|
1125
|
+
return self.get_entry_field(FieldMap.VALUE_FIELD_NUMBER)
|
|
1126
|
+
|
|
1127
|
+
# Push keyMaxLength and valueMaxLength into the string or bytes fields of the entry class.
|
|
1128
|
+
#
|
|
1129
|
+
# Without this the key and the value are singular fields of a message which carries no options of
|
|
1130
|
+
# its own, so each would expose a bare C++ template parameter and the user would have to size the
|
|
1131
|
+
# map through the generated entry type instead of through the map field itself.
|
|
1132
|
+
def apply_key_value_max_length(self):
|
|
1133
|
+
if self.embedded_proto_options is None:
|
|
1134
|
+
return
|
|
1135
|
+
|
|
1136
|
+
options = self.embedded_proto_options
|
|
1137
|
+
if options.keyMaxLength:
|
|
1138
|
+
key_field = self.get_key_field()
|
|
1139
|
+
if not isinstance(key_field, BaseStringBytes):
|
|
1140
|
+
raise Exception(self.get_location() + ": keyMaxLength is only valid on a map with a "
|
|
1141
|
+
"string key.")
|
|
1142
|
+
key_field.MaxLength = options.keyMaxLength
|
|
1143
|
+
|
|
1144
|
+
if options.valueMaxLength:
|
|
1145
|
+
value_field = self.get_value_field()
|
|
1146
|
+
if not isinstance(value_field, BaseStringBytes):
|
|
1147
|
+
raise Exception(self.get_location() + ": valueMaxLength is only valid on a map with "
|
|
1148
|
+
"a string or bytes value.")
|
|
1149
|
+
value_field.MaxLength = options.valueMaxLength
|
|
1150
|
+
|
|
1151
|
+
# A map entry is length delimited on the wire, whether the entries are stored resident or
|
|
1152
|
+
# streamed through callbacks. Streaming does not switch the framing to a group the way it does
|
|
1153
|
+
# for a plain repeated message field, so the bytes stay readable by a standard protoc peer.
|
|
1154
|
+
def is_delimited(self):
|
|
1155
|
+
return False
|
|
1156
|
+
|
|
1157
|
+
# True when this map streams its entries through user callbacks. The generated code then emits
|
|
1158
|
+
# each entry length delimited from a transient, which needs a serialize call of its own.
|
|
1159
|
+
def callback_is_len_expanded(self):
|
|
1160
|
+
return self.has_callback_storage()
|
|
1161
|
+
|
|
1162
|
+
# Entries are length delimited in both storage modes, so deserialization always follows the same
|
|
1163
|
+
# path as any other repeated message field, never the group path a message callback uses.
|
|
1164
|
+
def render_deserialize(self, jinja_env):
|
|
1165
|
+
return self.render("FieldBasic_Deserialize.h.jinja2", jinja_environment=jinja_env).rstrip()
|
|
1166
|
+
|
|
1167
|
+
def render_get_set(self, jinja_env):
|
|
1168
|
+
return self.render("FieldMap_GetSet.h.jinja2", jinja_environment=jinja_env)
|
|
1169
|
+
|
|
1170
|
+
# ---- Types used by the generated map API --------------------------------------------------
|
|
1171
|
+
|
|
1172
|
+
# The C++ type of one entry, the element type of the underlying repeated field.
|
|
1173
|
+
def get_entry_type(self):
|
|
1174
|
+
return self.actual_type.get_type()
|
|
1175
|
+
|
|
1176
|
+
@staticmethod
|
|
1177
|
+
def field_is_string(field):
|
|
1178
|
+
return FieldDescriptorProto.TYPE_STRING == field.descriptor.type
|
|
1179
|
+
|
|
1180
|
+
@staticmethod
|
|
1181
|
+
def field_is_bytes(field):
|
|
1182
|
+
return FieldDescriptorProto.TYPE_BYTES == field.descriptor.type
|
|
1183
|
+
|
|
1184
|
+
@staticmethod
|
|
1185
|
+
def field_is_message(field):
|
|
1186
|
+
return FieldDescriptorProto.TYPE_MESSAGE == field.descriptor.type
|
|
1187
|
+
|
|
1188
|
+
def key_is_string(self):
|
|
1189
|
+
return FieldMap.field_is_string(self.get_key_field())
|
|
1190
|
+
|
|
1191
|
+
def value_is_string(self):
|
|
1192
|
+
return FieldMap.field_is_string(self.get_value_field())
|
|
1193
|
+
|
|
1194
|
+
def value_is_bytes(self):
|
|
1195
|
+
return FieldMap.field_is_bytes(self.get_value_field())
|
|
1196
|
+
|
|
1197
|
+
def value_is_message(self):
|
|
1198
|
+
return FieldMap.field_is_message(self.get_value_field())
|
|
1199
|
+
|
|
1200
|
+
def value_is_enum(self):
|
|
1201
|
+
return self.get_value_field().of_type_enum
|
|
1202
|
+
|
|
1203
|
+
# True when the value can be returned by value from a lookup, with a sensible default when the
|
|
1204
|
+
# key is absent. Messages and bytes have no such literal default and are read through the
|
|
1205
|
+
# Error returning overload instead.
|
|
1206
|
+
def value_is_returned_by_value(self):
|
|
1207
|
+
return not (self.value_is_message() or self.value_is_bytes())
|
|
1208
|
+
|
|
1209
|
+
# The C++ type a key is passed as. A string key is taken as a plain c style string, which is what
|
|
1210
|
+
# a user reaches for, and compares directly against the stored key through FieldString.
|
|
1211
|
+
def get_key_param_type(self):
|
|
1212
|
+
if self.key_is_string():
|
|
1213
|
+
return "const char*"
|
|
1214
|
+
return self.get_key_field().get_cstdint_type()
|
|
1215
|
+
|
|
1216
|
+
# The C++ type a value is passed as when writing an entry.
|
|
1217
|
+
def get_value_param_type(self):
|
|
1218
|
+
value_field = self.get_value_field()
|
|
1219
|
+
if self.value_is_string():
|
|
1220
|
+
return "const char*"
|
|
1221
|
+
if self.value_is_bytes() or self.value_is_message():
|
|
1222
|
+
return "const " + value_field.get_type() + "&"
|
|
1223
|
+
if self.value_is_enum():
|
|
1224
|
+
return value_field.get_type_as_defined()
|
|
1225
|
+
return value_field.get_cstdint_type()
|
|
1226
|
+
|
|
1227
|
+
# The C++ type a lookup returns for the value kinds that have a literal default.
|
|
1228
|
+
def get_value_return_type(self):
|
|
1229
|
+
value_field = self.get_value_field()
|
|
1230
|
+
if self.value_is_string():
|
|
1231
|
+
return "const char*"
|
|
1232
|
+
if self.value_is_enum():
|
|
1233
|
+
return value_field.get_type_as_defined()
|
|
1234
|
+
return value_field.get_cstdint_type()
|
|
1235
|
+
|
|
1236
|
+
# The value handed back when a key is not present in the map. Scalars and enums reuse the default
|
|
1237
|
+
# the generator emits for the field itself, which also covers a CLOSED enum whose first
|
|
1238
|
+
# enumerator is not zero.
|
|
1239
|
+
def get_value_absent_default(self):
|
|
1240
|
+
if self.value_is_string():
|
|
1241
|
+
return '""'
|
|
1242
|
+
return self.get_value_field().get_default_value()
|
|
1243
|
+
|
|
1244
|
+
# The C++ type the Error returning lookup writes the value into.
|
|
1245
|
+
def get_value_out_type(self):
|
|
1246
|
+
value_field = self.get_value_field()
|
|
1247
|
+
if self.value_is_string() or self.value_is_bytes() or self.value_is_message():
|
|
1248
|
+
return value_field.get_type()
|
|
1249
|
+
if self.value_is_enum():
|
|
1250
|
+
return value_field.get_type_as_defined()
|
|
1251
|
+
return value_field.get_cstdint_type()
|
|
1252
|
+
|
|
1253
|
+
# The statement writing the key of a freshly added entry, given the names of the entry variable
|
|
1254
|
+
# and of the key parameter. A string is assigned through its field object, every other key kind
|
|
1255
|
+
# has a plain setter taking the value.
|
|
1256
|
+
def get_key_assign_statement(self, entry, key):
|
|
1257
|
+
if self.key_is_string():
|
|
1258
|
+
return entry + ".mutable_key().set(" + key + ");"
|
|
1259
|
+
return entry + ".set_key(" + key + ");"
|
|
1260
|
+
|
|
1261
|
+
# The statement writing the value of an entry, see get_key_assign_statement.
|
|
1262
|
+
def get_value_assign_statement(self, entry, value):
|
|
1263
|
+
if self.value_is_string():
|
|
1264
|
+
return entry + ".mutable_value().set(" + value + ");"
|
|
1265
|
+
return entry + ".set_value(" + value + ");"
|
|
1266
|
+
|
|
1267
|
+
# The expression reading the value out of an entry for the by value lookup. A string is handed
|
|
1268
|
+
# back as a c style string, matching get_value_return_type.
|
|
1269
|
+
def get_value_read_expression(self, entry):
|
|
1270
|
+
if self.value_is_string():
|
|
1271
|
+
return entry + ".get_value().get_const()"
|
|
1272
|
+
return entry + ".get_value()"
|
|
1273
|
+
|
|
1274
|
+
|
|
1275
|
+
# -----------------------------------------------------------------------------
|
|
1276
|
+
|
|
1277
|
+
|
|
1278
|
+
# This class represents a field we can not include because it causes a recursive inclusion.
|
|
1279
|
+
class FieldErrorRecursive(Field):
|
|
1280
|
+
def __init__(self, proto_descriptor, parent_msg, oneof=None):
|
|
1281
|
+
super().__init__(proto_descriptor, parent_msg, "FieldRepeated.h.jinja2", oneof)
|
|
1282
|
+
|
|
1283
|
+
self.descriptor.type_name = "FieldErrorRecursive"
|
|
1284
|
+
|
|
1285
|
+
def get_type(self):
|
|
1286
|
+
return "//"
|
|
1287
|
+
|
|
1288
|
+
def render_get_set(self, jinja_env):
|
|
1289
|
+
return self.render("FieldErrorRecursive_GetSet.h.jinja2", jinja_environment=jinja_env)
|
|
1290
|
+
|
|
1291
|
+
def render_serialize(self, jinja_env):
|
|
1292
|
+
return ""
|
|
1293
|
+
|
|
1294
|
+
def render_deserialize(self, jinja_env):
|
|
1295
|
+
return ""
|