People designing binary encoded formats tend to be cautious and try to avoid future backward incompatibilities by keeping some unused fields that are reserved for future use. This is the first kind of padding we will be looking at, and is particularly common in structures like headers.
See for example the header used to characterize compressed section contents in ELF files:
type Elf64_Chdr = struct { Elf_Word ch_type; Elf_Word ch_reserved; offset<Elf64_Xword,B> ch_size; offset<Elf64_Xword,B> ch_addralign; };
where the ch_reserved
field is reserved for future use. When
the time comes the space occupied by that field (32 bits in this case)
will be used to hold additional data in the form of one or more
fields. The idea is that implementations of the older formats will
still work.
The most obvious way to handle this in Poke is using a named field
like ch_reserved
above. This field will be decoded/encoded by
poke when constructing/mapping/writing struct values of this type, and
will be available to the user as chdr.ch_reserved
.
Sometimes reserved space is required to be filled with certain data values, such as zeroes. This may be to simplify things, or to force data producers to initialize the memory in order to avoid potential leaking of sensible information. In these cases we can use Poke initial values:
type Elf64_Chdr = struct { Elf_Word ch_type; Elf_Word ch_reserved == 0; offset<Elf64_Xword,B> ch_size; offset<Elf64_Xword,B> ch_addralign; };
This will make poke to check that ch_reserved
is zero when
constructing or mapping headers for compressed sections raising a
constraint violation exception otherwise. It will also make poke to
make sure ch_reserved
to 0 when constructing Elf64_Chdr
struct values:
(poke) Elf64_Chdr { ch_reserved = 23 } unhandled constraint violation exception
An alternative way to characterize reserved space in Poke is to use anonymous fields. For example:
type Elf64_Chdr = struct { Elf_Word ch_type; Elf_Word; offset<Elf64_Xword,B> ch_size; offset<Elf64_Xword,B> ch_addralign; };
Using Poke anonymous fields to implement reserved fields has at least
two advantages. First, the user cannot anymore temper with the data
in the reserved space in an easy way, i.e. chdr.ch_reserved
= 666
is no longer valid. Second, the printed representation of
anonymous struct fields is more compact and denotes better than the
involved space is not to be messed with:
(poke) Elf64_Chdr {} Elf64_Chdr { ch_type=0x0U, 0x0U, ch_size=0x0UL#B, ch_addralign=0x0UL#B }
A disadvantage of using anonymous fields is that you cannot specify constraint expressions for them, nor initial values. At some point we will probably add syntax to declare certain struct fields as read-only.
At this point, it is important to note that anonymous fields are still encoded/decoded by poke every time the struct value is mapped or written, exactly like regular fields. Therefore using them doesn’t pose any advantage in terms of performance.