This patch adds support for user dynamic array fields.
The element field type of a user dynamic array field type can be any of
the following:
* Bit array field type.
* String field type.
* Static array field type.
Note that it _cannot_ be another dynamic array field type.
The new `barectf.DynamicArrayFieldType` represents a dynamic array field
type. Its constructor accepts a length field type. As of this version,
this length field type must be within the same immediate structure field
type and before it.
The YAML ways to specify a dynamic array field type are:
barectf 2 configuration:
class: array
length: dynamic
element-type:
...
barectf 3 configuration:
class: dynamic-array
element-field-type:
...
Note that in YAML, you don't specify the length field type: the parser
automatically creates a 32-bit, byte-aligned unsigned integer field type
before which, for a dynamic array field type named `a`, has the name
`__a_len`. This also becomes part of the corresponding tracing function
parameter's name.
In the future, I can add a `length-field-type` property to a barectf 3
YAML dynamic array field type to point to an anterior unsigned integer
field type using some kind of reference, for example:
class: structure
members:
- my_length: uint16
- my_array:
field-type:
class: dynamic-array
length-field-type-name: my_length
element-field-type:
...
This would make it possible for more than one dynamic array fields
to use the same length field, for example:
class: structure
members:
- my_length: uint16
- my_uint_array:
field-type:
class: dynamic-array
length-field-type-name: my_length
element-field-type: uint8
- my_string_array:
field-type:
class: dynamic-array
length-field-type-name: my_length
element-field-type: string
The constructor of `barectf.StructureFieldType` calls
_set_dyn_array_ft_length_ft_member_names() which, for each member having
a dynamic array field type:
* Sets its `_length_ft_member_name` attribute to the name of the
structure field type member having its length field type.
* Sets its length field type's `_is_len` attribute to `True`.
I consider those as hacks, but considering the current constraints, it
makes parts of the (barectf) code easier to implement and maintain.
The C code generation approach is similar to the static array field
case. The `*-write-static-array-statements.j2` templates are renamed to
`*-write-array-statements.j2` and use the `length_src` variable as the
loop's length's value. `*-write-static-array-statements.j2` and the new
`*-write-dynamic-array-statements.j2` set `length_src` before including
`*-write-array-statements.j2`.
To make things easier, barectf now systematically generates alignment
statements if the alignment is greater than one. This could be optimized
again in the future, considering arrays this time. The
`_WriteOp.offset_in_byte` optimization still exists, although as soon as
it's not statically known, it's now `None` and
`serialize-write-bit-array-statements.j2` uses the safe, dynamic
`ctx->at % 8` expression. try_create_align_op() does this, more or less:
If `self._offset_in_byte` is not currently known and the requested
alignment is 8:
Set `self._offset_in_byte` to 0.
Else:
If we're currently within an array operation:
Reset `self._offset_in_byte`.
Else:
If `self._offset_in_byte` is currently known:
Align `self._offset_in_byte` with the requested alignment.
This ensures that each array field's element is aligned before being
written.
Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
ConfigurationOptions = barectf_config.ConfigurationOptions
DEFAULT_FIELD_TYPE = barectf_config.DEFAULT_FIELD_TYPE
DisplayBase = barectf_config.DisplayBase
+DynamicArrayFieldType = barectf_config.DynamicArrayFieldType
EnumerationFieldTypeMapping = barectf_config.EnumerationFieldTypeMapping
EnumerationFieldTypeMappingRange = barectf_config.EnumerationFieldTypeMappingRange
EnumerationFieldTypeMappings = barectf_config.EnumerationFieldTypeMappings
# Leaf operation (abstract class).
class _LeafOp(_Op):
- def __init__(self, offset_in_byte: Count, ft: barectf_config._FieldType, names: List[str],
- level: Count, templates: _OpTemplates):
- super().__init__(ft, names, level, templates)
- assert offset_in_byte >= 0 and offset_in_byte < 8
- self._offset_in_byte = offset_in_byte
-
- @property
- def offset_in_byte(self) -> Count:
- return self._offset_in_byte
+ pass
# An "align" operation.
class _AlignOp(_LeafOp):
- def __init__(self, offset_in_byte: Count, ft: barectf_config._FieldType, names: List[str],
- level: Count, templates: _OpTemplates, value: Alignment):
- super().__init__(offset_in_byte, ft, names, level, templates)
+ def __init__(self, ft: barectf_config._FieldType, names: List[str], level: Count,
+ templates: _OpTemplates, value: Alignment):
+ super().__init__(ft, names, level, templates)
self._value = value
@property
# A "write" operation.
class _WriteOp(_LeafOp):
- pass
+ def __init__(self, ft: barectf_config._FieldType, names: List[str], level: Count,
+ templates: _OpTemplates, offset_in_byte: Optional[Count]):
+ super().__init__(ft, names, level, templates)
+ assert offset_in_byte is None or (offset_in_byte >= 0 and offset_in_byte < 8)
+ self._offset_in_byte = offset_in_byte
+
+ @property
+ def offset_in_byte(self) -> Optional[Count]:
+ return self._offset_in_byte
_SpecSerializeWriteTemplates = Mapping[str, barectf_template._Template]
# and return it.
class _OpBuilder:
def __init__(self, cg: '_CodeGen'):
- self._last_alignment: Optional[Alignment] = None
- self._last_bit_array_size: Optional[Count] = None
self._names: List[str] = []
self._level = Count(0)
- self._offset_in_byte = Count(0)
+ self._offset_in_byte: Optional[Count] = None
self._cg = cg
+ # Whether or not we're within an array operation.
+ @property
+ def _in_array(self):
+ return self._level > 0
+
# Creates and returns an operation for the root structure field type
# `ft` named `name`.
#
assert type(ft) is not barectf_config.StructureFieldType
offset_in_byte = self._offset_in_byte
- if isinstance(ft, barectf_config._BitArrayFieldType):
+ if isinstance(ft, barectf_config._BitArrayFieldType) and self._offset_in_byte is not None:
self._offset_in_byte = Count((self._offset_in_byte + ft.size) % 8)
serialize_write_templ: Optional[barectf_template._Template] = None
elif type(ft) is barectf_config.StringFieldType:
size_write_templ = self._cg._size_write_string_statements_templ
- return _WriteOp(offset_in_byte, ft, self._names, self._level,
- _OpTemplates(serialize_write_templ, size_write_templ))
+ return _WriteOp(ft, self._names, self._level,
+ _OpTemplates(serialize_write_templ, size_write_templ), offset_in_byte)
# Creates and returns an "align" operation for the field type
# `ft` if needed.
#
# This function updates the builder's state.
- def try_create_align_op(alignment: Alignment, do_align: bool,
- ft: barectf_config._FieldType) -> Optional[_AlignOp]:
+ def try_create_align_op(alignment: Alignment, ft: barectf_config._FieldType) -> Optional[_AlignOp]:
def align(v: Count, alignment: Alignment) -> Count:
return Count((v + (alignment - 1)) & -alignment)
- offset_in_byte = self._offset_in_byte
- self._offset_in_byte = Count(align(self._offset_in_byte, alignment) % 8)
+ if self._offset_in_byte is None and alignment % 8 == 0:
+ self._offset_in_byte = Count(0)
+ else:
+ if self._in_array:
+ self._offset_in_byte = None
+ elif self._offset_in_byte is not None:
+ self._offset_in_byte = Count(align(self._offset_in_byte, alignment) % 8)
- if do_align and alignment > 1:
- return _AlignOp(offset_in_byte, ft, self._names, self._level,
+ if alignment > 1:
+ return _AlignOp(ft, self._names, self._level,
_OpTemplates(self._cg._serialize_align_statements_templ,
self._cg._size_align_statements_templ),
alignment)
return None
- # Returns whether or not, considering the alignment requirement
- # `align_req` and the builder's current state, we must create
- # and append an "align" operation.
- def must_align(align_req: Alignment) -> bool:
- return self._last_alignment != align_req or typing.cast(Count, self._last_bit_array_size) % align_req != 0
-
# Returns whether or not `ft` is a compound field type.
def ft_is_compound(ft: barectf_config._FieldType) -> bool:
return isinstance(ft, (barectf_config.StructureFieldType, barectf_config.StaticArrayFieldType))
# operations to return
ops: List[_Op] = []
- is_uuid_ft = type(ft) is barectf_config.StaticArrayFieldType and self._names == [_RootFtPrefixes.PH, 'uuid']
-
- if type(ft) is barectf_config.StringFieldType or is_uuid_ft:
+ if type(ft) is barectf_config.StringFieldType or self._names == [_RootFtPrefixes.PH, 'uuid']:
# strings and UUID array are always byte-aligned
- do_align = must_align(Alignment(8))
- self._last_alignment = Alignment(8)
- self._last_bit_array_size = Count(8)
- op = try_create_align_op(Alignment(8), do_align, ft)
+ op = try_create_align_op(Alignment(8), ft)
if op is not None:
ops.append(op)
ops.append(create_write_op(ft))
else:
- do_align = must_align(ft.alignment)
- self._last_alignment = ft.alignment
-
if ft_is_compound(ft):
- # reset last bit array size
- self._last_bit_array_size = typing.cast(Count, ft.alignment)
- else:
- assert isinstance(ft, barectf_config._BitArrayFieldType)
- ft = typing.cast(barectf_config._BitArrayFieldType, ft)
- self._last_bit_array_size = ft.size
+ self._offset_in_byte = None
- init_align_op = try_create_align_op(ft.alignment, do_align, ft)
+ init_align_op = try_create_align_op(ft.alignment, ft)
subops: List[_Op] = []
if type(ft) is barectf_config.StructureFieldType:
ft = typing.cast(barectf_config.StructureFieldType, ft)
if init_align_op is not None:
- # append structure field's alignment as a suboperation
+ # Append structure field's alignment as a
+ # suboperation.
+ #
+ # This is not strictly needed (could be appended to
+ # `ops`), but the properties of `_StreamOps` and
+ # `_EvOps` offer a single (structure field type)
+ # operation.
subops.append(init_align_op)
# append suboperations for each member
_OpTemplates(self._cg._serialize_write_struct_statements_templ,
self._cg._size_write_struct_statements_templ),
subops))
- elif type(ft) is barectf_config.StaticArrayFieldType:
- ft = typing.cast(barectf_config.StaticArrayFieldType, ft)
+ elif isinstance(ft, barectf_config._ArrayFieldType):
+ ft = typing.cast(barectf_config._ArrayFieldType, ft)
+ assert ft.alignment == 1 or init_align_op is not None
if init_align_op is not None:
- # append static array field's alignment as a suboperation
- subops.append(init_align_op)
+ ops.append(init_align_op)
# append element's suboperations
self._level = Count(self._level + 1)
spec_serialize_write_templates)
self._level = Count(self._level - 1)
- # create static array field's compound operation
- ops.append(_CompoundOp(ft, self._names, self._level,
- _OpTemplates(self._cg._serialize_write_static_array_statements_templ,
- self._cg._size_write_static_array_statements_templ),
- subops))
+ # select the right templates
+ if type(ft) is barectf_config.StaticArrayFieldType:
+ templates = _OpTemplates(self._cg._serialize_write_static_array_statements_templ,
+ self._cg._size_write_static_array_statements_templ)
+ else:
+ assert type(ft) is barectf_config.DynamicArrayFieldType
+ templates = _OpTemplates(self._cg._serialize_write_dynamic_array_statements_templ,
+ self._cg._size_write_dynamic_array_statements_templ)
+
+ # create array field's compound operation
+ ops.append(_CompoundOp(ft, self._names, self._level, templates, subops))
else:
# leaf field: align + write
if init_align_op is not None:
self._serialize_write_string_statements_templ = self._create_template('serialize-write-string-statements.j2')
self._serialize_write_struct_statements_templ = self._create_template('serialize-write-struct-statements.j2')
self._serialize_write_static_array_statements_templ = self._create_template('serialize-write-static-array-statements.j2')
+ self._serialize_write_dynamic_array_statements_templ = self._create_template('serialize-write-dynamic-array-statements.j2')
self._serialize_write_magic_statements_templ = self._create_template('serialize-write-magic-statements.j2')
self._serialize_write_uuid_statements_templ = self._create_template('serialize-write-uuid-statements.j2')
self._serialize_write_stream_type_id_statements_templ = self._create_template('serialize-write-stream-type-id-statements.j2')
self._size_write_string_statements_templ = self._create_template('size-write-string-statements.j2')
self._size_write_struct_statements_templ = self._create_template('size-write-struct-statements.j2')
self._size_write_static_array_statements_templ = self._create_template('size-write-static-array-statements.j2')
+ self._size_write_dynamic_array_statements_templ = self._create_template('size-write-dynamic-array-statements.j2')
# Creates and returns a template named `name` which is a file
# template if `is_file_template` is `True`.
elif type(ft) is barectf_config.StringFieldType:
return _PointerCType(_ArithCType('char', True), is_const)
else:
- assert type(ft) is barectf_config.StaticArrayFieldType
- ft = typing.cast(barectf_config.StaticArrayFieldType, ft)
+ assert isinstance(ft, barectf_config._ArrayFieldType)
+ ft = typing.cast(barectf_config._ArrayFieldType, ft)
return _PointerCType(self._ft_c_type(ft.element_field_type, True), is_const)
# Returns the function prototype parameters for the members of the
if member_name in exclude_set:
continue
- if only_dyn and not member.field_type.size_is_dynamic:
+ is_dyn = member.field_type.size_is_dynamic
+
+ if isinstance(member.field_type, barectf_config.UnsignedIntegerFieldType):
+ ft = typing.cast(barectf_config.UnsignedIntegerFieldType, member.field_type)
+ is_dyn = is_dyn or ft._is_len
+
+ if only_dyn and not is_dyn:
continue
params.append(_FtParam(member.field_type, member_name))
return self._length
+class DynamicArrayFieldType(_ArrayFieldType):
+ def __init__(self, length_field_type: UnsignedIntegerFieldType, element_field_type: _FieldType):
+ super().__init__(element_field_type)
+ self._length_field_type = length_field_type
+ self._length_ft_member_name = None
+
+ @property
+ def length_field_type(self):
+ return self._length_field_type
+
+ @property
+ def size_is_dynamic(self):
+ return True
+
+
class StructureFieldTypeMember:
def __init__(self, field_type: _FieldType):
self._field_type = field_type
self._members = StructureFieldTypeMembers(members)
self._set_alignment()
+ self._set_dyn_array_ft_length_ft_member_names()
def _set_alignment(self):
self._alignment: Alignment = self._minimum_alignment
if member.field_type.alignment > self._alignment:
self._alignment = member.field_type.alignment
+ def _set_dyn_array_ft_length_ft_member_names(self):
+ for member in self._members.values():
+ if type(member.field_type) is DynamicArrayFieldType:
+ # Find length field type member name within the same
+ # structure field type members.
+ for len_name, len_member in self._members.items():
+ if member.field_type.length_field_type is len_member.field_type:
+ member.field_type._length_ft_member_name = len_name
+ len_member.field_type._is_len = True
+ break
+
+ if member.field_type.alignment > self._alignment:
+ self._alignment = member.field_type.alignment
+
@property
def minimum_alignment(self) -> Alignment:
return self._minimum_alignment
'floating-point': self._conv_real_ft_node,
'str': self._conv_string_ft_node,
'string': self._conv_string_ft_node,
- 'array': self._conv_static_array_ft_node,
+ 'array': self._conv_array_ft_node,
'struct': self._conv_struct_ft_node,
'structure': self._conv_struct_ft_node,
}
# Converts a v2 array field type node to a v3 (static) array field
# type node and returns it.
- def _conv_static_array_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
- # class renamed to `static-array`
- v3_ft_node: _MapNode = collections.OrderedDict({'class': 'static-array'})
-
- # copy `length` property
- _copy_prop_if_exists(v3_ft_node, v2_ft_node, 'length')
+ def _conv_array_ft_node(self, v2_ft_node: _MapNode) -> _MapNode:
+ # class renamed to `static-array` or `dynamic-array`
+ is_dynamic = v2_ft_node['length'] == 'dynamic'
+ array_type = 'dynamic' if is_dynamic else 'static'
+ v3_ft_node: _MapNode = collections.OrderedDict({'class': f'{array_type}-array'})
+
+ # copy `length` property if it's a static array field type
+ if not is_dynamic:
+ _copy_prop_if_exists(v3_ft_node, v2_ft_node, 'length')
# convert element field type
v3_ft_node['element-field-type'] = self._conv_ft_node(v2_ft_node['element-type'])
from barectf.config_parse_common import _append_error_ctx
from barectf.config_parse_common import _MapNode
import barectf.config as barectf_config
-from barectf.config import _OptFt, _OptStructFt
+from barectf.config import _OptStructFt
import collections
import uuid
from barectf.typing import Count, Alignment, VersionNumber
ignore_include_not_found: bool):
super().__init__(root_file, node, with_pkg_include_dir, inclusion_dirs,
ignore_include_not_found, VersionNumber(3))
- self._ft_cls_name_to_create_method: Dict[str, Callable[[_MapNode], barectf_config._FieldType]] = {
+ self._ft_cls_name_to_create_method: Dict[str, Callable[[_MapNode],
+ List[barectf_config._FieldType]]] = {
'unsigned-integer': self._create_int_ft,
'signed-integer': self._create_int_ft,
'unsigned-enumeration': self._create_enum_ft,
'real': self._create_real_ft,
'string': self._create_string_ft,
'static-array': self._create_static_array_ft,
+ 'dynamic-array': self._create_dynamic_array_ft,
'structure': self._create_struct_ft,
}
self._parse()
# Creates an integer field type from the unsigned/signed integer
# field type node `ft_node`.
- def _create_int_ft(self, ft_node: _MapNode) -> barectf_config._IntegerFieldType:
+ def _create_int_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
ft_type = {
'unsigned-integer': barectf_config.UnsignedIntegerFieldType,
'signed-integer': barectf_config.SignedIntegerFieldType,
}[ft_node['class']]
- return self._create_common_int_ft(ft_node, ft_type)
+ return [self._create_common_int_ft(ft_node, ft_type)]
# Creates an enumeration field type from the unsigned/signed
# enumeration field type node `ft_node`.
- def _create_enum_ft(self, ft_node: _MapNode) -> barectf_config._EnumerationFieldType:
+ def _create_enum_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
ft_type = {
'unsigned-enumeration': barectf_config.UnsignedEnumerationFieldType,
'signed-enumeration': barectf_config.SignedEnumerationFieldType,
mappings[label] = barectf_config.EnumerationFieldTypeMapping(ranges)
- return typing.cast(barectf_config._EnumerationFieldType,
- self._create_common_int_ft(ft_node, ft_type,
- barectf_config.EnumerationFieldTypeMappings(mappings)))
+ return [typing.cast(barectf_config._EnumerationFieldType,
+ self._create_common_int_ft(ft_node, ft_type,
+ barectf_config.EnumerationFieldTypeMappings(mappings)))]
# Creates a real field type from the real field type node `ft_node`.
- def _create_real_ft(self, ft_node: _MapNode) -> barectf_config.RealFieldType:
- return typing.cast(barectf_config.RealFieldType,
- self._create_common_bit_array_ft(ft_node, barectf_config.RealFieldType,
- Alignment(8)))
+ def _create_real_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
+ return [typing.cast(barectf_config.RealFieldType,
+ self._create_common_bit_array_ft(ft_node, barectf_config.RealFieldType,
+ Alignment(8)))]
# Creates a string field type from the string field type node
# `ft_node`.
- def _create_string_ft(self, ft_node: _MapNode) -> barectf_config.StringFieldType:
- return barectf_config.StringFieldType()
+ def _create_string_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
+ return [barectf_config.StringFieldType()]
- # Creates a static array field type from the static array field type
- # node `ft_node`.
- def _create_static_array_ft(self, ft_node: _MapNode) -> barectf_config.StaticArrayFieldType:
+ def _create_array_ft(self, ft_type, ft_node: _MapNode, **kwargs) -> barectf_config._ArrayFieldType:
prop_name = 'element-field-type'
try:
- element_ft = self._create_ft(ft_node[prop_name])
+ element_fts = self._create_fts(ft_node[prop_name])
except _ConfigurationParseError as exc:
_append_error_ctx(exc, f'`{prop_name}` property')
- return barectf_config.StaticArrayFieldType(ft_node['length'], element_ft)
+ if len(element_fts) != 1 or isinstance(element_fts[0], (barectf_config.StructureFieldType,
+ barectf_config.DynamicArrayFieldType)):
+ raise _ConfigurationParseError(f'`{prop_name}` property',
+ 'Nested structure and dynamic array field types are not supported')
+
+ return ft_type(element_field_type=element_fts[0], **kwargs)
+
+ # Creates a static array field type from the static array field type
+ # node `ft_node`.
+ def _create_static_array_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
+ return [typing.cast(barectf_config.StaticArrayFieldType,
+ self._create_array_ft(barectf_config.StaticArrayFieldType, ft_node,
+ length=ft_node['length']))]
+
+ # Creates a dynamic array field type from the dynamic array field
+ # type node `ft_node`.
+ def _create_dynamic_array_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
+ # create length unsigned integer field type
+ len_ft = barectf_config.UnsignedIntegerFieldType(32, alignment=Alignment(8))
+ return [
+ len_ft,
+ typing.cast(barectf_config.DynamicArrayFieldType,
+ self._create_array_ft(barectf_config.DynamicArrayFieldType, ft_node,
+ length_field_type=len_ft))
+ ]
# Creates structure field type members from the structure field type
# members node `members_node`.
'Nested structure field types are not supported')
try:
- member_ft = self._create_ft(ft_node)
+ member_fts = self._create_fts(ft_node)
except _ConfigurationParseError as exc:
- exc._append_ctx(f'`{ft_prop_name}` property')
+ _append_error_ctx(exc, f'`{ft_prop_name}` property')
except _ConfigurationParseError as exc:
_append_error_ctx(exc, f'Structure field type member `{member_name}`')
- members[member_name] = barectf_config.StructureFieldTypeMember(member_ft)
+ if len(member_fts) == 2:
+ # The only case where this happens is a dynamic array
+ # field type node which generates an unsigned integer
+ # field type for the length and the dynamic array field
+ # type itself.
+ assert type(member_fts[1]) is barectf_config.DynamicArrayFieldType
+ members[f'__{member_name}_len'] = barectf_config.StructureFieldTypeMember(member_fts[0])
+ else:
+ assert len(member_fts) == 1
+
+ members[member_name] = barectf_config.StructureFieldTypeMember(member_fts[-1])
return barectf_config.StructureFieldTypeMembers(members)
# Creates a structure field type from the structure field type node
# `ft_node`.
- def _create_struct_ft(self, ft_node: _MapNode) -> barectf_config.StructureFieldType:
+ def _create_struct_ft(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
minimum_alignment = self._alignment_prop(ft_node, 'minimum-alignment')
if minimum_alignment is None:
if members_node is not None:
members = self._create_struct_ft_members(members_node, prop_name)
- return barectf_config.StructureFieldType(minimum_alignment, members)
+ return [barectf_config.StructureFieldType(minimum_alignment, members)]
- # Creates a field type from the field type node `ft_node`.
- def _create_ft(self, ft_node: _MapNode) -> barectf_config._FieldType:
+ # Creates field types from the field type node `ft_node`.
+ def _create_fts(self, ft_node: _MapNode) -> List[barectf_config._FieldType]:
return self._ft_cls_name_to_create_method[ft_node['class']](ft_node)
- # Creates a field type from the field type node `parent_node[key]`
+ # Creates field types from the field type node `parent_node[key]`
# if it exists.
- def _try_create_ft(self, parent_node: _MapNode, key: str) -> _OptFt:
+ def _try_create_fts(self, parent_node: _MapNode, key: str) -> Optional[List[barectf_config._FieldType]]:
if key not in parent_node:
return None
try:
- return self._create_ft(parent_node[key])
+ return self._create_fts(parent_node[key])
except _ConfigurationParseError as exc:
_append_error_ctx(exc, f'`{key}` property')
# satisfy static type checker (never reached)
raise
- # Like _try_create_ft(), but casts the result's type to
- # `barectf_config.StructureFieldType` to satisfy static type
- # checkers.
+ # Like _try_create_fts(), but casts the result's type (first and
+ # only element) to `barectf_config.StructureFieldType` to satisfy
+ # static type checkers.
def _try_create_struct_ft(self, parent_node: _MapNode, key: str) -> _OptStructFt:
- return typing.cast(barectf_config.StructureFieldType,
- self._try_create_ft(parent_node, key))
+ fts = self._try_create_fts(parent_node, key)
+
+ if fts is None:
+ return None
+
+ return typing.cast(barectf_config.StructureFieldType, fts[0])
# Returns the total number of members in the structure field type
# node `ft_node` if it exists, otherwise 0.
return None
assert type(ft_node) is collections.OrderedDict
- return self._create_ft(ft_node)
+ return self._create_fts(ft_node)[0]
def _create_stream_type(self, name: str, stream_type_node: _MapNode) -> barectf_config.StreamType:
try:
const: 32
uuid:
allOf:
- - $ref: https://barectf.org/schemas/config/2/field-type.json#/definitions/static-array-ft
+ - $ref: https://barectf.org/schemas/config/2/field-type.json#/definitions/array-ft
- properties:
length:
const: 16
required:
- class
additionalProperties: false
- static-array-ft-class-prop:
+ array-ft-class-prop:
type: string
const: array
- static-array-ft:
+ array-ft:
title: Static array field type object
type: object
properties:
class:
- $ref: '#/definitions/static-array-ft-class-prop'
+ $ref: '#/definitions/array-ft-class-prop'
element-type:
$ref: '#/definitions/ft'
length:
- type: integer
- minimum: 0
+ if:
+ type: integer
+ then:
+ minimum: 0
+ else:
+ type: string
+ const: dynamic
required:
- class
- element-type
- if:
properties:
class:
- $ref: '#/definitions/static-array-ft-class-prop'
+ $ref: '#/definitions/array-ft-class-prop'
then:
- $ref: '#/definitions/static-array-ft'
+ $ref: '#/definitions/array-ft'
- if:
properties:
class:
properties:
class: true
additionalProperties: false
+ array-ft:
+ title: Array field type object
+ allOf:
+ - $ref: '#/definitions/ft-base'
+ - properties:
+ element-field-type:
+ $ref: '#/definitions/ft'
+ required:
+ - element-field-type
static-array-ft-class-prop:
type: string
const: static-array
static-array-ft:
title: Static array field type object
allOf:
- - $ref: '#/definitions/ft-base'
+ - $ref: '#/definitions/array-ft'
- properties:
class:
$ref: '#/definitions/static-array-ft-class-prop'
- element-field-type:
- $ref: '#/definitions/ft'
length:
type: integer
minimum: 0
- required:
- - element-field-type
- - length
properties:
class: true
element-field-type: true
length: true
additionalProperties: false
+ dynamic-array-ft-class-prop:
+ type: string
+ const: dynamic-array
+ dynamic-array-ft-class-prop:
+ title: Dynamic array field type object
+ allOf:
+ - $ref: '#/definitions/array-ft'
+ - properties:
+ class:
+ $ref: '#/definitions/dynamic-array-ft-class-prop'
+ properties:
+ class: true
+ element-field-type: true
+ additionalProperties: false
struct-ft-class-prop:
type: string
enum:
- str
- string
- static-array
+ - dynamic-array
- struct
- structure
- if:
$ref: '#/definitions/static-array-ft-class-prop'
then:
$ref: '#/definitions/static-array-ft'
+ - if:
+ properties:
+ class:
+ $ref: '#/definitions/dynamic-array-ft-class-prop'
+ then:
+ $ref: '#/definitions/dynamic-array-ft'
- if:
properties:
class:
{% macro ft_call_params(param_prefix, ft, only_dyn=false) %}
{% if ft %}
{% for member_name, member in ft.members.items() %}
- {% if not only_dyn or member.field_type.size_is_dynamic %}
+ {% if not only_dyn or member.field_type.size_is_dynamic or member.field_type._is_len %}
, {{ param_prefix }}_{{ member_name }}
{%- endif %}
{% endfor %}
{% macro const_str(is_const) %}
{{ 'const ' if is_const else '' }}
{%- endmacro %}
+
+{#
+ # Generates the length variable name of the dynamic field type
+ # operation `op`.
+ #}
+{% macro dyn_array_ft_op_len_var_name(op) %}
+{{ op.names[0] }}_{{ op.ft._length_ft_member_name }}
+{%- endmacro %}
--- /dev/null
+{#
+ # The MIT License (MIT)
+ #
+ # Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
+ #
+ # Permission is hereby granted, free of charge, to any person obtaining
+ # a copy of this software and associated documentation files (the
+ # "Software"), to deal in the Software without restriction, including
+ # without limitation the rights to use, copy, modify, merge, publish,
+ # distribute, sublicense, and/or sell copies of the Software, and to
+ # permit persons to whom the Software is furnished to do so, subject to
+ # the following conditions:
+ #
+ # The above copyright notice and this permission notice shall be
+ # included in all copies or substantial portions of the Software.
+ #
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ #}
+{% import 'c/common.j2' as c_common %}
+{% set var_name = op.level | loop_var_name %}
+{% include 'c/serialize-write-statements-comment.j2' %}
+
+{
+ uint32_t {{ var_name }};
+
+ for ({{ var_name }} = 0; {{ var_name }} < (uint32_t) {{ length_src }}; ++{{ var_name }}) {
+{% for subop in op.subops %}
+ {{ subop.serialize_str(stream_type=stream_type, ev_type=ev_type) | indent_tab(2) }}
+
+{% endfor %}
+ }
+}
{%- else %}
{% set bo = 'le' if cfg.target_byte_order == barectf_config.ByteOrder.LITTLE_ENDIAN else 'be' %}
{% set c_type_non_const = c_type | replace('const ', '') %}
-bt_bitfield_write_{{ bo }}(&ctx->buf[_BITS_TO_BYTES(ctx->at)], {{ op.offset_in_byte }}, {{ op.ft.size }},
+ {% set offset_in_byte = 'ctx->at % 8' if op.offset_in_byte == none else op.offset_in_byte %}
+bt_bitfield_write_{{ bo }}(&ctx->buf[_BITS_TO_BYTES(ctx->at)], {{ offset_in_byte }}, {{ op.ft.size }},
{{ c_type_non_const }}, ({{ c_type_non_const }}) {{ src }});
{{ incr_at }};
{%- endif %}
--- /dev/null
+{#
+ # The MIT License (MIT)
+ #
+ # Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
+ #
+ # Permission is hereby granted, free of charge, to any person obtaining
+ # a copy of this software and associated documentation files (the
+ # "Software"), to deal in the Software without restriction, including
+ # without limitation the rights to use, copy, modify, merge, publish,
+ # distribute, sublicense, and/or sell copies of the Software, and to
+ # permit persons to whom the Software is furnished to do so, subject to
+ # the following conditions:
+ #
+ # The above copyright notice and this permission notice shall be
+ # included in all copies or substantial portions of the Software.
+ #
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ #}
+{% import 'c/common.j2' as c_common %}
+{% set length_src = c_common.dyn_array_ft_op_len_var_name(op) %}
+{% include 'c/serialize-write-array-statements.j2' %}
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#}
-{% import 'c/common.j2' as c_common %}
-{% set var_name = op.level | loop_var_name %}
-{% include 'c/serialize-write-statements-comment.j2' %}
-
-{
- uint32_t {{ var_name }};
-
- for ({{ var_name }} = 0; {{ var_name }} < (uint32_t) {{ op.ft.length }}; ++{{ var_name }}) {
-{% for subop in op.subops %}
- {{ subop.serialize_str(stream_type=stream_type, ev_type=ev_type) | indent_tab(2) }}
-{% endfor %}
- }
-}
+{% set length_src %}{{ op.ft.length }}U{% endset %}
+{% include 'c/serialize-write-array-statements.j2' %}
--- /dev/null
+{#
+ # The MIT License (MIT)
+ #
+ # Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
+ #
+ # Permission is hereby granted, free of charge, to any person obtaining
+ # a copy of this software and associated documentation files (the
+ # "Software"), to deal in the Software without restriction, including
+ # without limitation the rights to use, copy, modify, merge, publish,
+ # distribute, sublicense, and/or sell copies of the Software, and to
+ # permit persons to whom the Software is furnished to do so, subject to
+ # the following conditions:
+ #
+ # The above copyright notice and this permission notice shall be
+ # included in all copies or substantial portions of the Software.
+ #
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ #}
+{% import 'c/common.j2' as c_common %}
+{% set var_name = op.level | loop_var_name %}
+{% include 'c/serialize-write-statements-comment.j2' %}
+
+{
+ uint32_t {{ var_name }};
+
+ for ({{ var_name }} = 0; {{ var_name }} < (uint32_t) {{ length_src }}; ++{{ var_name }}) {
+{% for subop in op.subops %}
+ {{ subop.size_str(stream_type=stream_type, ev_type=ev_type) | indent_tab(2) }}
+
+{% endfor %}
+ }
+}
--- /dev/null
+{#
+ # The MIT License (MIT)
+ #
+ # Copyright (c) 2020 Philippe Proulx <pproulx@efficios.com>
+ #
+ # Permission is hereby granted, free of charge, to any person obtaining
+ # a copy of this software and associated documentation files (the
+ # "Software"), to deal in the Software without restriction, including
+ # without limitation the rights to use, copy, modify, merge, publish,
+ # distribute, sublicense, and/or sell copies of the Software, and to
+ # permit persons to whom the Software is furnished to do so, subject to
+ # the following conditions:
+ #
+ # The above copyright notice and this permission notice shall be
+ # included in all copies or substantial portions of the Software.
+ #
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ #}
+{% import 'c/common.j2' as c_common %}
+{% set length_src = c_common.dyn_array_ft_op_len_var_name(op) %}
+{% include 'c/size-write-array-statements.j2' %}
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#}
-{% import 'c/common.j2' as c_common %}
-{% set var_name = op.level | loop_var_name %}
-{% include 'c/serialize-write-statements-comment.j2' %}
-
-{
- uint32_t {{ var_name }};
-
- for ({{ var_name }} = 0; {{ var_name }} < (uint32_t) {{ op.ft.length }}; ++{{ var_name }}) {
-{% for subop in op.subops %}
- {{ subop.size_str(stream_type=stream_type, ev_type=ev_type) | indent_tab(2) }}
-{% endfor %}
- }
-}
+{% set length_src %}{{ op.ft.length }}U{% endset %}
+{% include 'c/size-write-array-statements.j2' %}
#}
struct {
{% for name, member in ft.members.items() %}
- {#
- # `chain` is a list of static array field types terminated with a
- # non-array field type (the most nested).
- #}
- {% set chain = ft_chain(member.field_type) %}
- {{ chain[-1] | ft_str | indent_tab }} {{ name }}
- {%- for array_ft in chain[:-1] %}[{{ array_ft.length }}]{% endfor %};
+ {{ member.field_type | deepest_ft | ft_str | indent_tab }} {{ name }}
+ {%- for len in member.field_type | ft_lengths(name) %}[{{ len }}]{% endfor %};
{% endfor %}
} align({{ ft.minimum_alignment }})
import barectf.config as barectf_config
import barectf.template as barectf_template
-from typing import List, Optional
+from typing import List, Optional, Union
import typing
return _STR_FT_TEMPL.render(ft=ft)
-def _ft_chain(ft: barectf_config._FieldType) -> List[barectf_config._FieldType]:
- chain: List[barectf_config._FieldType] = []
+def _filt_ft_lengths(ft: barectf_config._FieldType, name: str) -> List[Union[str, int]]:
+ lengths: List[Union[str, int]] = []
- while isinstance(ft, barectf_config.StaticArrayFieldType):
- chain.append(ft)
+ while isinstance(ft, barectf_config._ArrayFieldType):
+ if type(ft) is barectf_config.StaticArrayFieldType:
+ ft = typing.cast(barectf_config.StaticArrayFieldType, ft)
+ lengths.append(ft.length)
+ else:
+ assert type(ft) is barectf_config.DynamicArrayFieldType
+ ft = typing.cast(barectf_config.DynamicArrayFieldType, ft)
+ lengths.append(ft._length_ft_member_name)
+
+ ft = ft.element_field_type
+
+ return lengths
+
+
+def _filt_deepest_ft(ft: barectf_config._FieldType) -> barectf_config._FieldType:
+ while isinstance(ft, barectf_config._ArrayFieldType):
ft = ft.element_field_type
- chain.append(ft)
- return chain
+ return ft
def _gen_struct_ft(ft: barectf_config._FieldType) -> str:
- return _STRUCT_FT_TEMPL.render(ft=ft, ft_chain=_ft_chain)
+ return _STRUCT_FT_TEMPL.render(ft=ft)
_FT_CLS_TO_GEN_FT_FUNC = {
'disp_base_int': _filt_disp_base_int,
'int_ft_str': _filt_int_ft_str,
'ft_str': _filt_ft_str,
+ 'ft_lengths': _filt_ft_lengths,
+ 'deepest_ft': _filt_deepest_ft,
}