Skip to content

Type system

Every dynamic field in a RAD file is described by a type. Types are what let the format store each field at the narrowest width that fits and let a reader parse a value it has never seen before. A type is encoded as a single u8 identifier in a tag descriptor.

IDTypeWidthNotes
0bool1 byte
1u81 byte
2u162 bytes
3u324 bytes
4u648 bytes
5f324 bytesIEEE-754
6f648 bytesIEEE-754
7arrayvariablesee below
8stringvariableu16 length + bytes
9u12816 bytes
10i81 byte
11i162 bytes
12i324 bytes
13i648 bytes
14i12816 bytes

All multi-byte integers and floats are little-endian.

The scalar types (bool, the signed/unsigned integers, and the floats) are stored as their natural little-endian byte representation, with no length prefix. A bool is a single byte, conventionally 0 or 1.

A string is:

[ length : u16 ][ length bytes ]

the bytes being the (UTF-8/ASCII) characters, with no trailing NUL. Reference names and tag names are encoded this way.

An array descriptor records two further type identifiers — the type of its length field and the type of its elements:

descriptor: [ 7 ][ length_type : u8 ][ element_type : u8 ]
value: [ length : length_type ][ element … ] # `length` elements

For example, an array of u32 with a u32 length is descriptor 7, 3, 3, and a value is a u32 count followed by that many u32s. Arrays do not nest — the element type must be a scalar, not another array or string.

The signed integers (i8–i128) and the wide unsigned integer (u128) round out the set for fields that need them — for instance a barcode that does not fit in 64 bits. They are stored little-endian like the other integers. Not every consumer supports every width in every position; a reader that encounters a width it cannot handle for a given field should report a clear error rather than misparse.