20.15.2 The Map Operator

Poke values reside in memory, and their in-memory representation is not visible from Poke programs. For example, 32 is a 32-bit signed integer value, and it happens to not be boxed in the Poke Virtual Machine. Therefore, it occupies exactly 32-bit in the memory of the machine running poke. Other values, like arrays for example, are boxed, and they need to store various meta-data.

Regardless of the internal representation, we say these values live “in memory”. Now, it is also possible to “map” a value to some area in some underlying IO space. This is done with the map operator @, which has two alternate syntax:

type @ offset
type @ ios : offset

The ternary version creates a new value using the data located at the offset offset in the specified IO space ios, which shall be an expression evaluating to a signed 32-bit integer.

The binary version uses the current IO space.

If there is no IO space, or the specified IO space doesn’t exist, an E_no_ios exception is raised:

(poke) int @ 0#B
unhandled no IOS exception

The value created in a map can be either mapped or not mapped. Mapping simple types produces not mapped values, whereas mapping non-simple types create mapped values.

The value attributes mapped and offset can be used to check whether a value is mapped or not, and in that case the offset where it is mapped:

(poke) var p = Packet @ 0#B
(poke) p'mapped
0x1
(poke) p'offset
0x0UL#b

Using the offset attribute in a not mapped value results in the E_no_map exception being raised:

(poke) [1,2,3]'mapped
0x0
(poke) [1,2,3]'offset
unhandled no map exception

If the type specified in the map is not a simple type, like an array or a struct, the resulting value is said to be mapped in the IO space:

(poke) type Packet = struct { int i; long l; }
(poke) Packet @ 0#B
Packet {i=0x464c457f,l=0x10102L}
(poke) uint<8>[2] @ 0#B
[0x7fUB,0x45UB]

A very important idea on Poke mapping is that it should be possible to manipulate mapped and non-mapped values in a transparent way. For example, consider the quick sort implementation in poke’s standard library. The prototype is:

fun qsort = (any[] array, Comparator cmp_f,
               long left = 0, long right = array'length - 1) void

qsort works with both mapped and not-mapped arrays:

(poke) var a = [2,3,1]
(poke) var b = int[3] @ 0#B
(poke) b
[1179403647,65794,0]
(poke) qsort (a, IntComparator)
(poke) a
[1,2,3]
(poke) qsort (b, IntComparator)
(poke) b
[0,33620224,1179403647]
(poke) dump :from b'offset :size b'size :ascii 0
76543210  0011 2233 4455 6677 8899 aabb ccdd eeff
00000000: 0000 0000 0001 0102 7f45 4c46

Similarly, you can write functions that operate on abstract entities and data structures such as ELF relocations and sections, DWARF DIEs, etc, and the same code will work with non mapped and mapped values.