# flare.quic.frame

> A module of flare_mlake's API.

QUIC v1 transport-frame codec (RFC 9000 §19).

This module is the canonical sans-I/O codec for the 22 transport
frame types QUIC v1 carries inside (deprotected) packet payloads.
The parser walks one frame at a time and dispatches each decoded
payload through a typed callback on a caller-supplied
:trait:`FrameHandler`; the QUIC connection state machine implements
the handler and advances per-frame state without ever materialising
an intermediate carrier value.

Frame types covered (RFC 9000 §19, all 22):

* §19.1 PADDING (0x00)
* §19.2 PING (0x01)
* §19.3 ACK (0x02), ACK with ECN (0x03)
* §19.4 RESET_STREAM (0x04)
* §19.5 STOP_SENDING (0x05)
* §19.6 CRYPTO (0x06)
* §19.7 NEW_TOKEN (0x07)
* §19.8 STREAM (0x08..0x0f -- 8 sub-shapes via OFF/LEN/FIN bits)
* §19.9 MAX_DATA (0x10)
* §19.10 MAX_STREAM_DATA (0x11)
* §19.11 MAX_STREAMS (bidirectional 0x12, unidirectional 0x13)
* §19.12 DATA_BLOCKED (0x14)
* §19.13 STREAM_DATA_BLOCKED (0x15)
* §19.14 STREAMS_BLOCKED (bidirectional 0x16, unidirectional 0x17)
* §19.15 NEW_CONNECTION_ID (0x18)
* §19.16 RETIRE_CONNECTION_ID (0x19)
* §19.17 PATH_CHALLENGE (0x1a)
* §19.18 PATH_RESPONSE (0x1b)
* §19.19 CONNECTION_CLOSE (transport 0x1c, application 0x1d)
* §19.20 HANDSHAKE_DONE (0x1e)

Dispatch contract
-----------------

:func:`parse_frame_into` reads exactly one frame at the start of
the supplied :class:`Span[UInt8, _]`, fires the matching typed
callback on the caller's handler, and returns the number of wire
bytes consumed. The caller advances its cursor and re-invokes the
dispatcher on the remainder until the buffer drains or a parse
error fires.

Every typed payload struct is ``Copyable`` (which implies ``Movable``)
so the handler can stash the dispatched value (or move it into a
queue) without lifetime gymnastics.

Sans-I/O contract
-----------------

This file holds zero I/O imports. It is registered in
``tests/tools/check_sans_io.sh`` so the contract is lint-enforced.

References
----------

* RFC 9000 §19 "Frame Types and Formats".
* RFC 9000 §16 "Variable-Length Integer Encoding" (varint).

## Traits

### `FrameHandler`

```mojo
undefined
```

Per-type callback contract :func:`parse_frame_into` fires.

The dispatcher reads one wire frame at the start of the input
buffer and invokes the matching callback. Implementors carry
out the per-frame state-machine work (or stash the dispatched
payload for later use) without an intermediate carrier
allocation: each callback receives the already-typed payload
by value.

The 20 callbacks below cover every RFC 9000 §19 frame type.
The ACK / ACK-ECN split is collapsed into :meth:`on_ack` (the
handler reads ``len(ack.ecn)`` to discriminate); the
MAX_STREAMS / STREAMS_BLOCKED bidi/uni splits are collapsed
via the ``unidirectional`` flag on the typed payload; the
CONNECTION_CLOSE transport/application split is collapsed via
the ``application`` flag.

A frame type outside the v1 master table never reaches a
callback: :func:`parse_frame_into` raises a
``FRAME_ENCODING_ERROR`` for it (RFC 9000 §12.4).

Implements: `Movable`

#### `on_padding`

```mojo
def on_padding(mut self, count: Int)
```

PADDING run consumed (§19.1).

``count`` is the number of consecutive 0x00 bytes the
parser collapsed into one callback (≥ 1).

#### `on_ping`

```mojo
def on_ping(mut self)
```

PING frame (§19.2).

#### `on_ack`

```mojo
def on_ack(mut self, ack: AckFrame)
```

ACK / ACK-ECN frame (§19.3). When ``len(ack.ecn) == 1`` the wire type was 0x03 (ACK with ECN counts); otherwise 0x02.

#### `on_reset_stream`

