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.
Files changed (63) hide show
  1. EmbeddedProto/EmbeddedProto.py +122 -0
  2. EmbeddedProto/Features.py +255 -0
  3. EmbeddedProto/Field.py +1295 -0
  4. EmbeddedProto/Oneof.py +74 -0
  5. EmbeddedProto/ProtoFile.py +210 -0
  6. EmbeddedProto/TypeDefinitions.py +367 -0
  7. EmbeddedProto/__init__.py +0 -0
  8. EmbeddedProto/__main__.py +28 -0
  9. EmbeddedProto/config.py +173 -0
  10. EmbeddedProto/custom_header.py +513 -0
  11. EmbeddedProto/embedded_proto_options.proto +62 -0
  12. EmbeddedProto/embedded_proto_options_pb2.py +37 -0
  13. EmbeddedProto/field_options.py +344 -0
  14. EmbeddedProto/main.py +316 -0
  15. EmbeddedProto/src/EmbeddedProto/BytesStringCallback.h +476 -0
  16. EmbeddedProto/src/EmbeddedProto/Defines.h +118 -0
  17. EmbeddedProto/src/EmbeddedProto/EmptyArray.h +81 -0
  18. EmbeddedProto/src/EmbeddedProto/Errors.h +53 -0
  19. EmbeddedProto/src/EmbeddedProto/FieldStringBytes.h +793 -0
  20. EmbeddedProto/src/EmbeddedProto/Fields.h +832 -0
  21. EmbeddedProto/src/EmbeddedProto/Functional.h +412 -0
  22. EmbeddedProto/src/EmbeddedProto/MessageCallback.h +688 -0
  23. EmbeddedProto/src/EmbeddedProto/MessageInterface.h +792 -0
  24. EmbeddedProto/src/EmbeddedProto/MessageSizeCalculator.h +109 -0
  25. EmbeddedProto/src/EmbeddedProto/MessageState.h +207 -0
  26. EmbeddedProto/src/EmbeddedProto/ReadBufferFixedSize.h +204 -0
  27. EmbeddedProto/src/EmbeddedProto/ReadBufferInterface.h +181 -0
  28. EmbeddedProto/src/EmbeddedProto/ReadBufferSection.h +232 -0
  29. EmbeddedProto/src/EmbeddedProto/RepeatedField.h +1053 -0
  30. EmbeddedProto/src/EmbeddedProto/RepeatedFieldCallback.h +432 -0
  31. EmbeddedProto/src/EmbeddedProto/RepeatedFieldFixedSize.h +367 -0
  32. EmbeddedProto/src/EmbeddedProto/Version.h +39 -0
  33. EmbeddedProto/src/EmbeddedProto/WireFormatter.h +852 -0
  34. EmbeddedProto/src/EmbeddedProto/WriteBufferFixedSize.h +114 -0
  35. EmbeddedProto/src/EmbeddedProto/WriteBufferInterface.h +134 -0
  36. EmbeddedProto/src/EmbeddedProto.h +55 -0
  37. EmbeddedProto/templates/FieldBasic_Deserialize.h.jinja2 +26 -0
  38. EmbeddedProto/templates/FieldBasic_GetSet.h.jinja2 +89 -0
  39. EmbeddedProto/templates/FieldBytes_GetSet.h.jinja2 +86 -0
  40. EmbeddedProto/templates/FieldEnum_Deserialize.h.jinja2 +50 -0
  41. EmbeddedProto/templates/FieldEnum_GetSet.h.jinja2 +93 -0
  42. EmbeddedProto/templates/FieldErrorRecursive_GetSet.h.jinja2 +29 -0
  43. EmbeddedProto/templates/FieldMap_GetSet.h.jinja2 +203 -0
  44. EmbeddedProto/templates/FieldMsg_Deserialize.h.jinja2 +44 -0
  45. EmbeddedProto/templates/FieldMsg_GetSet.h.jinja2 +93 -0
  46. EmbeddedProto/templates/FieldRepeated_GetSet.h.jinja2 +94 -0
  47. EmbeddedProto/templates/FieldString_GetSet.h.jinja2 +86 -0
  48. EmbeddedProto/templates/Field_DeserializePartial.h.jinja2 +33 -0
  49. EmbeddedProto/templates/Field_Serialize.h.jinja2 +112 -0
  50. EmbeddedProto/templates/Field_SerializePartial.h.jinja2 +119 -0
  51. EmbeddedProto/templates/Header.h.jinja2 +108 -0
  52. EmbeddedProto/templates/TypeDefEnum.h.jinja2 +45 -0
  53. EmbeddedProto/templates/TypeDefMsg.h.jinja2 +791 -0
  54. EmbeddedProto/templates/TypeOneof.h.jinja2 +178 -0
  55. EmbeddedProto/version.json +3 -0
  56. embeddedproto-4.0.0b1.dist-info/METADATA +141 -0
  57. embeddedproto-4.0.0b1.dist-info/RECORD +63 -0
  58. embeddedproto-4.0.0b1.dist-info/WHEEL +5 -0
  59. embeddedproto-4.0.0b1.dist-info/entry_points.txt +3 -0
  60. embeddedproto-4.0.0b1.dist-info/licenses/LICENSE +30 -0
  61. embeddedproto-4.0.0b1.dist-info/licenses/LICENSES/GPL-3.0-only.txt +674 -0
  62. embeddedproto-4.0.0b1.dist-info/licenses/LICENSES/LicenseRef-EmbeddedProto-Commercial.txt +12 -0
  63. 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 ""