Building a binary header#
When a format specification gives field widths in bits, a small table of
Dtype objects can keep the field layout in one place. This example
uses that table to build and decode the fixed part of an MPEG sequence header.
from tibs import Dtype, Tibs
SEQUENCE_HEADER = [
("start_code", Dtype("hex32"), "000001b3"),
("width", Dtype("u12"), 352),
("height", Dtype("u12"), 288),
("aspect_ratio", Dtype("u4"), 1),
("frame_rate", Dtype("u4"), 3),
("bit_rate_value", Dtype("u18"), 5040), # Stored in 400 bit/s units.
("marker_bit", Dtype("bool"), True),
("vbv_buffer_size", Dtype("u10"), 20),
("constrained_parameters", Dtype("bool"), False),
]
header = Tibs.from_joined(
dtype.pack(value)
for _, dtype, value in SEQUENCE_HEADER
)
assert len(header) == 94
# The header is 94 bits, so byte serialization pads two zero bits on the right.
assert header.to_padded_bytes() == bytes.fromhex("000001b31601201304ec20a0")
fields = {}
offset = 0
for name, dtype, _ in SEQUENCE_HEADER:
fields[name] = dtype.unpack(header, offset, offset + dtype.length)
offset += dtype.length
assert offset == len(header)
assert fields == {
name: value
for name, _, value in SEQUENCE_HEADER
}
This is the scale where format-driven bit assembly is useful: compact headers,
control words, and test vectors. For a complete container or protocol parser,
use a higher-level parser for the structure and keep Tibs for the fields
that need bit-accurate construction or interpretation.