Skip to content

Advanced Features

This page covers advanced scenarios for special use cases.

Package IDs for Extended Addressing

Package IDs enable 16-bit message addressing for large systems.

When to use:

  • Systems with more than 255 messages
  • Multi-package systems requiring namespace separation

Frame compatibility: Use Extended or ExtendedMultiSystemStream payload types with package IDs.

Message ID encoding:

  • Without pkgid: 8-bit (0-255)
  • With pkgid: 16-bit = (package_id << 8) | msg_id

See Message Definitions for usage details.

Minimal Frames (Bandwidth-Limited Scenarios)

Minimal frames remove length and checksum fields for lowest overhead.

Requirements:

  • All messages must be fixed-size (no max_size, only size)
  • Parser needs message length lookup function

The generator creates a get_msg_info function automatically:

from frame_profiles import ProfileSensorWriter, ProfileSensorAccumulatingReader
from struct_frame.generated.messages import get_message_info
# Encode using sensor profile (Tiny + Minimal)
writer = ProfileSensorWriter(1024)
writer.write(msg)
frame = writer.data()
# Parser requires get_message_info callback for minimal profiles
reader = ProfileSensorAccumulatingReader(get_message_info=get_message_info)
reader.add_data(frame)
result = reader.next()
#include "frame_profiles.hpp"
#include "messages.structframe.hpp"
// Use sensor profile with minimal frames (requires message info callback)
ProfileSensorAccumulatingReader reader(get_message_info);

See Framing for more on minimal frames.

Large Messages

For messages > 255 bytes, use Extended payload type:

Terminal window
python -m struct_frame large.proto --build_c

Use BasicExtended or Network frame profile when encoding.

Variable-Length Encoding

Use option variable = true; for efficient encoding of messages with variable-length arrays or strings:

message LogEntry {
option msgid = 10;
option variable = true;
uint64 timestamp = 1;
string message = 2 [max_size=256];
repeated uint8 data = 3 [max_size=128];
}

Only used bytes are transmitted instead of full max_size. See Message Definitions for details.

Variable Oneofs

A oneof with a discriminator inside a variable message transmits only the active variant’s bytes by default (“trimmed” mode). Add option variable = true; to the oneof for a length-prefixed forward-compatible format.

Trimmed Mode (Default)

When the parent message is variable and the oneof has a discriminator (no option variable = true;), only the active variant’s bytes are sent. The receiver derives the size from the discriminator:

message CommandMessage {
option msgid = 20;
option variable = true;
oneof data {
option discriminator = "field_order";
SmallPayload small = 1; // 3 bytes on the wire when active
LargePayload large = 2; // 32 bytes on the wire when active
}
}

Variable Mode (Length-Prefixed)

Add option variable = true; to include a uint16 length prefix, allowing receivers to skip unknown variants:

message CommandMessage {
option msgid = 20;
option variable = true;
oneof data {
option discriminator = "field_order";
option variable = true;
SmallPayload small = 1; // Only its own bytes transmitted
LargePayload large = 2; // Only its own bytes transmitted
}
}

Wire format: [discriminator][uint16 length][variant_bytes]

Controlling Oneof Size

option max_size = N; — valid on non-variable (trimmed) oneofs only. Reserves N bytes in the in-memory struct union for future variants without changing the wire size of current variants:

oneof data {
option discriminator = "field_order";
option max_size = 64; // 64-byte in-memory buffer; not for variable oneofs
SmallPayload small = 1; // Still 3 bytes on the wire
}

option min_size = N; — valid when the parent message is variable = true and the oneof is either variable = true or the only variable-length (trimmed) field in the message. Pads the wire payload to at least N bytes for backward compatibility:

oneof data {
option discriminator = "field_order";
option variable = true;
option min_size = 8; // Transmit at least 8 bytes
SmallPayload small = 1; // 3 bytes → padded to 8 bytes on the wire
LargePayload large = 2; // 8 bytes → transmitted as-is
}

max_size applies to non-variable (trimmed) oneofs — it reserves in-memory space without affecting wire size per variant.

min_size applies to variable or trimmed oneofs in variable messages — it pads the wire payload and cannot exceed the oneof’s buffer size.

See Variable Oneofs in Message Definitions for the complete reference.