25.3.2 Offset Units

There are several ways to express the unit of an offset, which is always interpreted as a multiple of the basic unit, which is the bit (one bit).

25.3.2.1 Named Units

The first way is to refer to an unit by name. For example, 2#B for two bytes. Units are defined using the unit construction:

unit name = constant_expression [, name = constant_expression]...;

where name is the name of the unit, and constant_expression is a constant expression that should evaluate to an integral value. The resulting value is always coerced into an unsigned 64-bit integer.

Note that unit names live in a different namespace than variables and types. However, when a given name is both a type name and an unit name in an unit context, the named unit takes precedence:

(poke) type xx = int
(poke) unit xx = 2
(poke) 1#xx
1#2

Many useful units are defined in the standard library. See Standard Units.

25.3.2.2 Arbitrary Units

It is also possible to express units in multiples of the base unit, which is the bit. Using this syntax, it becomes possible to express offsets in any arbitrary unit, as disparate as it may seem:

17#3
0#12
8#1

That’s it: 17 units of 3 bits each, zero units of 12 bits each, and eight units of 1 bit each. Note that the unit should be greater than 0.

25.3.2.3 Types as Units

But then, why stop there? Poking is all about defining data structures and operating on them… so why not use these structures as units as well? Consider the following struct:

type Packet = struct { int i; long j; };

The size of a Packet is known at compile time (which is not generally true for Poke structs). Wouldn’t it be nice to use it as a unit in offsets? Sure it is:

23#Packet

The above is the size occupied by 23 packets. Any type whose size is known at compile time can be specified as an offset unit.

Expressing offsets as united values also relieves the programmer from doing many explicit unit conversions: poke can do them for you. Consider for example an ELF section header. One of its fields is the size of the described section, in bytes:

type Elf64_Shdr =
  struct
  {
   …
   offset<Elf64_Xword,B> sh_size;
   …
  };

If a given section is to contain, say, relocations with addends, we can set its size doing something like this:

shdr.sh_size = 10#Elf64_Rela;

Instead of doing the conversion to bytes explicitly.

If the magnitude of an offset is 1 then it is allowed to omit it entirely. To denote one kilobyte, for example, we can write #KB.