```mojo
def on_reset_stream(mut self, rs: ResetStreamFrame)
```

RESET_STREAM (§19.4).

#### `on_stop_sending`

```mojo
def on_stop_sending(mut self, ss: StopSendingFrame)
```

STOP_SENDING (§19.5).

#### `on_crypto`

```mojo
def on_crypto(mut self, c: CryptoFrame)
```

CRYPTO (§19.6).

#### `on_new_token`

```mojo
def on_new_token(mut self, t: NewTokenFrame)
```

NEW_TOKEN (§19.7).

#### `on_stream`

```mojo
def on_stream(mut self, sf: StreamFrame)
```

STREAM (§19.8). The wire-type flag bits are surfaced via ``sf.offset`` (set / zero), the explicit data length (read when LEN was set) and ``sf.fin``.

#### `on_max_data`

```mojo
def on_max_data(mut self, m: MaxDataFrame)
```

MAX_DATA (§19.9).

#### `on_max_stream_data`

```mojo
def on_max_stream_data(mut self, m: MaxStreamDataFrame)
```

MAX_STREAM_DATA (§19.10).

#### `on_max_streams`

```mojo
def on_max_streams(mut self, m: MaxStreamsFrame)
```

MAX_STREAMS (§19.11). The bidi (0x12) vs uni (0x13) wire split is surfaced via ``m.unidirectional``.

#### `on_data_blocked`

```mojo
def on_data_blocked(mut self, db: DataBlockedFrame)
```

DATA_BLOCKED (§19.12).

#### `on_stream_data_blocked`

```mojo
def on_stream_data_blocked(mut self, sdb: StreamDataBlockedFrame)
```

STREAM_DATA_BLOCKED (§19.13).

#### `on_streams_blocked`

```mojo
def on_streams_blocked(mut self, sb: StreamsBlockedFrame)
```

STREAMS_BLOCKED (§19.14). The bidi (0x16) vs uni (0x17) wire split is surfaced via ``sb.unidirectional``.

#### `on_new_connection_id`

```mojo
def on_new_connection_id(mut self, ncid: NewConnectionIdFrame)
```

NEW_CONNECTION_ID (§19.15).

#### `on_retire_connection_id`

```mojo
def on_retire_connection_id(mut self, rcid: RetireConnectionIdFrame)
```

RETIRE_CONNECTION_ID (§19.16).

#### `on_path_challenge`

```mojo
def on_path_challenge(mut self, pc: PathChallengeFrame)
```

PATH_CHALLENGE (§19.17).

#### `on_path_response`

```mojo
def on_path_response(mut self, pr: PathResponseFrame)
```

PATH_RESPONSE (§19.18).

#### `on_connection_close`

```mojo
def on_connection_close(mut self, cc: ConnectionCloseFrame)
```

CONNECTION_CLOSE (§19.19). The transport (0x1c) vs application (0x1d) wire split is surfaced via ``cc.application``.

#### `on_handshake_done`

```mojo
def on_handshake_done(mut self)
```

HANDSHAKE_DONE (§19.20).

#### `on_datagram`

```mojo
def on_datagram(mut self, dg: DatagramFrame)
```

DATAGRAM (RFC 9221 §4) -- an unreliable application datagram. ``dg.has_length`` records whether the wire type carried an explicit length (0x31) or ran to the end of the packet (0x30).

#### `on_ack_frequency`

```mojo
def on_ack_frequency(mut self, f: AckFrequencyFrame)
```

ACK_FREQUENCY (draft-ietf-quic-ack-frequency).

#### `on_immediate_ack`

```mojo
def on_immediate_ack(mut self)
```

IMMEDIATE_ACK (draft-ietf-quic-ack-frequency): acknowledge now.

## Functions

### `encode_datagram`

```mojo
def encode_datagram(frame: DatagramFrame, mut out: List[UInt8])
```

Encode a DATAGRAM frame (RFC 9221 §4).

When ``frame.has_length`` is set, emit type 0x31 with an explicit
length varint so the frame may be followed by others in the packet;
otherwise emit type 0x30, which the caller MUST place last in the
packet (its payload runs to the end of the QUIC payload).

### `encode_padding`

```mojo
def encode_padding(length: Int, mut out: List[UInt8])
```

