Commit | Line | Data |
---|---|---|
e5aa0be3 PP |
1 | # The MIT License (MIT) |
2 | # | |
4a90140d | 3 | # Copyright (c) 2014-2020 Philippe Proulx <pproulx@efficios.com> |
e5aa0be3 | 4 | # |
1378f213 PP |
5 | # Permission is hereby granted, free of charge, to any person obtaining |
6 | # a copy of this software and associated documentation files (the | |
7 | # "Software"), to deal in the Software without restriction, including | |
8 | # without limitation the rights to use, copy, modify, merge, publish, | |
9 | # distribute, sublicense, and/or sell copies of the Software, and to | |
10 | # permit persons to whom the Software is furnished to do so, subject to | |
11 | # the following conditions: | |
e5aa0be3 | 12 | # |
1378f213 PP |
13 | # The above copyright notice and this permission notice shall be |
14 | # included in all copies or substantial portions of the Software. | |
e5aa0be3 | 15 | # |
1378f213 PP |
16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
17 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
18 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
19 | # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
20 | # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
21 | # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
22 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
e5aa0be3 | 23 | |
8c7c6ed2 | 24 | import barectf.template as barectf_template |
4810b707 | 25 | import barectf.config as barectf_config |
d6483c83 | 26 | import collections |
acfb8213 | 27 | import copy |
1624d186 PP |
28 | from typing import List, Optional, Mapping, Callable, Any, Set, Tuple |
29 | import typing | |
30 | from barectf.typing import Count, Alignment | |
4810b707 PP |
31 | |
32 | ||
d6483c83 PP |
33 | # A tuple containing serialization and size computation function |
34 | # templates for a given operation. | |
35 | _OpTemplates = collections.namedtuple('_OpTemplates', ['serialize', 'size']) | |
70e191bd PP |
36 | |
37 | ||
2394a4b4 | 38 | # Abstract base class of any operation within source code. |
d6483c83 PP |
39 | # |
40 | # Any operation has: | |
41 | # | |
d6483c83 PP |
42 | # * A field type. |
43 | # | |
2394a4b4 PP |
44 | # * A list of names which, when joined with `_`, form the generic |
45 | # C source variable name. | |
d6483c83 | 46 | # |
6bc97055 PP |
47 | # * A level: how deep this operation is within the operation tree. |
48 | # | |
d6483c83 PP |
49 | # * Serialization and size computation templates to generate the |
50 | # operation's source code for those functions. | |
51 | class _Op: | |
6bc97055 PP |
52 | def __init__(self, ft: barectf_config._FieldType, names: List[str], level: Count, |
53 | templates: _OpTemplates): | |
4810b707 | 54 | self._ft = ft |
d6483c83 | 55 | self._names = copy.copy(names) |
6bc97055 | 56 | self._level = level |
d6483c83 | 57 | self._templates = templates |
e5aa0be3 | 58 | |
acfb8213 | 59 | @property |
1624d186 | 60 | def ft(self) -> barectf_config._FieldType: |
4810b707 | 61 | return self._ft |
acfb8213 PP |
62 | |
63 | @property | |
1624d186 | 64 | def names(self) -> List[str]: |
acfb8213 | 65 | return self._names |
e5aa0be3 | 66 | |
6bc97055 PP |
67 | @property |
68 | def level(self) -> Count: | |
69 | return self._level | |
70 | ||
d6483c83 | 71 | @property |
1624d186 | 72 | def top_name(self) -> str: |
d6483c83 | 73 | return self._names[-1] |
e5aa0be3 | 74 | |
1624d186 | 75 | def _render_template(self, templ: barectf_template._Template, **kwargs) -> str: |
d6483c83 PP |
76 | return templ.render(op=self, root_ft_prefixes=_RootFtPrefixes, |
77 | root_ft_prefix_names=_ROOT_FT_PREFIX_NAMES, **kwargs) | |
78 | ||
1624d186 | 79 | def serialize_str(self, **kwargs) -> str: |
d6483c83 PP |
80 | return self._render_template(self._templates.serialize, **kwargs) |
81 | ||
1624d186 | 82 | def size_str(self, **kwargs) -> str: |
d6483c83 PP |
83 | return self._render_template(self._templates.size, **kwargs) |
84 | ||
85 | ||
2394a4b4 PP |
86 | # Compound operation. |
87 | # | |
88 | # A compound operation contains a list of suboperations (leaf or | |
89 | # compound). | |
90 | # | |
91 | # Get the suboperations of a compound operation with its `subops` | |
92 | # property. | |
93 | # | |
94 | # The templates of a compound operation handles its suboperations. | |
95 | class _CompoundOp(_Op): | |
6bc97055 | 96 | def __init__(self, ft: barectf_config._FieldType, names: List[str], level: Count, |
cfee1573 | 97 | templates: _OpTemplates, subops: Optional[List[Any]] = None): |
6bc97055 | 98 | super().__init__(ft, names, level, templates) |
2394a4b4 PP |
99 | self._subops = subops |
100 | ||
101 | @property | |
102 | def subops(self): | |
103 | return self._subops | |
104 | ||
105 | ||
106 | # Leaf operation (abstract class). | |
107 | class _LeafOp(_Op): | |
be9f12dc | 108 | pass |
2394a4b4 PP |
109 | |
110 | ||
d6483c83 | 111 | # An "align" operation. |
2394a4b4 | 112 | class _AlignOp(_LeafOp): |
be9f12dc PP |
113 | def __init__(self, ft: barectf_config._FieldType, names: List[str], level: Count, |
114 | templates: _OpTemplates, value: Alignment): | |
115 | super().__init__(ft, names, level, templates) | |
acfb8213 | 116 | self._value = value |
e5aa0be3 | 117 | |
acfb8213 | 118 | @property |
1624d186 | 119 | def value(self) -> Alignment: |
acfb8213 | 120 | return self._value |
e5aa0be3 | 121 | |
e5aa0be3 | 122 | |
d6483c83 | 123 | # A "write" operation. |
2394a4b4 | 124 | class _WriteOp(_LeafOp): |
be9f12dc PP |
125 | def __init__(self, ft: barectf_config._FieldType, names: List[str], level: Count, |
126 | templates: _OpTemplates, offset_in_byte: Optional[Count]): | |
127 | super().__init__(ft, names, level, templates) | |
128 | assert offset_in_byte is None or (offset_in_byte >= 0 and offset_in_byte < 8) | |
129 | self._offset_in_byte = offset_in_byte | |
130 | ||
131 | @property | |
132 | def offset_in_byte(self) -> Optional[Count]: | |
133 | return self._offset_in_byte | |
70e191bd | 134 | |
e5aa0be3 | 135 | |
1624d186 | 136 | _SpecSerializeWriteTemplates = Mapping[str, barectf_template._Template] |
1624d186 PP |
137 | |
138 | ||
2394a4b4 | 139 | # An operation builder. |
d6483c83 | 140 | # |
728fc4a7 PP |
141 | # Such a builder is closely connected to a `_CodeGen` object using it to |
142 | # find generic templates. | |
d6483c83 | 143 | # |
2394a4b4 PP |
144 | # Call build_for_root_ft() to make an operation builder create a |
145 | # compound operation for a given root structure field type, recursively, | |
146 | # and return it. | |
147 | class _OpBuilder: | |
324b1c40 | 148 | def __init__(self, cg: '_CodeGen'): |
1624d186 | 149 | self._names: List[str] = [] |
6bc97055 | 150 | self._level = Count(0) |
be9f12dc | 151 | self._offset_in_byte: Optional[Count] = None |
d6483c83 | 152 | self._cg = cg |
acfb8213 | 153 | |
be9f12dc PP |
154 | # Whether or not we're within an array operation. |
155 | @property | |
156 | def _in_array(self): | |
157 | return self._level > 0 | |
158 | ||
2394a4b4 PP |
159 | # Creates and returns an operation for the root structure field type |
160 | # `ft` named `name`. | |
d6483c83 PP |
161 | # |
162 | # `spec_serialize_write_templates` is a mapping of first level | |
163 | # member names to specialized serialization "write" templates. | |
2394a4b4 PP |
164 | def build_for_root_ft(self, ft: barectf_config.StructureFieldType, name: str, |
165 | spec_serialize_write_templates: Optional[_SpecSerializeWriteTemplates] = None) -> _CompoundOp: | |
166 | assert ft is not None | |
e5aa0be3 | 167 | |
d6483c83 PP |
168 | if spec_serialize_write_templates is None: |
169 | spec_serialize_write_templates = {} | |
170 | ||
171 | assert type(ft) is barectf_config.StructureFieldType | |
172 | assert len(self._names) == 0 | |
6bc97055 | 173 | assert self._level == 0 |
2394a4b4 PP |
174 | ops = self._build_for_ft(ft, name, spec_serialize_write_templates) |
175 | assert len(ops) == 1 | |
176 | assert type(ops[0]) is _CompoundOp | |
177 | return typing.cast(_CompoundOp, ops[0]) | |
d6483c83 | 178 | |
2394a4b4 | 179 | # Creates and returns the operation(s) for a given field type `ft` |
d6483c83 PP |
180 | # named `name`. |
181 | # | |
2394a4b4 PP |
182 | # See build_for_root_ft() for `spec_serialize_write_templates`. |
183 | def _build_for_ft(self, ft: barectf_config._FieldType, name: str, | |
184 | spec_serialize_write_templates: _SpecSerializeWriteTemplates) -> List[_Op]: | |
1624d186 | 185 | def top_name() -> str: |
d6483c83 PP |
186 | return self._names[-1] |
187 | ||
2394a4b4 PP |
188 | # Creates and returns a "write" operation for the field type |
189 | # `ft`. | |
d6483c83 PP |
190 | # |
191 | # This function considers `spec_serialize_write_templates` to | |
192 | # override generic templates. | |
2394a4b4 | 193 | def create_write_op(ft: barectf_config._FieldType) -> _WriteOp: |
d6483c83 PP |
194 | assert type(ft) is not barectf_config.StructureFieldType |
195 | offset_in_byte = self._offset_in_byte | |
196 | ||
be9f12dc | 197 | if isinstance(ft, barectf_config._BitArrayFieldType) and self._offset_in_byte is not None: |
1624d186 | 198 | self._offset_in_byte = Count((self._offset_in_byte + ft.size) % 8) |
d6483c83 | 199 | |
1624d186 | 200 | serialize_write_templ: Optional[barectf_template._Template] = None |
d6483c83 PP |
201 | |
202 | if len(self._names) == 2: | |
203 | serialize_write_templ = spec_serialize_write_templates.get(top_name()) | |
204 | ||
205 | if serialize_write_templ is None: | |
206 | if isinstance(ft, barectf_config._IntegerFieldType): | |
207 | serialize_write_templ = self._cg._serialize_write_int_statements_templ | |
208 | elif type(ft) is barectf_config.RealFieldType: | |
209 | serialize_write_templ = self._cg._serialize_write_real_statements_templ | |
210 | else: | |
211 | assert type(ft) is barectf_config.StringFieldType | |
212 | serialize_write_templ = self._cg._serialize_write_string_statements_templ | |
213 | ||
214 | size_write_templ = None | |
e5aa0be3 | 215 | |
d6483c83 PP |
216 | if isinstance(ft, barectf_config._BitArrayFieldType): |
217 | size_write_templ = self._cg._size_write_bit_array_statements_templ | |
218 | elif type(ft) is barectf_config.StringFieldType: | |
219 | size_write_templ = self._cg._size_write_string_statements_templ | |
220 | ||
be9f12dc PP |
221 | return _WriteOp(ft, self._names, self._level, |
222 | _OpTemplates(serialize_write_templ, size_write_templ), offset_in_byte) | |
d6483c83 | 223 | |
2394a4b4 | 224 | # Creates and returns an "align" operation for the field type |
d6483c83 PP |
225 | # `ft` if needed. |
226 | # | |
227 | # This function updates the builder's state. | |
be9f12dc | 228 | def try_create_align_op(alignment: Alignment, ft: barectf_config._FieldType) -> Optional[_AlignOp]: |
1624d186 PP |
229 | def align(v: Count, alignment: Alignment) -> Count: |
230 | return Count((v + (alignment - 1)) & -alignment) | |
d6483c83 | 231 | |
be9f12dc PP |
232 | if self._offset_in_byte is None and alignment % 8 == 0: |
233 | self._offset_in_byte = Count(0) | |
234 | else: | |
235 | if self._in_array: | |
236 | self._offset_in_byte = None | |
237 | elif self._offset_in_byte is not None: | |
238 | self._offset_in_byte = Count(align(self._offset_in_byte, alignment) % 8) | |
d6483c83 | 239 | |
be9f12dc PP |
240 | if alignment > 1: |
241 | return _AlignOp(ft, self._names, self._level, | |
2394a4b4 PP |
242 | _OpTemplates(self._cg._serialize_align_statements_templ, |
243 | self._cg._size_align_statements_templ), | |
244 | alignment) | |
245 | ||
246 | return None | |
acfb8213 | 247 | |
6bc97055 PP |
248 | # Returns whether or not `ft` is a compound field type. |
249 | def ft_is_compound(ft: barectf_config._FieldType) -> bool: | |
250 | return isinstance(ft, (barectf_config.StructureFieldType, barectf_config.StaticArrayFieldType)) | |
251 | ||
d6483c83 PP |
252 | # push field type's name to the builder's name stack initially |
253 | self._names.append(name) | |
acfb8213 | 254 | |
2394a4b4 PP |
255 | # operations to return |
256 | ops: List[_Op] = [] | |
257 | ||
be9f12dc | 258 | if type(ft) is barectf_config.StringFieldType or self._names == [_RootFtPrefixes.PH, 'uuid']: |
6bc97055 | 259 | # strings and UUID array are always byte-aligned |
be9f12dc | 260 | op = try_create_align_op(Alignment(8), ft) |
2394a4b4 PP |
261 | |
262 | if op is not None: | |
263 | ops.append(op) | |
264 | ||
265 | ops.append(create_write_op(ft)) | |
4810b707 | 266 | else: |
6bc97055 | 267 | if ft_is_compound(ft): |
be9f12dc | 268 | self._offset_in_byte = None |
70e191bd | 269 | |
be9f12dc | 270 | init_align_op = try_create_align_op(ft.alignment, ft) |
6bc97055 | 271 | subops: List[_Op] = [] |
70e191bd | 272 | |
4810b707 | 273 | if type(ft) is barectf_config.StructureFieldType: |
1624d186 | 274 | ft = typing.cast(barectf_config.StructureFieldType, ft) |
2394a4b4 PP |
275 | |
276 | if init_align_op is not None: | |
be9f12dc PP |
277 | # Append structure field's alignment as a |
278 | # suboperation. | |
279 | # | |
280 | # This is not strictly needed (could be appended to | |
e8f0d548 PP |
281 | # `ops`), but the properties of `_DsOps` and |
282 | # `_ErOps` offer a single (structure field type) | |
be9f12dc | 283 | # operation. |
2394a4b4 PP |
284 | subops.append(init_align_op) |
285 | ||
286 | # append suboperations for each member | |
4810b707 | 287 | for member_name, member in ft.members.items(): |
2394a4b4 PP |
288 | subops += self._build_for_ft(member.field_type, member_name, |
289 | spec_serialize_write_templates) | |
290 | ||
6bc97055 PP |
291 | # create structure field's compound operation |
292 | ops.append(_CompoundOp(ft, self._names, self._level, | |
2394a4b4 PP |
293 | _OpTemplates(self._cg._serialize_write_struct_statements_templ, |
294 | self._cg._size_write_struct_statements_templ), | |
6bc97055 | 295 | subops)) |
be9f12dc PP |
296 | elif isinstance(ft, barectf_config._ArrayFieldType): |
297 | ft = typing.cast(barectf_config._ArrayFieldType, ft) | |
298 | assert ft.alignment == 1 or init_align_op is not None | |
6bc97055 PP |
299 | |
300 | if init_align_op is not None: | |
be9f12dc | 301 | ops.append(init_align_op) |
6bc97055 PP |
302 | |
303 | # append element's suboperations | |
304 | self._level = Count(self._level + 1) | |
305 | subops += self._build_for_ft(ft.element_field_type, | |
306 | f'[{_loop_var_name(Count(self._level - 1))}]', | |
307 | spec_serialize_write_templates) | |
308 | self._level = Count(self._level - 1) | |
309 | ||
be9f12dc PP |
310 | # select the right templates |
311 | if type(ft) is barectf_config.StaticArrayFieldType: | |
312 | templates = _OpTemplates(self._cg._serialize_write_static_array_statements_templ, | |
313 | self._cg._size_write_static_array_statements_templ) | |
314 | else: | |
315 | assert type(ft) is barectf_config.DynamicArrayFieldType | |
316 | templates = _OpTemplates(self._cg._serialize_write_dynamic_array_statements_templ, | |
317 | self._cg._size_write_dynamic_array_statements_templ) | |
318 | ||
319 | # create array field's compound operation | |
320 | ops.append(_CompoundOp(ft, self._names, self._level, templates, subops)) | |
70e191bd | 321 | else: |
2394a4b4 PP |
322 | # leaf field: align + write |
323 | if init_align_op is not None: | |
324 | ops.append(init_align_op) | |
325 | ||
326 | ops.append(create_write_op(ft)) | |
d6483c83 PP |
327 | |
328 | # exiting for this field type: pop its name | |
329 | del self._names[-1] | |
70e191bd | 330 | |
2394a4b4 PP |
331 | return ops |
332 | ||
333 | ||
334 | _OptCompoundOp = Optional[_CompoundOp] | |
335 | ||
70e191bd | 336 | |
e8f0d548 | 337 | # The operations for an event record. |
d6483c83 PP |
338 | # |
339 | # The available operations are: | |
340 | # | |
2394a4b4 PP |
341 | # * Specific context operation. |
342 | # * Payload operation. | |
e8f0d548 | 343 | class _ErOps: |
2394a4b4 PP |
344 | def __init__(self, spec_ctx_op: _OptCompoundOp, payload_op: _OptCompoundOp): |
345 | self._spec_ctx_op = spec_ctx_op | |
346 | self._payload_op = payload_op | |
70e191bd | 347 | |
d6483c83 | 348 | @property |
2394a4b4 PP |
349 | def spec_ctx_op(self) -> _OptCompoundOp: |
350 | return self._spec_ctx_op | |
acfb8213 | 351 | |
d6483c83 | 352 | @property |
2394a4b4 PP |
353 | def payload_op(self) -> _OptCompoundOp: |
354 | return self._payload_op | |
acfb8213 | 355 | |
70e191bd | 356 | |
e8f0d548 | 357 | _ErOpsMap = Mapping[barectf_config.EventRecordType, _ErOps] |
1624d186 PP |
358 | |
359 | ||
e8f0d548 | 360 | # The operations for a data stream. |
d6483c83 PP |
361 | # |
362 | # The available operations are: | |
363 | # | |
2394a4b4 PP |
364 | # * Packet header operation. |
365 | # * Packet context operation. | |
e8f0d548 PP |
366 | # * Event record header operation. |
367 | # * Event record common context operation. | |
368 | # * Event record operations (`_ErOps`). | |
369 | class _DsOps: | |
2394a4b4 | 370 | def __init__(self, pkt_header_op: _OptCompoundOp, pkt_ctx_op: _CompoundOp, |
e8f0d548 | 371 | er_header_op: _OptCompoundOp, er_common_ctx_op: _OptCompoundOp, er_ops: _ErOpsMap): |
2394a4b4 PP |
372 | self._pkt_header_op = pkt_header_op |
373 | self._pkt_ctx_op = pkt_ctx_op | |
e8f0d548 PP |
374 | self._er_header_op = er_header_op |
375 | self._er_common_ctx_op = er_common_ctx_op | |
376 | self._er_ops = er_ops | |
70e191bd | 377 | |
d6483c83 | 378 | @property |
2394a4b4 PP |
379 | def pkt_header_op(self) -> _OptCompoundOp: |
380 | return self._pkt_header_op | |
d6483c83 PP |
381 | |
382 | @property | |
2394a4b4 PP |
383 | def pkt_ctx_op(self) -> _CompoundOp: |
384 | return self._pkt_ctx_op | |
d6483c83 PP |
385 | |
386 | @property | |
e8f0d548 PP |
387 | def er_header_op(self) -> _OptCompoundOp: |
388 | return self._er_header_op | |
d6483c83 PP |
389 | |
390 | @property | |
e8f0d548 PP |
391 | def er_common_ctx_op(self) -> _OptCompoundOp: |
392 | return self._er_common_ctx_op | |
d6483c83 PP |
393 | |
394 | @property | |
e8f0d548 PP |
395 | def er_ops(self) -> _ErOpsMap: |
396 | return self._er_ops | |
d6483c83 PP |
397 | |
398 | ||
399 | # The C variable name prefixes for the six kinds of root field types. | |
1b49c7b8 | 400 | class _RootFtPrefixes: |
1c650e47 PP |
401 | PH = 'ph' |
402 | PC = 'pc' | |
a7e54146 PP |
403 | ERH = 'h' |
404 | ERCC = 'cc' | |
405 | ERSC = 'sc' | |
406 | ERP = 'p' | |
d6483c83 PP |
407 | |
408 | ||
409 | # The human-readable names of the `_RootFtPrefixes` members. | |
410 | _ROOT_FT_PREFIX_NAMES = { | |
1c650e47 PP |
411 | _RootFtPrefixes.PH: 'packet header', |
412 | _RootFtPrefixes.PC: 'packet context', | |
a7e54146 PP |
413 | _RootFtPrefixes.ERH: 'header', |
414 | _RootFtPrefixes.ERCC: 'common context', | |
415 | _RootFtPrefixes.ERSC: 'specific context', | |
416 | _RootFtPrefixes.ERP: 'payload', | |
acfb8213 | 417 | } |
e5aa0be3 PP |
418 | |
419 | ||
d6483c83 PP |
420 | # A named function parameter for a given field type. |
421 | _FtParam = collections.namedtuple('_FtParam', ['ft', 'name']) | |
422 | ||
423 | ||
2d18e033 PP |
424 | # C type abstract base class. |
425 | class _CType: | |
426 | def __init__(self, is_const: bool): | |
427 | self._is_const = is_const | |
428 | ||
429 | @property | |
430 | def is_const(self) -> bool: | |
431 | return self._is_const | |
432 | ||
433 | ||
434 | # Arithmetic C type. | |
435 | class _ArithCType(_CType): | |
436 | def __init__(self, name: str, is_const: bool): | |
437 | super().__init__(is_const) | |
438 | self._name = name | |
439 | ||
440 | @property | |
441 | def name(self) -> str: | |
442 | return self._name | |
443 | ||
444 | def __str__(self) -> str: | |
445 | return f'{"const " if self._is_const else ""}{self._name}' | |
446 | ||
447 | ||
448 | # Pointer C type. | |
449 | class _PointerCType(_CType): | |
450 | def __init__(self, pointed_c_type: _CType, is_const: bool): | |
451 | super().__init__(is_const) | |
452 | self._pointed_c_type = pointed_c_type | |
453 | ||
454 | @property | |
455 | def pointed_c_type(self) -> _CType: | |
456 | return self._pointed_c_type | |
457 | ||
458 | def __str__(self) -> str: | |
459 | s = str(self._pointed_c_type) | |
460 | ||
461 | if not s.endswith('*'): | |
462 | s += ' ' | |
463 | ||
464 | s += '*' | |
465 | ||
466 | if self._is_const: | |
467 | s += ' const' | |
468 | ||
469 | return s | |
470 | ||
471 | ||
6bc97055 PP |
472 | # Returns the name of a loop variable given a nesting level `level`. |
473 | def _loop_var_name(level: Count) -> str: | |
474 | if level < 3: | |
475 | return 'ijk'[level] | |
476 | ||
477 | return f'k{level - 2}' | |
478 | ||
479 | ||
d6483c83 PP |
480 | # A C code generator. |
481 | # | |
482 | # Such a code generator can generate: | |
483 | # | |
728fc4a7 PP |
484 | # * The bitfield header (gen_bitfield_header()). |
485 | # * The public header (gen_header()). | |
486 | # * The source code (gen_src()). | |
487 | class _CodeGen: | |
1624d186 | 488 | def __init__(self, cfg: barectf_config.Configuration): |
e5aa0be3 | 489 | self._cfg = cfg |
d6483c83 | 490 | self._iden_prefix = cfg.options.code_generation_options.identifier_prefix |
1624d186 | 491 | self._templ_filters: Mapping[str, Callable[..., Any]] = { |
d6483c83 PP |
492 | 'ft_c_type': self._ft_c_type, |
493 | 'open_func_params_str': self._open_func_params_str, | |
494 | 'trace_func_params_str': self._trace_func_params_str, | |
e8f0d548 | 495 | 'serialize_er_common_ctx_func_params_str': self._serialize_er_common_ctx_func_params_str, |
6bc97055 PP |
496 | 'loop_var_name': _loop_var_name, |
497 | 'op_src_var_name': self._op_src_var_name, | |
1b49c7b8 | 498 | } |
d6483c83 PP |
499 | self._func_proto_params_templ = self._create_template('func-proto-params.j2') |
500 | self._serialize_align_statements_templ = self._create_template('serialize-align-statements.j2') | |
501 | self._serialize_write_int_statements_templ = self._create_template('serialize-write-int-statements.j2') | |
502 | self._serialize_write_real_statements_templ = self._create_template('serialize-write-real-statements.j2') | |
503 | self._serialize_write_string_statements_templ = self._create_template('serialize-write-string-statements.j2') | |
2394a4b4 | 504 | self._serialize_write_struct_statements_templ = self._create_template('serialize-write-struct-statements.j2') |
6bc97055 | 505 | self._serialize_write_static_array_statements_templ = self._create_template('serialize-write-static-array-statements.j2') |
be9f12dc | 506 | self._serialize_write_dynamic_array_statements_templ = self._create_template('serialize-write-dynamic-array-statements.j2') |
d6483c83 PP |
507 | self._serialize_write_magic_statements_templ = self._create_template('serialize-write-magic-statements.j2') |
508 | self._serialize_write_uuid_statements_templ = self._create_template('serialize-write-uuid-statements.j2') | |
e8f0d548 | 509 | self._serialize_write_dst_id_statements_templ = self._create_template('serialize-write-dst-id-statements.j2') |
462e49b3 | 510 | self._serialize_write_timestamp_statements_templ = self._create_template('serialize-write-timestamp-statements.j2') |
d6483c83 | 511 | self._serialize_write_packet_size_statements_templ = self._create_template('serialize-write-packet-size-statements.j2') |
af09c4fc | 512 | self._serialize_write_seq_num_statements_templ = self._create_template('serialize-write-seq-num-statements.j2') |
d6483c83 | 513 | self._serialize_write_skip_save_statements_templ = self._create_template('serialize-write-skip-save-statements.j2') |
e8f0d548 | 514 | self._serialize_write_ert_id_statements_templ = self._create_template('serialize-write-ert-id-statements.j2') |
d6483c83 PP |
515 | self._size_align_statements_templ = self._create_template('size-align-statements.j2') |
516 | self._size_write_bit_array_statements_templ = self._create_template('size-write-bit-array-statements.j2') | |
517 | self._size_write_string_statements_templ = self._create_template('size-write-string-statements.j2') | |
2394a4b4 | 518 | self._size_write_struct_statements_templ = self._create_template('size-write-struct-statements.j2') |
6bc97055 | 519 | self._size_write_static_array_statements_templ = self._create_template('size-write-static-array-statements.j2') |
be9f12dc | 520 | self._size_write_dynamic_array_statements_templ = self._create_template('size-write-dynamic-array-statements.j2') |
d6483c83 PP |
521 | |
522 | # Creates and returns a template named `name` which is a file | |
523 | # template if `is_file_template` is `True`. | |
524 | # | |
525 | # `name` is the file name, including the `.j2` extension, within the | |
526 | # `c` directory. | |
527 | # | |
528 | # Such a template has the filters custom filters | |
529 | # `self._templ_filters`. | |
1624d186 PP |
530 | def _create_template_base(self, name: str, |
531 | is_file_template: bool) -> barectf_template._Template: | |
d6483c83 PP |
532 | return barectf_template._Template(f'c/{name}', is_file_template, self._cfg, |
533 | self._templ_filters) | |
534 | ||
535 | # Creates and returns a non-file template named `name`. | |
536 | # | |
537 | # See _create_template_base() for `name`. | |
8c7c6ed2 | 538 | def _create_template(self, name: str) -> barectf_template._Template: |
d6483c83 | 539 | return self._create_template_base(name, False) |
8c7c6ed2 | 540 | |
d6483c83 PP |
541 | # Creates and returns a file template named `name`. |
542 | # | |
543 | # See _create_template_base() for `name`. | |
8c7c6ed2 | 544 | def _create_file_template(self, name: str) -> barectf_template._Template: |
d6483c83 | 545 | return self._create_template_base(name, True) |
8c7c6ed2 | 546 | |
d6483c83 | 547 | # Trace type of this code generator's barectf configuration. |
4810b707 | 548 | @property |
a209cf4d | 549 | def _trace_type(self) -> barectf_config._TraceType: |
4810b707 | 550 | return self._cfg.trace.type |
27bc6f1e | 551 | |
6bc97055 PP |
552 | # Returns the name of a source variable for the operation `op`. |
553 | def _op_src_var_name(self, op: _LeafOp) -> str: | |
554 | s = '' | |
555 | ||
556 | for index, name in enumerate(op.names): | |
557 | if index > 0 and not name.startswith('['): | |
558 | s += '_' | |
559 | ||
560 | s += name | |
561 | ||
562 | return s | |
563 | ||
2d18e033 PP |
564 | # Returns the C type for the field type `ft`, making it `const` if |
565 | # `is_const` is `True`. | |
566 | def _ft_c_type(self, ft: barectf_config._FieldType, is_const: bool = False): | |
4810b707 | 567 | if isinstance(ft, barectf_config._IntegerFieldType): |
1624d186 | 568 | ft = typing.cast(barectf_config._IntegerFieldType, ft) |
4810b707 PP |
569 | sign_prefix = 'u' if isinstance(ft, barectf_config.UnsignedIntegerFieldType) else '' |
570 | ||
571 | if ft.size <= 8: | |
572 | sz = 8 | |
573 | elif ft.size <= 16: | |
574 | sz = 16 | |
575 | elif ft.size <= 32: | |
576 | sz = 32 | |
577 | else: | |
d6483c83 | 578 | assert ft.size <= 64 |
4810b707 PP |
579 | sz = 64 |
580 | ||
2d18e033 | 581 | return _ArithCType(f'{sign_prefix}int{sz}_t', is_const) |
4810b707 | 582 | elif type(ft) is barectf_config.RealFieldType: |
1624d186 PP |
583 | ft = typing.cast(barectf_config.RealFieldType, ft) |
584 | ||
4810b707 | 585 | if ft.size == 32 and ft.alignment == 32: |
2d18e033 | 586 | s = 'float' |
4810b707 | 587 | elif ft.size == 64 and ft.alignment == 64: |
2d18e033 | 588 | s = 'double' |
4810b707 | 589 | else: |
2d18e033 | 590 | s = 'uint64_t' |
e18cf9d6 | 591 | |
2d18e033 | 592 | return _ArithCType(s, is_const) |
6bc97055 | 593 | elif type(ft) is barectf_config.StringFieldType: |
2d18e033 | 594 | return _PointerCType(_ArithCType('char', True), is_const) |
6bc97055 | 595 | else: |
be9f12dc PP |
596 | assert isinstance(ft, barectf_config._ArrayFieldType) |
597 | ft = typing.cast(barectf_config._ArrayFieldType, ft) | |
6bc97055 | 598 | return _PointerCType(self._ft_c_type(ft.element_field_type, True), is_const) |
e5aa0be3 | 599 | |
d6483c83 PP |
600 | # Returns the function prototype parameters for the members of the |
601 | # root structure field type `root_ft`. | |
602 | # | |
603 | # Each parameter has the prefix `name_prefix` followed with `_`. | |
604 | # | |
605 | # Members of which the name is in `exclude_set` are excluded. | |
1624d186 PP |
606 | def _proto_params_str(self, root_ft: Optional[barectf_config.StructureFieldType], |
607 | name_prefix: str, const_params: bool, | |
608 | exclude_set: Optional[Set[str]] = None, only_dyn: bool = False) -> str: | |
d6483c83 | 609 | if root_ft is None: |
1624d186 | 610 | return '' |
e5aa0be3 | 611 | |
4810b707 PP |
612 | if exclude_set is None: |
613 | exclude_set = set() | |
614 | ||
d6483c83 | 615 | params = [] |
e5aa0be3 | 616 | |
d6483c83 | 617 | for member_name, member in root_ft.members.items(): |
4810b707 | 618 | if member_name in exclude_set: |
e5aa0be3 PP |
619 | continue |
620 | ||
be9f12dc PP |
621 | is_dyn = member.field_type.size_is_dynamic |
622 | ||
623 | if isinstance(member.field_type, barectf_config.UnsignedIntegerFieldType): | |
624 | ft = typing.cast(barectf_config.UnsignedIntegerFieldType, member.field_type) | |
625 | is_dyn = is_dyn or ft._is_len | |
626 | ||
627 | if only_dyn and not is_dyn: | |
b622b24f PP |
628 | continue |
629 | ||
d6483c83 | 630 | params.append(_FtParam(member.field_type, member_name)) |
e5aa0be3 | 631 | |
e18cf9d6 PP |
632 | return self._func_proto_params_templ.render(params=params, prefix=name_prefix, |
633 | const_params=const_params) | |
4810b707 | 634 | |
d6483c83 | 635 | # Returns the packet opening function prototype parameters for the |
e8f0d548 PP |
636 | # data stream type `dst`. |
637 | def _open_func_params_str(self, dst: barectf_config.DataStreamType, const_params: bool) -> str: | |
d6483c83 | 638 | parts = [] |
1c650e47 | 639 | parts.append(self._proto_params_str(self._trace_type._pkt_header_ft, _RootFtPrefixes.PH, |
e18cf9d6 | 640 | const_params, {'magic', 'stream_id', 'uuid'})) |
e5aa0be3 | 641 | |
4810b707 PP |
642 | exclude_set = { |
643 | 'timestamp_begin', | |
644 | 'timestamp_end', | |
645 | 'packet_size', | |
646 | 'content_size', | |
647 | 'events_discarded', | |
af09c4fc | 648 | 'packet_seq_num', |
4810b707 | 649 | } |
e8f0d548 PP |
650 | parts.append(self._proto_params_str(dst._pkt_ctx_ft, _RootFtPrefixes.PC, const_params, |
651 | exclude_set)) | |
d6483c83 | 652 | return ''.join(parts) |
e5aa0be3 | 653 | |
e8f0d548 PP |
654 | # Returns the tracing function prototype parameters for the data |
655 | # stream and event record types `ds_er_types`. | |
656 | def _trace_func_params_str(self, ds_er_types: Tuple[barectf_config.DataStreamType, | |
657 | barectf_config.EventRecordType], | |
1624d186 | 658 | const_params: bool, only_dyn: bool = False): |
e8f0d548 PP |
659 | dst = ds_er_types[0] |
660 | ert = ds_er_types[1] | |
d6483c83 | 661 | parts = [] |
e5aa0be3 | 662 | |
e8f0d548 PP |
663 | if dst._er_header_ft is not None: |
664 | parts.append(self._proto_params_str(dst._er_header_ft, _RootFtPrefixes.ERH, | |
b622b24f PP |
665 | const_params, {'id', 'timestamp'}, |
666 | only_dyn=only_dyn)) | |
4810b707 | 667 | |
e8f0d548 PP |
668 | if dst.event_record_common_context_field_type is not None: |
669 | parts.append(self._proto_params_str(dst.event_record_common_context_field_type, | |
670 | _RootFtPrefixes.ERCC, const_params, | |
b622b24f | 671 | only_dyn=only_dyn)) |
4810b707 | 672 | |
e8f0d548 PP |
673 | if ert.specific_context_field_type is not None: |
674 | parts.append(self._proto_params_str(ert.specific_context_field_type, | |
a7e54146 | 675 | _RootFtPrefixes.ERSC, const_params, |
b622b24f | 676 | only_dyn=only_dyn)) |
4810b707 | 677 | |
e8f0d548 | 678 | if ert.payload_field_type is not None: |
a7e54146 | 679 | parts.append(self._proto_params_str(ert.payload_field_type, _RootFtPrefixes.ERP, |
b622b24f | 680 | const_params, only_dyn=only_dyn)) |
e5aa0be3 | 681 | |
d6483c83 | 682 | return ''.join(parts) |
4810b707 | 683 | |
e8f0d548 PP |
684 | # Returns the event record common context serialization function |
685 | # prototype parameters for the data stream type `dst`. | |
686 | def _serialize_er_common_ctx_func_params_str(self, dst: barectf_config.DataStreamType, | |
1624d186 | 687 | const_params: bool) -> str: |
e8f0d548 PP |
688 | return self._proto_params_str(dst.event_record_common_context_field_type, |
689 | _RootFtPrefixes.ERCC, const_params) | |
e5aa0be3 | 690 | |
d6483c83 | 691 | # Generates the bitfield header file contents. |
1624d186 | 692 | def gen_bitfield_header(self) -> str: |
d6483c83 | 693 | return self._create_file_template('bitfield.h.j2').render() |
e5aa0be3 | 694 | |
d6483c83 | 695 | # Generates the public header file contents. |
1624d186 | 696 | def gen_header(self) -> str: |
d6483c83 | 697 | return self._create_file_template('barectf.h.j2').render(root_ft_prefixes=_RootFtPrefixes) |
3cb793a1 | 698 | |
d6483c83 | 699 | # Generates the source code file contents. |
1624d186 | 700 | def gen_src(self, header_file_name: str, bitfield_header_file_name: str) -> str: |
e8f0d548 PP |
701 | # Creates and returns the operations for all the data stream and |
702 | # for all their event records. | |
703 | def create_ds_ops() -> Mapping[barectf_config.DataStreamType, _DsOps]: | |
704 | ds_ops = {} | |
d6483c83 | 705 | |
e8f0d548 PP |
706 | for dst in self._trace_type.data_stream_types: |
707 | pkt_header_op = None | |
2394a4b4 | 708 | builder = _OpBuilder(self) |
d6483c83 PP |
709 | pkt_header_ft = self._trace_type._pkt_header_ft |
710 | ||
2394a4b4 | 711 | # packet header operations |
d6483c83 PP |
712 | if pkt_header_ft is not None: |
713 | spec_serialize_write_templates = { | |
714 | 'magic': self._serialize_write_magic_statements_templ, | |
715 | 'uuid': self._serialize_write_uuid_statements_templ, | |
e8f0d548 | 716 | 'stream_id': self._serialize_write_dst_id_statements_templ, |
d6483c83 | 717 | } |
e8f0d548 | 718 | pkt_header_op = builder.build_for_root_ft(pkt_header_ft, |
2394a4b4 PP |
719 | _RootFtPrefixes.PH, |
720 | spec_serialize_write_templates) | |
d6483c83 | 721 | |
e8f0d548 | 722 | # packet context operation |
d6483c83 | 723 | spec_serialize_write_templates = { |
462e49b3 | 724 | 'timestamp_begin': self._serialize_write_timestamp_statements_templ, |
d6483c83 PP |
725 | 'packet_size': self._serialize_write_packet_size_statements_templ, |
726 | 'timestamp_end': self._serialize_write_skip_save_statements_templ, | |
727 | 'events_discarded': self._serialize_write_skip_save_statements_templ, | |
728 | 'content_size': self._serialize_write_skip_save_statements_templ, | |
af09c4fc | 729 | 'packet_seq_num': self._serialize_write_seq_num_statements_templ, |
d6483c83 | 730 | } |
e8f0d548 PP |
731 | pkt_ctx_op = builder.build_for_root_ft(dst._pkt_ctx_ft, _RootFtPrefixes.PC, |
732 | spec_serialize_write_templates) | |
d6483c83 | 733 | |
e8f0d548 | 734 | # event record header operation |
2394a4b4 | 735 | builder = _OpBuilder(self) |
e8f0d548 | 736 | er_header_op = None |
d6483c83 | 737 | |
e8f0d548 | 738 | if dst._er_header_ft is not None: |
d6483c83 | 739 | spec_serialize_write_templates = { |
462e49b3 | 740 | 'timestamp': self._serialize_write_timestamp_statements_templ, |
e8f0d548 | 741 | 'id': self._serialize_write_ert_id_statements_templ, |
d6483c83 | 742 | } |
e8f0d548 PP |
743 | er_header_op = builder.build_for_root_ft(dst._er_header_ft, _RootFtPrefixes.ERH, |
744 | spec_serialize_write_templates) | |
d6483c83 | 745 | |
e8f0d548 PP |
746 | # event record common context operation |
747 | er_common_ctx_op = None | |
d6483c83 | 748 | |
e8f0d548 PP |
749 | if dst.event_record_common_context_field_type is not None: |
750 | er_common_ctx_op = builder.build_for_root_ft(dst.event_record_common_context_field_type, | |
751 | _RootFtPrefixes.ERCC) | |
d6483c83 | 752 | |
e8f0d548 PP |
753 | # operations specific to each event record type |
754 | er_ops = {} | |
d6483c83 | 755 | |
e8f0d548 | 756 | for ert in dst.event_record_types: |
d6483c83 PP |
757 | ev_builder = copy.copy(builder) |
758 | ||
e8f0d548 PP |
759 | # specific context operation |
760 | spec_ctx_op = None | |
d6483c83 | 761 | |
e8f0d548 PP |
762 | if ert.specific_context_field_type is not None: |
763 | spec_ctx_op = ev_builder.build_for_root_ft(ert.specific_context_field_type, | |
a7e54146 | 764 | _RootFtPrefixes.ERSC) |
d6483c83 | 765 | |
e8f0d548 PP |
766 | # payload operation |
767 | payload_op = None | |
d6483c83 | 768 | |
e8f0d548 PP |
769 | if ert.payload_field_type is not None: |
770 | payload_op = ev_builder.build_for_root_ft(ert.payload_field_type, | |
a7e54146 | 771 | _RootFtPrefixes.ERP) |
d6483c83 | 772 | |
e8f0d548 | 773 | er_ops[ert] = _ErOps(spec_ctx_op, payload_op) |
d6483c83 | 774 | |
e8f0d548 PP |
775 | ds_ops[dst] = _DsOps(pkt_header_op, pkt_ctx_op, er_header_op, er_common_ctx_op, |
776 | er_ops) | |
d6483c83 | 777 | |
e8f0d548 | 778 | return ds_ops |
d6483c83 PP |
779 | |
780 | # Returns the "write" operation for the packet context member | |
e8f0d548 PP |
781 | # named `member_name` within the data stream type `dst`. |
782 | def ds_op_pkt_ctx_op(dst: barectf_config.DataStreamType, member_name: str) -> _Op: | |
1624d186 PP |
783 | ret_op = None |
784 | ||
e8f0d548 | 785 | for op in ds_ops[dst].pkt_ctx_op.subops: |
d6483c83 | 786 | if op.top_name == member_name and type(op) is _WriteOp: |
1624d186 PP |
787 | ret_op = op |
788 | break | |
789 | ||
790 | assert ret_op is not None | |
791 | return typing.cast(_Op, ret_op) | |
d6483c83 | 792 | |
e8f0d548 | 793 | ds_ops = create_ds_ops() |
f42a1daf PP |
794 | return self._create_file_template('barectf.c.j2').render(header_file_name=header_file_name, |
795 | bitfield_header_file_name=bitfield_header_file_name, | |
796 | root_ft_prefixes=_RootFtPrefixes, | |
797 | root_ft_prefix_names=_ROOT_FT_PREFIX_NAMES, | |
798 | ds_ops=ds_ops, | |
799 | ds_op_pkt_ctx_op=ds_op_pkt_ctx_op) |