Skip to content

Flat Values

Canonical offset-based layouts for values stored in a runtime arena.

Updated View as Markdown

“Flat” describes the canonical in-arena representation of an Atoll value. A flat value uses fixed-width scalar fields and arena-relative handles rather than native pointers. The complete object graph can therefore move with its arena without rewriting internal references.

Flat values are runtime memory layouts. They are not the removed legacy FlatMessage wire codec, and an arbitrary arena image is not automatically a stable cross-version serialization format.

Encoding rules

  • multi-byte numbers are little-endian;
  • primitive values use their natural canonical width, listed below;
  • Char uses the four-byte u32 Unicode scalar representation;
  • structs use source field order with C-like alignment and padding;
  • enums store an i32 discriminant at offset 0, followed by the selected payload layout;
  • references to variable-size blocks are u32 arena-relative handles;
  • handle 0 represents null or an empty niche where the type permits it.

Padding belongs to the layout but does not carry language-level data.

The canonical collection-element widths on wasm32 are:

Width Kinds
1 byte i8, byte, bool
2 bytes i16, u16
4 bytes i32, u32, f32, char, arena handle
8 bytes int, u64, f64
16 bytes i128, u128

WebAssembly evaluation lanes can be wider than stored fields. For example, a one-byte bool is carried in an i32 Wasm lane.

Resolving handles

Suppose a struct field at offset 20 contains the value 96. 96 is not a WebAssembly address. It resolves relative to the current body base:

field address  = arena body base + 20
block address  = arena body base + 96

After growth, the body base may differ, but the values 20 and 96 still describe the same relationship.

A handle is meaningful only with the arena that owns it. Passing a bare offset between Unit instances, link groups, or restored arena generations without an explicit transfer protocol is invalid.

Strings

A stored string field is one four-byte handle. The referenced block begins with:

Offset Field
0 byte length, u32
4 byte capacity, u32
8 UTF-8 bytes

Handle 0 is the empty representation. Checked readers validate the block bounds and UTF-8. Trusted runtime paths may use a Safe view after the guest builder has established those invariants.

Lists

A stored list field is also a four-byte handle:

Offset Field
0 element count, u32
4 element capacity, u32
8 canonical element storage

Elements are laid out at the element type’s canonical size and alignment. Reference-containing elements store their own relative handles and participate in generated clone, drop, and mark walks.

The empty-list niche uses handle 0. Implementations must not derive a data pointer from it before checking the length or handle.

ABI lanes

Do not confuse an in-arena field layout with a compiler ABI lane. Frames and calls sometimes reserve a wider logical lane so generated code can materialize cached metadata:

Value Stored field Logical ABI lane
String 4-byte block handle 8 bytes
List 4-byte block handle 12 bytes

Lane zero contains the backing block handle. Length and capacity can be loaded or materialized into the remaining lanes. This is an execution convenience, not evidence that every stored string or list is a fat pointer.

Maps

Maps use a Swiss-table-style block. The first 16 bytes contain:

Offset Field
0 length
4 capacity
8 tombstone count
12 reserved

The header is followed by capacity + 16 control bytes and aligned key/value storage. Control byte 0x80 marks an empty bucket and 0xFE marks a tombstone. Other control values contain hash fragments used during probing.

Keys and values still use their canonical flat layouts. A map’s internal table is an implementation layout and must be traversed through generated/runtime operations rather than assumed by application code.

Sets

Sets use the same table organization as maps but store only keys. They share the empty and tombstone control values, probing discipline, and ownership requirements.

Function values

Compiled function identities are dense u32 FunctionId values for one build. They are not stable symbols across program versions. A Unit instance containing stored function values is therefore not automatically safe to adopt during a redeploy; the deployment compatibility check must prove that the relevant mapping remains valid or reject migration.

Checked views

Flat access has two trust levels:

  • checked access validates offsets, lengths, alignment, discriminants, and UTF-8 before exposing typed data;
  • trusted Safe views avoid repeating checks after a builder or runtime boundary has established the complete layout.

Trusted does not mean that bytes from an untrusted host or old program version may skip validation. It describes a proof already obtained in the current operation.

Copying

Copying the complete [FlatBuilderHeader | arena body] preserves all intra-arena relationships. Copying one referenced block alone generally does not: its nested handles still point into the source arena. Cross-arena value transfer therefore requires a generated deep clone or a typed marshal/unmarshal operation.

Host calls use typed layouts rather than exposing raw native pointers. At suspension boundaries, code retains handles and offsets or participates in the generation-pin protocol described in Host I/O.

Evolution

Flat layouts are shared by compiler-generated code and the runtime. Changes must be coordinated through ABI/version metadata and migration compatibility checks. In particular:

  • never infer current sizes from old prose when generated constants disagree;
  • never persist a build-local FunctionId as a cross-version identity;
  • never reinterpret an arena from a different layout fingerprint;
  • validate external or restored bytes before creating a trusted view.
Navigation

Type to search…

↑↓ navigate↵ selectEsc close