Skip to main content

Module tail

Module tail 

Source
Expand description

Hybrid serialization tail for #[hopper::state(dynamic_tail = T)].

Closes Hopper Safety Audit innovation I5 (“Hybrid serialization”). The rationale from the audit (page 14):

Lets Hopper own the fixed-layout hot path while still supporting a dynamic tail for vectors, strings, and optional metadata.

§Wire format

After the layout’s fixed body (offset TYPE_OFFSET + WIRE_SIZE), the tail is encoded as:

[ len: u32 LE ] [ payload: len bytes ]

The fixed-body fast path remains fully zero-copy. code that never touches the tail pays zero overhead. Tail access is explicit (tail_read::<T>() / tail_write::<T>()), which is why the tail is not zero-copy: the typed representation is reconstructed on read and serialized on write.

§Canonical tail encoding (TailCodec)

TailCodec is a minimal Borsh-subset serializer:

  • integers: native little-endian
  • [u8; N]: raw bytes, fixed width
  • bounded byte/string payloads: program-defined length prefix + bytes
  • Option<T>: 1-byte tag (0 = None, 1 = Some) + inner payload

Programs that need richer types (bounded strings, bounded vectors, custom structs) implement TailCodec themselves; the framework does not force a derive or pull Vec / String into the no-alloc runtime surface.

Traits§

TailCodec
Canonical serializer for dynamic-tail payloads.

Functions§

read_tail
Decode the tail as T: TailCodec, checking that the encoded length exactly matches the u32 prefix. Extra bytes beyond T’s decode are a malformed-encoding signal.
read_tail_len
Read the tail’s u32-LE length prefix.
tail_payload
Return a slice referencing just the tail payload bytes (excluding the 4-byte length prefix). Length-bounded by the u32 prefix.
write_tail
Encode tail into the account’s tail slot, rewriting the u32 length prefix. Returns AccountDataTooSmall when the existing account byte buffer can’t fit the encoded payload. in that case the caller should realloc first.