24.6.15 Optional Fields

Sometimes a field in a struct is optional, i.e. it exists or not depending on some criteria. A good example of this is the “extended header” in a MP3 id3v2 tag. From the specification:

The second bit (bit 6) indicates whether or not the header is followed
by an extended header. The extended header is described in section
3.2. A set bit indicates the presence of an extended header.

In order to express this in a Poke struct type, we could of course use an union, like:

type ID3V2_Tag =
  struct
  {
    ID3V2_Hdr hdr;
    union
    {
      ID3V2_Ext_Hdr hdr : hdr.extended_hdr_present;
      struct {};
    } ext;
    ...
  }

That’s it, we use an union with an empty alternative. However, this approach has two problems. First, it is a bit cumbersome to write and it becomes necessary to introduce an intermediate named field like ext above. Second, and most importantly, the constraint expression in the union alternative is evaluated only once the alternative has been already decoded.

Therefore Poke provides a more convenient way to denote this:

type ID3V2_Tag =
  struct
  {
     ID3V2_Hdr hdr;
     if (hdr.extended_hdr_present)
       IDV2_Ext_Hdr ext_hdr;
     ...
  }

In the example above ext_hdr will be decoded only if hdr.extended_hdr_present is true, which is what we want.

Sometimes, however, we really need to decode a field to decide whether it is present or absent in the final struct. In these cases, we can use a suffix syntax:

type ID3V2_Tag =
  struct
  {
     ID3V2_Hdr hdr;
     IDV2_Ext_Hdr ext_hdr if hdr.extended_hdr_present
                             && ext_hdr.invisible;
     ...
  }

Note how in this example the field ext_hdr can appear itself in the conditional expression.

If both a constraint and a suffix optional field expression are specified, the second should follow the first.

One important characteristic of optional fields with suffix expressions to keep in mind is that they are always constructed or mapped, even when they are omitted. This is because, generally speaking, the value of an optional field may be necessary in order to determine whether the optional field is present or not.

This may result on unexpected results for the unaware pokist, like in the example below:

struct
{
  Header hdr;
  byte[hdr.len - hdr.padding] data if hdr.len >= hdr.padding;
}

where the array index may underflow if hdr.len is less or equal than hdr.padding.

The obvious solution for this problem is to use a prefix conditional expression instead.

For obvious reasons, optional fields are not allowed in unions.