Field.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. #
  2. # Copyright (C) 2020 Embedded AMS B.V. - All Rights Reserved
  3. #
  4. # This file is part of Embedded Proto.
  5. #
  6. # Embedded Proto is open source software: you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License as published
  8. # by the Free Software Foundation, version 3 of the license.
  9. #
  10. # Embedded Proto is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with Embedded Proto. If not, see <https://www.gnu.org/licenses/>.
  17. #
  18. # For commercial and closed source application please visit:
  19. # <https://EmbeddedProto.com/license/>.
  20. #
  21. # Embedded AMS B.V.
  22. # Info:
  23. # info at EmbeddedProto dot com
  24. #
  25. # Postal address:
  26. # Johan Huizingalaan 763a
  27. # 1066 VH, Amsterdam
  28. # the Netherlands
  29. #
  30. from google.protobuf.descriptor_pb2 import FieldDescriptorProto
  31. import copy
  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. # A reference to the parent message in which this field is defined.
  38. self.parent = parent_msg
  39. # If this field is part of a oneof this is the reference to it.
  40. self.oneof = oneof
  41. self.name = self.descriptor.name
  42. self.variable_name = self.name + "_"
  43. self.variable_id_name = self.name.upper()
  44. self.variable_id = self.descriptor.number
  45. self.template_file = template_filename
  46. self.of_type_enum = FieldDescriptorProto.TYPE_ENUM == proto_descriptor.type
  47. @staticmethod
  48. # This function create the appropriate field object for a variable defined in the message.
  49. # The descriptor and parent message parameters are required parameters, all field need them to be created. The oneof
  50. # parameter is only required for fields which are part of a oneof. The parameter is the reference to the oneof
  51. # object.
  52. # The last parameter is not to be used manually. It is set when we are already in a FieldNested class.
  53. def factory(proto_descriptor, parent_msg, oneof=None, already_nested=False):
  54. if (FieldDescriptorProto.LABEL_REPEATED == proto_descriptor.label) and not already_nested:
  55. result = FieldRepeated(proto_descriptor, parent_msg, oneof)
  56. elif FieldDescriptorProto.TYPE_MESSAGE == proto_descriptor.type:
  57. result = FieldMessage(proto_descriptor, parent_msg, oneof)
  58. elif FieldDescriptorProto.TYPE_ENUM == proto_descriptor.type:
  59. result = FieldEnum(proto_descriptor, parent_msg, oneof)
  60. elif FieldDescriptorProto.TYPE_STRING == proto_descriptor.type:
  61. result = FieldString(proto_descriptor, parent_msg, oneof)
  62. elif FieldDescriptorProto.TYPE_BYTES == proto_descriptor.type:
  63. result = FieldBytes(proto_descriptor, parent_msg, oneof)
  64. else:
  65. result = FieldBasic(proto_descriptor, parent_msg, oneof)
  66. return result
  67. def get_wire_type_str(self):
  68. return ""
  69. def get_type(self):
  70. return ""
  71. def get_short_type(self):
  72. return ""
  73. def get_default_value(self):
  74. return ""
  75. def get_name(self):
  76. return self.name
  77. def get_variable_name(self):
  78. var_name = ""
  79. if self.oneof:
  80. var_name = self.oneof.get_variable_name() + "."
  81. var_name += self.variable_name
  82. return var_name
  83. def get_variable_id_name(self):
  84. return self.variable_id_name
  85. # Returns a list with a dictionaries for each template parameter this field had. The dictionary holds the parameter
  86. # name and its type.
  87. def get_template_parameters(self):
  88. # For the field that do not have any templates return an empty list.
  89. return []
  90. def match_field_with_definitions(self, all_types_definitions):
  91. pass
  92. def register_template_parameters(self):
  93. return True
  94. # Returns true if in oneof.init the new& function needs to be call to initialize already allocated memory.
  95. def oneof_allocation_required(self):
  96. return (type(self) is FieldMessage) or (type(self) is FieldRepeated) or (type(self) is FieldString) or \
  97. (type(self) is FieldBytes)
  98. def get_oneof_name(self):
  99. return self.oneof.get_name()
  100. def get_which_oneof(self):
  101. return self.oneof.get_which_oneof()
  102. # Get the scope relevant compared to the scope this field is used in.
  103. def get_reduced_scope(self):
  104. parent_scope = self.parent.scope.get()
  105. def_scope = self.definition.scope.get()
  106. start_index = 0
  107. for ds, ps in zip(def_scope[:-1], parent_scope):
  108. if ds == ps:
  109. start_index += 1
  110. else:
  111. break
  112. reduced_scope = def_scope[start_index:]
  113. return reduced_scope
  114. def render(self, filename, jinja_environment):
  115. template = jinja_environment.get_template(filename)
  116. rendered_str = template.render(field=self, environment=jinja_environment)
  117. return rendered_str
  118. # -----------------------------------------------------------------------------
  119. # This class is used to define any type of basic field.
  120. class FieldBasic(Field):
  121. # A dictionary to convert the wire type into a default value.
  122. type_to_default_value = {FieldDescriptorProto.TYPE_DOUBLE: "0.0",
  123. FieldDescriptorProto.TYPE_FLOAT: "0.0",
  124. FieldDescriptorProto.TYPE_INT64: "0",
  125. FieldDescriptorProto.TYPE_UINT64: "0U",
  126. FieldDescriptorProto.TYPE_INT32: "0",
  127. FieldDescriptorProto.TYPE_FIXED64: "0U",
  128. FieldDescriptorProto.TYPE_FIXED32: "0U",
  129. FieldDescriptorProto.TYPE_BOOL: "false",
  130. FieldDescriptorProto.TYPE_UINT32: "0U",
  131. FieldDescriptorProto.TYPE_SFIXED32: "0",
  132. FieldDescriptorProto.TYPE_SFIXED64: "0",
  133. FieldDescriptorProto.TYPE_SINT32: "0",
  134. FieldDescriptorProto.TYPE_SINT64: "0"}
  135. # A dictionary to convert the protobuf wire type into a C++ type.
  136. type_to_cpp_type = {FieldDescriptorProto.TYPE_DOUBLE: "EmbeddedProto::doublefixed",
  137. FieldDescriptorProto.TYPE_FLOAT: "EmbeddedProto::floatfixed",
  138. FieldDescriptorProto.TYPE_INT64: "EmbeddedProto::int64",
  139. FieldDescriptorProto.TYPE_UINT64: "EmbeddedProto::uint64",
  140. FieldDescriptorProto.TYPE_INT32: "EmbeddedProto::int32",
  141. FieldDescriptorProto.TYPE_FIXED64: "EmbeddedProto::fixed64",
  142. FieldDescriptorProto.TYPE_FIXED32: "EmbeddedProto::fixed32",
  143. FieldDescriptorProto.TYPE_BOOL: "EmbeddedProto::boolean",
  144. FieldDescriptorProto.TYPE_UINT32: "EmbeddedProto::uint32",
  145. FieldDescriptorProto.TYPE_SFIXED32: "EmbeddedProto::sfixed32",
  146. FieldDescriptorProto.TYPE_SFIXED64: "EmbeddedProto::sfixed64",
  147. FieldDescriptorProto.TYPE_SINT32: "EmbeddedProto::sint32",
  148. FieldDescriptorProto.TYPE_SINT64: "EmbeddedProto::sint64"}
  149. # A dictionary to convert the wire type number into a wire type string.
  150. type_to_wire_type = {FieldDescriptorProto.TYPE_INT32: "VARINT",
  151. FieldDescriptorProto.TYPE_INT64: "VARINT",
  152. FieldDescriptorProto.TYPE_UINT32: "VARINT",
  153. FieldDescriptorProto.TYPE_UINT64: "VARINT",
  154. FieldDescriptorProto.TYPE_SINT32: "VARINT",
  155. FieldDescriptorProto.TYPE_SINT64: "VARINT",
  156. FieldDescriptorProto.TYPE_BOOL: "VARINT",
  157. FieldDescriptorProto.TYPE_FIXED64: "FIXED64",
  158. FieldDescriptorProto.TYPE_SFIXED64: "FIXED64",
  159. FieldDescriptorProto.TYPE_DOUBLE: "FIXED64",
  160. FieldDescriptorProto.TYPE_FIXED32: "FIXED32",
  161. FieldDescriptorProto.TYPE_FLOAT: "FIXED32",
  162. FieldDescriptorProto.TYPE_SFIXED32: "FIXED32"}
  163. def __init__(self, proto_descriptor, parent_msg, oneof=None):
  164. super().__init__(proto_descriptor, parent_msg, "FieldBasic.h", oneof)
  165. def get_wire_type_str(self):
  166. return self.type_to_wire_type[self.descriptor.type]
  167. def get_type(self):
  168. return self.type_to_cpp_type[self.descriptor.type]
  169. def get_short_type(self):
  170. return self.get_type().split("::")[-1]
  171. def get_default_value(self):
  172. return self.type_to_default_value[self.descriptor.type]
  173. def render_get_set(self, jinja_env):
  174. return self.render("FieldBasic_GetSet.h", jinja_environment=jinja_env)
  175. def render_serialize(self, jinja_env):
  176. return self.render("FieldBasic_Serialize.h", jinja_environment=jinja_env)
  177. def render_deserialize(self, jinja_env):
  178. return self.render("FieldBasic_Deserialize.h", jinja_environment=jinja_env)
  179. # -----------------------------------------------------------------------------
  180. # This class defines a string field
  181. class FieldString(Field):
  182. def __init__(self, proto_descriptor, parent_msg, oneof=None):
  183. super().__init__(proto_descriptor, parent_msg, "FieldString.h", oneof)
  184. # This is the name given to the template parameter for the length.
  185. self.template_param_str = self.variable_name + "LENGTH"
  186. def get_wire_type_str(self):
  187. return "LENGTH_DELIMITED"
  188. def get_type(self):
  189. return "::EmbeddedProto::FieldString<" + self.template_param_str + ">"
  190. def get_short_type(self):
  191. return "FieldString"
  192. def get_template_parameters(self):
  193. return [{"name": self.template_param_str, "type": "uint32_t"}]
  194. def register_template_parameters(self):
  195. self.parent.scope.register_template_parameters(self)
  196. return True
  197. def render_get_set(self, jinja_env):
  198. return self.render("FieldString_GetSet.h", jinja_environment=jinja_env)
  199. def render_serialize(self, jinja_env):
  200. return self.render("FieldRepeated_Serialize.h", jinja_environment=jinja_env)
  201. def render_deserialize(self, jinja_env):
  202. return self.render("FieldBasic_Deserialize.h", jinja_environment=jinja_env)
  203. # -----------------------------------------------------------------------------
  204. # This class defines a bytes array field
  205. class FieldBytes(Field):
  206. def __init__(self, proto_descriptor, parent_msg, oneof=None):
  207. super().__init__(proto_descriptor, parent_msg, "FieldBytes.h", oneof)
  208. # This is the name given to the template parameter for the length.
  209. self.template_param_str = self.variable_name + "LENGTH"
  210. def get_wire_type_str(self):
  211. return "LENGTH_DELIMITED"
  212. def get_type(self):
  213. return "::EmbeddedProto::FieldBytes<" + self.template_param_str + ">"
  214. def get_short_type(self):
  215. return "FieldBytes"
  216. def get_template_parameters(self):
  217. return [{"name": self.template_param_str, "type": "uint32_t"}]
  218. def register_template_parameters(self):
  219. self.parent.register_child_with_template(self)
  220. return True
  221. def render_get_set(self, jinja_env):
  222. return self.render("FieldBytes_GetSet.h", jinja_environment=jinja_env)
  223. def render_serialize(self, jinja_env):
  224. return self.render("FieldRepeated_Serialize.h", jinja_environment=jinja_env)
  225. def render_deserialize(self, jinja_env):
  226. return self.render("FieldBasic_Deserialize.h", jinja_environment=jinja_env)
  227. # -----------------------------------------------------------------------------
  228. # This class is used to wrap around any enum used as a field.
  229. class FieldEnum(Field):
  230. def __init__(self, proto_descriptor, parent_msg, oneof=None):
  231. super().__init__(proto_descriptor, parent_msg, "FieldEnum.h", oneof)
  232. # Reserve a member variable for the reference to the enum definition used for this field.
  233. self.definition = None
  234. def get_wire_type_str(self):
  235. return "VARINT"
  236. def get_type(self):
  237. if not self.definition:
  238. # When the actual definition is unknown use the protobuf type.
  239. type_name = self.descriptor.type_name if "." != self.descriptor.type_name[0] else self.descriptor.type_name[1:]
  240. type_name = type_name.replace(".", "::")
  241. else:
  242. scopes = self.get_reduced_scope()
  243. type_name = ""
  244. for scope in scopes:
  245. if scope["templates"]:
  246. raise Exception("You are trying to use a field with the type: \"" + self.descriptor.type_name +
  247. "\". It is defined in different scope as where you are using it. But the scope of "
  248. "definition includes template parameters for repeated, string or byte fields. It "
  249. "is there for not possible to define the field where you are using it as we do not "
  250. "know the template value. Try defining the field in the main scope or the one you "
  251. "are using it in.")
  252. type_name += scope["name"] + "::"
  253. # Remove the last ::
  254. type_name = type_name[:-2]
  255. return type_name
  256. def get_short_type(self):
  257. return self.get_type().split("::")[-1]
  258. def get_default_value(self):
  259. return "static_cast<" + self.get_type() + ">(0)"
  260. def match_field_with_definitions(self, all_types_definitions):
  261. found = False
  262. my_type = self.get_type()
  263. for enum_defs in all_types_definitions["enums"]:
  264. other_scope = enum_defs.scope.get_scope_str()
  265. if my_type == other_scope:
  266. self.definition = enum_defs
  267. found = True
  268. break
  269. if not found:
  270. raise Exception("Unable to find the definition of this enum: " + self.name)
  271. def render_get_set(self, jinja_env):
  272. return self.render("FieldEnum_GetSet.h", jinja_environment=jinja_env)
  273. def render_serialize(self, jinja_env):
  274. return self.render("FieldEnum_Serialize.h", jinja_environment=jinja_env)
  275. def render_deserialize(self, jinja_env):
  276. return self.render("FieldEnum_Deserialize.h", jinja_environment=jinja_env)
  277. # -----------------------------------------------------------------------------
  278. # This class is used to wrap around any type of message used as a field.
  279. class FieldMessage(Field):
  280. def __init__(self, proto_descriptor, parent_msg, oneof=None):
  281. super().__init__(proto_descriptor, parent_msg, "FieldMsg.h", oneof)
  282. # Reserve a member variable for the reference to the message definition used for this field.
  283. self.definition = None
  284. def get_wire_type_str(self):
  285. return "LENGTH_DELIMITED"
  286. def get_type(self):
  287. if not self.definition:
  288. # When the actual definition is unknown use the protobuf type.
  289. type_name = self.descriptor.type_name if "." != self.descriptor.type_name[0] else self.descriptor.type_name[1:]
  290. type_name = type_name.replace(".", "::")
  291. else:
  292. scopes = self.get_reduced_scope()
  293. type_name = ""
  294. for scope in scopes:
  295. type_name += scope["name"] + "::"
  296. # Remove the last ::
  297. type_name = type_name[:-2]
  298. tmpl_param = self.get_template_parameters()
  299. if tmpl_param:
  300. type_name += "<"
  301. for param in tmpl_param:
  302. type_name += param["name"] + ", "
  303. type_name = type_name[:-2] + ">"
  304. return type_name
  305. def get_short_type(self):
  306. return self.get_type().split("::")[-1]
  307. def get_default_value(self):
  308. # Just call the default constructor.
  309. return ""
  310. def get_template_parameters(self):
  311. # Get the template names used by the definition.
  312. templates = copy.deepcopy(self.definition.get_templates())
  313. # Next add our variable name to make them unique.
  314. for tmp in templates:
  315. tmp["name"] = self.variable_name + tmp["name"]
  316. return templates
  317. def match_field_with_definitions(self, all_types_definitions):
  318. found = False
  319. my_type = self.get_type()
  320. for msg_defs in all_types_definitions["messages"]:
  321. other_scope = msg_defs.scope.get_scope_str()
  322. if my_type == other_scope:
  323. self.definition = msg_defs
  324. found = True
  325. break
  326. if not found:
  327. raise Exception("Unable to find the definition of this message: " + self.name)
  328. def register_template_parameters(self):
  329. if self.definition.all_parameters_registered:
  330. if self.definition.contains_template_parameters:
  331. self.parent.register_child_with_template(self)
  332. return True
  333. else:
  334. return False
  335. # Get the whole scope of the definition of this field.
  336. def get_scope(self):
  337. return self.definition.scope.get()
  338. def render_get_set(self, jinja_env):
  339. return self.render("FieldMsg_GetSet.h", jinja_environment=jinja_env)
  340. def render_serialize(self, jinja_env):
  341. return self.render("FieldMsg_Serialize.h", jinja_environment=jinja_env)
  342. def render_deserialize(self, jinja_env):
  343. return self.render("FieldMsg_Deserialize.h", jinja_environment=jinja_env)
  344. # -----------------------------------------------------------------------------
  345. # This class wraps around any other type of field which is repeated.
  346. class FieldRepeated(Field):
  347. def __init__(self, proto_descriptor, parent_msg, oneof=None):
  348. super().__init__(proto_descriptor, parent_msg, "FieldRepeated.h", oneof)
  349. # To make use of the field object actual type create one of their objects.
  350. self.actual_type = Field.factory(proto_descriptor, parent_msg, oneof, already_nested=True)
  351. # This is the name given to the template parameter for the length.
  352. self.template_param_str = self.variable_name + "REP_LENGTH"
  353. def get_wire_type_str(self):
  354. return "LENGTH_DELIMITED"
  355. def get_type(self):
  356. return "::EmbeddedProto::RepeatedFieldFixedSize<" + self.actual_type.get_type() + ", " + \
  357. self.template_param_str + ">"
  358. def get_short_type(self):
  359. return "::EmbeddedProto::RepeatedFieldFixedSize<" + self.actual_type.get_short_type() + ", " + \
  360. self.template_param_str + ">"
  361. # As this is a repeated field we need a function to get the type we are repeating.
  362. def get_base_type(self):
  363. return self.actual_type.get_type()
  364. def get_template_parameters(self):
  365. result = [{"name": self.template_param_str, "type": "uint32_t"}]
  366. result.extend(self.actual_type.get_template_parameters())
  367. return result
  368. def match_field_with_definitions(self, all_types_definitions):
  369. self.actual_type.match_field_with_definitions(all_types_definitions)
  370. def register_template_parameters(self):
  371. self.parent.register_child_with_template(self)
  372. return True
  373. def render_get_set(self, jinja_env):
  374. return self.render("FieldRepeated_GetSet.h", jinja_environment=jinja_env)
  375. def render_serialize(self, jinja_env):
  376. return self.render("FieldRepeated_Serialize.h", jinja_environment=jinja_env)
  377. def render_deserialize(self, jinja_env):
  378. return self.render("FieldBasic_Deserialize.h", jinja_environment=jinja_env)