This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Frame Layer: Binary Framing Protocol
Loading…
Frame Layer: Binary Framing Protocol
Relevant source files
- core/README.md
- core/src/constants.rs
- core/src/frame.rs
- core/src/frame/README.md
- core/src/frame/frame_codec.rs
- core/src/frame/frame_error.rs
- core/src/frame/frame_kind.rs
- core/src/frame/frame_mux_stream_decoder.rs
- core/src/frame/frame_stream_encoder.rs
- core/src/frame/frame_struct.rs
The Frame Layer is the foundational transport-agnostic layer of the Muxio library. It provides a binary-first protocol for multiplexing multiple logical streams over a single physical connection (e.g., TCP, WebSockets, or UDP). It handles serialization, chunking, sequencing, and out-of-order reassembly of binary data.
1. Data Structures & Constants
The core unit of communication is the Frame struct core/src/frame/frame_struct.rs:13-48 Every frame contains a fixed-size header followed by a variable-length payload.
Frame Structure
| Field | Type | Description |
|---|---|---|
stream_id | u32 | Identifies the logical stream core/src/frame/frame_struct.rs18 |
seq_id | u32 | Monotonically increasing sequence number for ordering core/src/frame/frame_struct.rs25 |
kind | FrameKind | The control type of the frame (Open, Data, End, etc.) core/src/frame/frame_struct.rs33 |
timestamp_micros | u64 | Send timestamp in microseconds (UNIX epoch) core/src/frame/frame_struct.rs40 |
payload | Vec<u8> | The actual binary data core/src/frame/frame_struct.rs47 |
FrameKind Enum
The FrameKind core/src/frame/frame_kind.rs:5-12 determines the lifecycle state of a logical stream:
Open (0): Initiates a stream; may contain initial data core/src/frame/frame_kind.rs6Data (1): Standard data transmission core/src/frame/frame_kind.rs7End (2): Graceful termination; no further frames will be sent core/src/frame/frame_kind.rs8Cancel (3): Immediate termination; remaining buffered frames should be discarded core/src/frame/frame_kind.rs9Ping (5)/Pong (4): Connectivity and latency checks core/src/frame/frame_kind.rs:10-11
Binary Layout
The protocol uses a fixed header size (21 bytes) core/src/constants.rs7 All multi-byte integers are encoded in Little-Endian format core/src/frame/frame_codec.rs:38-44
| Offset | Size | Field |
|---|---|---|
| 0 | 4 | Total Payload Length (u32) core/src/frame/frame_codec.rs38 |
| 4 | 4 | Stream ID (u32) core/src/frame/frame_codec.rs41 |
| 8 | 4 | Sequence ID (u32) core/src/frame/frame_codec.rs42 |
| 12 | 1 | Frame Kind (u8) core/src/frame/frame_codec.rs43 |
| 13 | 8 | Timestamp (u64) core/src/frame/frame_codec.rs44 |
| 21 | Var | Payload Data core/src/frame/frame_codec.rs45 |
Sources: core/src/frame/frame_struct.rs:13-48 core/src/frame/frame_kind.rs:5-12 core/src/frame/frame_codec.rs:34-48 core/src/constants.rs:1-7
2. Encoding and Decoding Logic
The FrameCodec core/src/frame/frame_codec.rs18 provides the stateless logic for converting between Frame objects and raw bytes.
encode(&Frame) -> Vec<u8>: Serializes the header and appends the payload. It pre-allocates the exact capacity needed (FRAME_HEADER_SIZE + payload.len()) to avoid multiple reallocations core/src/frame/frame_codec.rs:34-48decode(&[u8]) -> Result<DecodedFrame, FrameDecodeError>: Validates that the buffer is at leastFRAME_HEADER_SIZEcore/src/frame/frame_codec.rs:69-71 It returns aDecodedFramewhich wraps theFrameand an optional error core/src/frame/frame_struct.rs:51-54
Error Types
Errors are categorized into FrameEncodeError core/src/frame/frame_error.rs:4-12 and FrameDecodeError core/src/frame/frame_error.rs:27-37 Key errors include:
CorruptFrame: Data does not match the expected protocol format core/src/frame/frame_error.rs5 core/src/frame/frame_error.rs28IncompleteHeader: The buffer provided to the decoder is too small to read the header or the full payload core/src/frame/frame_error.rs36ReadAfterCancel/WriteAfterCancel: Attempting I/O on a stream that has been terminated viaCancelcore/src/frame/frame_error.rs11 core/src/frame/frame_error.rs33
Sources: core/src/frame/frame_codec.rs:9-128 core/src/frame/frame_error.rs:1-54 core/src/frame/frame_struct.rs:51-54
3. Stream Management
FrameStreamEncoder
The FrameStreamEncoder core/src/frame/frame_stream_encoder.rs:11-23 converts a continuous stream of bytes into a sequence of Frame objects for a single stream_id.
- Chunking : It accepts arbitrary byte slices via
write_bytesand splits them into chunks ofmax_chunk_sizecore/src/frame/frame_stream_encoder.rs:63-91 - State Tracking : It maintains the
next_seq_idand transitions thenext_kindfromOpentoDataafter the first frame is emitted core/src/frame/frame_stream_encoder.rs:86-87 - Callback Emission : Encoded bytes are passed to an
on_emitclosure, allowing integration with various transports (e.g., writing to a WebSocket) core/src/frame/frame_stream_encoder.rs:47-59
FrameMuxStreamDecoder
The FrameMuxStreamDecoder core/src/frame/frame_mux_stream_decoder.rs:31-34 is a stateful, multiplexed reassembler. Unlike the encoder, one decoder handles all active streams on a connection.
- Buffering : It maintains a
Vec<u8>for partial frames that haven’t fully arrived across the wire core/src/frame/frame_mux_stream_decoder.rs32 core/src/frame/frame_mux_stream_decoder.rs71 - Reassembly : It uses a
HashMap<u32, StreamReassembly>to tracknext_expectedsequence IDs for every stream core/src/frame/frame_mux_stream_decoder.rs33 core/src/frame/frame_mux_stream_decoder.rs:36-41 - Out-of-Order Handling : Frames arriving out of order are stored in a
BTreeMapkeyed byseq_idcore/src/frame/frame_mux_stream_decoder.rs38 They are only yielded to the application viaFrameDecoderIteratoronce all preceding frames for that stream have been processed core/src/frame/frame_mux_stream_decoder.rs:139-142
Sources: core/src/frame/frame_stream_encoder.rs:11-166 core/src/frame/frame_mux_stream_decoder.rs:31-158
4. System Flow Diagrams
Entity Mapping: Logical to Code
This diagram maps the conceptual framing protocol to the specific Rust entities implementing it.
Sources: core/src/frame/frame_mux_stream_decoder.rs:31-41 core/src/frame/frame_struct.rs:13-48 core/src/frame/frame_stream_encoder.rs:11-23
graph TD
subgraph "Logical Protocol"
["Binary Stream"] --> ["Multiplexed Frames"]
["Multiplexed Frames"] --> ["Stream ID 100"]
["Multiplexed Frames"] --> ["Stream ID 200"]
end
subgraph "Code Entity Space (core/src/frame/)"
direction LR
["FrameMuxStreamDecoder"] -- "manages" --> ["StreamReassembly"]
["FrameCodec"] -- "creates" --> ["Frame"]
["FrameStreamEncoder"] -- "uses" --> ["FrameCodec"]
end
["Multiplexed Frames"] -- "processed by" --> ["FrameMuxStreamDecoder"]
["Frame"] -- "defines" --> ["Multiplexed Frames"]
["Stream ID 100"] -- "tracked by" --> ["StreamReassembly"]
sequenceDiagram
participant App as "Application Layer"
participant Enc as "FrameStreamEncoder"
participant Codec as "FrameCodec"
participant Net as "Physical Transport (TCP/WS)"
participant MuxDec as "FrameMuxStreamDecoder"
App->>Enc: write_bytes(data)
Note over Enc: Chunks data by\nmax_chunk_size [core/src/frame/frame_stream_encoder.rs:73]
Enc->>Codec: encode(Frame)
Codec-->>Enc: Vec<u8> [core/src/frame/frame_codec.rs:34]
Enc->>Net: on_emit(bytes) [core/src/frame/frame_stream_encoder.rs:56]
Net->>MuxDec: read_bytes(bytes) [core/src/frame/frame_mux_stream_decoder.rs:70]
MuxDec->>Codec: decode(chunk) [core/src/frame/frame_mux_stream_decoder.rs:94]
Codec-->>MuxDec: DecodedFrame [core/src/frame/frame_codec.rs:121]
alt Out of Order
MuxDec->>MuxDec: Buffer in BTreeMap [core/src/frame/frame_mux_stream_decoder.rs:137]
else In Order
MuxDec-->>App: Iterator::next() -> Frame [core/src/frame/frame_mux_stream_decoder.rs:50]
end
Data Flow: Encoding to Decoding
The following diagram illustrates the lifecycle of data as it passes through the Frame Layer.
Sources: core/src/frame/frame_stream_encoder.rs:63-91 core/src/frame/frame_mux_stream_decoder.rs:70-157 core/src/frame/frame_codec.rs:20-128
5. Summary of Key Functions
| Component | Function | Role |
|---|---|---|
FrameCodec | encode | Serializes Frame to Vec<u8> core/src/frame/frame_codec.rs34 |
FrameCodec | decode | Deserializes &[u8] to DecodedFrame core/src/frame/frame_codec.rs68 |
FrameStreamEncoder | write_bytes | Accepts data, chunks it, and triggers emission core/src/frame/frame_stream_encoder.rs63 |
FrameStreamEncoder | end_stream | Emits a FrameKind::End to close the stream core/src/frame/frame_stream_encoder.rs122 |
FrameStreamEncoder | cancel_stream | Emits a FrameKind::Cancel for immediate termination core/src/frame/frame_stream_encoder.rs145 |
FrameMuxStreamDecoder | read_bytes | Ingests raw bytes and returns an iterator of ordered frames core/src/frame/frame_mux_stream_decoder.rs70 |
Sources: core/src/frame/frame_codec.rs:1-128 core/src/frame/frame_stream_encoder.rs:1-166 core/src/frame/frame_mux_stream_decoder.rs:1-158