Encode ``length`` PADDING frames (§19.1) as repeated 0x00s.

### `encode_ping`

```mojo
def encode_ping(mut out: List[UInt8])
```

Encode a PING frame (§19.2): single 0x01 byte.

### `encode_ack`

```mojo
def encode_ack(frame: AckFrame, mut out: List[UInt8])
```

Encode an ACK / ACK-ECN frame (§19.3).

Picks the type byte based on whether ``frame.ecn`` is empty
(0x02) or carries a single :class:`EcnCounts` entry (0x03).

### `encode_reset_stream`

```mojo
def encode_reset_stream(frame: ResetStreamFrame, mut out: List[UInt8])
```

### `encode_stop_sending`

```mojo
def encode_stop_sending(frame: StopSendingFrame, mut out: List[UInt8])
```

### `encode_crypto`

```mojo
def encode_crypto(frame: CryptoFrame, mut out: List[UInt8])
```

### `encode_new_token`

```mojo
def encode_new_token(frame: NewTokenFrame, mut out: List[UInt8])
```

### `encode_stream`

```mojo
def encode_stream(frame: StreamFrame, mut out: List[UInt8], emit_length: Bool = True)
```

Encode a STREAM frame (§19.8).

``emit_length`` controls whether the LEN bit is set: producers
that emit a STREAM frame as the *last* frame of a packet may
omit the explicit length and let the frame extend to the
packet boundary. Most callers pass ``emit_length=True`` for
safe self-describing framing.

### `encode_max_data`

```mojo
def encode_max_data(frame: MaxDataFrame, mut out: List[UInt8])
```

### `encode_max_stream_data`

```mojo
def encode_max_stream_data(frame: MaxStreamDataFrame, mut out: List[UInt8])
```

### `encode_max_streams`

```mojo
def encode_max_streams(frame: MaxStreamsFrame, mut out: List[UInt8])
```

### `encode_data_blocked`

```mojo
def encode_data_blocked(frame: DataBlockedFrame, mut out: List[UInt8])
```

### `encode_stream_data_blocked`

```mojo
def encode_stream_data_blocked(frame: StreamDataBlockedFrame, mut out: List[UInt8])
```

### `encode_streams_blocked`

```mojo
def encode_streams_blocked(frame: StreamsBlockedFrame, mut out: List[UInt8])
```

### `encode_new_connection_id`

```mojo
def encode_new_connection_id(frame: NewConnectionIdFrame, mut out: List[UInt8])
```

### `encode_retire_connection_id`

```mojo
def encode_retire_connection_id(frame: RetireConnectionIdFrame, mut out: List[UInt8])
```

### `encode_path_challenge`

```mojo
def encode_path_challenge(frame: PathChallengeFrame, mut out: List[UInt8])
```

### `encode_path_response`

```mojo
def encode_path_response(frame: PathResponseFrame, mut out: List[UInt8])
```

### `encode_connection_close`

```mojo
def encode_connection_close(frame: ConnectionCloseFrame, mut out: List[UInt8])
```

### `encode_handshake_done`

```mojo
def encode_handshake_done(mut out: List[UInt8])
```

### `encode_ack_frequency`

```mojo
def encode_ack_frequency(f: AckFrequencyFrame, mut out: List[UInt8])
```

### `encode_immediate_ack`

```mojo
def encode_immediate_ack(mut out: List[UInt8])
```

### `parse_frame_into`

```mojo
def parse_frame_into[H: FrameHandler](buf: Span[UInt8], mut handler: H) -> Int
```

Parse a single transport frame at the start of ``buf`` and fire the matching :trait:`FrameHandler` callback.

The QUIC frame type is itself varint-encoded (§19); for the
22 codepoints defined in v1 the encoding is single-byte, but
the dispatcher reads it as a varint to stay forward-compatible
with extension types that may register higher-numbered
codepoints. Codepoints outside the v1 master table are a
connection error of type FRAME_ENCODING_ERROR (RFC 9000 §12.4):
the dispatcher raises, so an unknown frame's body is never
re-read as further frames.

Returns the number of wire bytes consumed -- the caller
advances its cursor and re-invokes the dispatcher on the
remainder. The dispatcher never panics on malformed input;
structural errors (truncated payload, malformed varint, range
out of bounds) are reported via raised :class:`Error`.
