3.2.1.1. Sequence#
A sequence functions independently of fields. The library introduces the Sequence
as a named finite collection of Field objects. A Sequence
operates on a model, which is a string-to-field mapping by default. Later, we will discuss
the distinctions between a Sequence and a Struct regarding the model representation.
A sequence definition entails the specification of a Sequence object by directly
indicating the model to use. Inheritance poses a challenge with sequences, as they are not
designed to operate on a type hierarchy. The default instantiation with all default options
involves passing the dictionary with all fields directly:
>>> Format = Sequence({"a": uint8, "b": uint32})
Programmers Note:
All sequence types introduced by this library can also store so-called unnamed fields.
These fields are not visible in the unpacked result and are automatically packed, removing
concerns about them when the option S_DISCARD_UNNAMED is active. Their names usually
begin with an underscore and must solely contain numbers (e.g., _123).
The sequence follows the Field configuration model, allowing sequence and
field-related options to be set. As mentioned earlier, the S_DISCARD_UNNAMED option can
be used for example to exclude all unnamed fields from the final representation. A complete
list of all configuration options and their impact can be found in Options / Flags.
All sequences store a configurable ByteOrder and Arch as architecture,
which are passed to all fields in the current model. For more information on why these
classes are not specified as an enum class, please refer to Byteorder and Architecture.
Inheritance in sequences is intricate, as a Sequence is constructed from a dictionary
of elements. We can attempt to simulate a chain of extended base sequences using the
concatenation of two sequences. The __add__() method will import all fields
from the other specified sequence. The only disadvantage is the placement required by the
operator. For instance:
>>> BaseFormat = Sequence({"magic": b"MAGIC", "a": uint8})
>>> Format = Sequence({"b": uint32, "c": uint16}) + BaseFormat
will result in the following field order:
>>> list(Format.get_members())
['b', 'c', 'magic', 'a']
which is not the intended order. The correct order should be ['magic', 'a', 'b', 'c'].
This can be achieved by using the BaseFormat instance as the first operand.
Warning
This will alter the BaseFormat sequence, making it unusable elsewhere as the base for
all sub-sequences. Therefore, it is not recommended to use inheritance within sequences.
The Struct class resolves this issue with ease.
Nesting sequences is allowed by default and can be achieved by incorporating another
Sequence into the model. It is important to note that nesting is distinct from
inheritance, adding an additional layer of packing and unpacking.
>>> Format = Sequence({"other": BaseFormat, "b": uint32})