4.9 Functions in Structs

Further reading of the Packet Specification 1.2 reveals that each Packet has an additional crc field. The content of this field is derived from both the payload bytes and the control bytes.

But this is no vulgar CRC we are talking about. On the contrary, it is a special function developed by the CRC Foundation in partnership with the Packet Foundation, called superCRC (patented, TM).

Fortunately, the CRC Foundation distributes a pickle supercrc.pk, that provides a calculate_crc function with the following spec:

fun calculate_crc = (byte[] data, byte[] control) int:

So let’s use the function like this in our type, after loading the supercrc pickle:

load supercrc;

type Packet =
  struct
  {
    byte magic == 0xab;
    byte size;

    var real_size = (size == 0xff ? 0 : size);

    byte[real_size] payload;
    byte[real_size] control;

    int crc = calculate_crc (payload, control);
  };

However, there is a caveat: it happens that the calculation of the CRC may involve arithmetic and division, so the CRC Foundation warns us that the calculate_crc function may raise E_div_by_zero. However, the Packet 1.2 Specification tells us that in these situations, the crc field of the packet should contain zero. If we used the type above, any exception raised by calculate_crc would be propagated by the mapper/constructor:

(poke) Packet @ 12#B
unhandled division by zero exception

A solution is to use a function that takes care of the extra needed logic, wrapping calculate_crc:

load supercrc;

type Packet =
  struct
  {
    byte magic == 0xab;
    byte size;

    var real_size = (size == 0xff ? 0 : size);

    byte[real_size] payload;
    byte[real_size] control;

    fun corrected_crc = int:
    {
      try return calculate_crc (payload, control);
      catch if E_div_by_zero { return 0; }
    }

    int crc == corrected_crc;
  };

Again, note how the function is accessible after its definition. Note as well how both fields and variables and other functions can be used in the function body. There is no difference to define variables and functions in struct types than to define them inside other functions or in the top-level environment: the same lexical rules apply.