@@ -16,3 +16,71 @@ length using the `padding` keyword. This is useful when you need to ensure
1616that certain fields are aligned or when the structure requires reserved spaces.
1717
1818>>> field = padding[10 ] # greedy or dynamic size
19+
20+
21+ .. _tutorial-align_to :
22+
23+ Whole-model alignment
24+ ----------------------
25+
26+ .. versionadded :: 2.10.0
27+
28+ The :code: `padding ` field above reserves space explicitly, as an extra field
29+ of its own. Sometimes what you actually want is different: a whole
30+ :code: `@struct `, :code: `@union ` or :code: `@bitfield ` class whose *total *
31+ packed size is always rounded up to a multiple of some alignment.
32+ Pass :code: `align_to= ` to the decorator instead:
33+
34+ >>> @ struct(align_to = 4 )
35+ ... class Format :
36+ ... a: uint8
37+ ...
38+ >>> sizeof(Format)
39+ 4
40+ >>> unpack(Format, b " \x01\x00\x00\x00 " )
41+ Format(a=1)
42+
43+ This is deliberately different from :class: `~caterpillar.fields.Aligned ` and
44+ :func: `~caterpillar.fields.align `, which both pad relative to the *absolute *
45+ stream position. :code: `align_to= ` instead measures *this model's own * start
46+ and end, so the padding only ever depends on the model's own content - not on
47+ where it happens to be embedded:
48+
49+ >>> @ struct
50+ ... class Outer :
51+ ... b: uint8
52+ ... inner: Format
53+ ... c: uint8
54+ ...
55+ >>> pack(Outer(b = 1 , inner = Format(a = 2 ), c = 3 ))
56+ b'\x01\x02\x00\x00\x00\x03'
57+
58+ A bare :code: `int ` or context lambda is accepted directly, as shown above.
59+ Use :class: `~caterpillar.fields.AlignTo ` explicitly to customize the fill
60+ byte (or a multi-byte fill pattern) and/or relax verification:
61+
62+ >>> @ struct(align_to = AlignTo(4 , fill = 0x FF , strict = False ))
63+ ... class Lenient :
64+ ... a: uint8
65+
66+ With the default :code: `strict=True `, unpacking verifies that the padding
67+ bytes actually match :code: `fill ` and raises :code: `ValueError ` if they
68+ don't. With :code: `strict=False `, padding bytes are consumed without
69+ verification.
70+
71+ :code: `align_to= ` also works on :code: `@union ` (padding after the largest
72+ member) and :code: `@bitfield ` (padding after all bit-groups have been
73+ finalized to whole bytes), and applies per-element when the model is used
74+ inside an array, so each element keeps its own alignment:
75+
76+ >>> @ struct
77+ ... class Many :
78+ ... items: Format[2 ]
79+ ...
80+ >>> pack(Many(items = [Format(a = 1 ), Format(a = 2 )]))
81+ b'\x01\x00\x00\x00\x02\x00\x00\x00'
82+
83+ A dynamic (context-dependent) alignment value is supported too, but makes the
84+ model's size undeterminable ahead of time - :func: `~caterpillar.model.sizeof `
85+ raises :class: `~caterpillar.exception.DynamicSizeError ` in that case.
86+
0 commit comments