# flare.quic.cc

> A module of flare_mlake's API.

QUIC congestion control (RFC 9002 section 7): Reno + CUBIC (HyStart++).

Sans-I/O, sans-clock controllers: the caller feeds byte counts and the
monotonic millisecond clock; the controller answers "may I send another
``bytes`` right now?" via :meth:`can_send` and grows / shrinks the
congestion window on ACK / loss. No socket, no timer -- the
:mod:`flare.quic._loss_recovery` bookkeeping and the client / server
send paths own those and consult a controller through the
:trait:`CongestionController` contract.

Two algorithms ship:

- :struct:`RenoController` -- the RFC 9002 section 7.3 NewReno reference
  (slow start + AIMD congestion avoidance). Simple, robust, the safe
  default.
- :struct:`CubicController` -- RFC 9438 CUBIC with the RFC 9406
  HyStart++ slow-start exit. Better throughput on high
  bandwidth-delay-product paths (the case HTTP/3 over WAN hits).

:struct:`CcChoice` selects between them. Both share the RFC 9002
constants below and the in-recovery guard (one window reduction per
round-trip, keyed on the lost packet's send time).

These controllers model the window only -- they do not pace
egress (no inter-packet send timer). The QUIC send path uses the window
as a gate (burst up to ``cwnd - bytes_in_flight``); add a pacer
(RFC 9002 section 7.7) only if a profile shows bursty loss. The upgrade
path is a ``next_send_time`` accessor here plus a timer in the reactor.

## Structs

### `CcChoice`

```mojo
struct CcChoice
```

Which congestion controller a connection uses.

``CcChoice.reno()`` / ``CcChoice.cubic()`` are the two values; the
QUIC send path reads :attr:`kind` to pick the controller. Kept a
tiny tagged value (not a Mojo enum) so it round-trips through config
structs and comparisons cheaply.

Implements: `Copyable`, `Movable`

Fields:

- `kind: Int`

#### `RENO`

```mojo
comptime RENO = Int(0)
```

#### `CUBIC`

```mojo
comptime CUBIC = Int(1)
```

#### `__eq__`

```mojo
def __eq__(self, other: Self) -> Bool
```

#### `__ne__`

```mojo
def __ne__(self, other: Self) -> Bool
```

#### `reno`

```mojo
def reno() -> Self
```

#### `cubic`

```mojo
def cubic() -> Self
```

### `RenoController`

```mojo
struct RenoController
```

RFC 9002 section 7.3 NewReno: slow start until ``cwnd`` reaches ``ssthresh``, then additive-increase / multiplicative-decrease.

Implements: `CongestionController`, `Copyable`, `Movable`

Fields:

- `cwnd: UInt64`
- `ssthresh: UInt64`
- `recovery_start_ms: UInt64`

#### `__init__`

```mojo
def __init__(out self)
```

#### `window`

```mojo
def window(self) -> UInt64
```

#### `can_send`

```mojo
def can_send(self, bytes_in_flight: UInt64, bytes: UInt64) -> Bool
```

#### `on_packet_acked`

```mojo
def on_packet_acked(mut self, bytes: UInt64, rtt_ms: UInt64, now_ms: UInt64)
```

#### `on_congestion_event`

```mojo
def on_congestion_event(mut self, sent_time_ms: UInt64, now_ms: UInt64)
```

### `CubicController`

```mojo
struct CubicController
```

RFC 9438 CUBIC with the RFC 9406 HyStart++ slow-start exit.

In slow start the window grows exponentially (like Reno) but
HyStart++ watches per-round minimum RTT: a sustained delay rise
means the path queue is filling, so it exits slow start before a
loss (setting ``ssthresh = cwnd``). In congestion avoidance the
window follows the CUBIC cubic curve around ``w_max`` (the window at
the last loss), with a Reno-friendly floor so it never underperforms
Reno on short-RTT paths.

Implements: `CongestionController`, `Copyable`, `Movable`

Fields:

- `cwnd: UInt64`
- `ssthresh: UInt64`
- `recovery_start_ms: UInt64`
- `w_max: Float64`
- `epoch_start_ms: UInt64`
- `k: Float64`

#### `__init__`

```mojo
def __init__(out self)
```

#### `window`

```mojo
def window(self) -> UInt64
```

#### `can_send`

```mojo
def can_send(self, bytes_in_flight: UInt64, bytes: UInt64) -> Bool
```

#### `on_packet_acked`

```mojo
def on_packet_acked(mut self, bytes: UInt64, rtt_ms: UInt64, now_ms: UInt64)
```

#### `on_congestion_event`

```mojo
def on_congestion_event(mut self, sent_time_ms: UInt64, now_ms: UInt64)
```

## Traits

### `CongestionController`

```mojo
undefined
```

The window-management contract the QUIC send path consults.

A controller owns the congestion window (in bytes) and updates it on
ACK / loss. It never touches a socket or a clock; the caller passes
byte counts, RTT samples (ms), and the monotonic clock (ms).

Implements: `Copyable`, `Movable`

#### `window`

```mojo
def window(self) -> UInt64
```

Current congestion window in bytes.

#### `can_send`

```mojo
def can_send(self, bytes_in_flight: UInt64, bytes: UInt64) -> Bool
```

True if sending ``bytes`` more keeps in-flight <= window.

#### `on_packet_acked`

```mojo
def on_packet_acked(mut self, bytes: UInt64, rtt_ms: UInt64, now_ms: UInt64)
```

Grow the window for ``bytes`` newly acknowledged.

#### `on_congestion_event`

```mojo
def on_congestion_event(mut self, sent_time_ms: UInt64, now_ms: UInt64)
```

React to loss of a packet sent at ``sent_time_ms`` (one window reduction per round-trip).

## Aliases

### `MAX_DATAGRAM_SIZE`

```mojo
comptime MAX_DATAGRAM_SIZE = UInt64(1200)
```

Assumed max UDP payload (RFC 9002 kMaxDatagramSize); the window is an integer multiple of this in spirit, tracked in bytes.

### `INITIAL_WINDOW`

```mojo
comptime INITIAL_WINDOW = UInt64(12000)
```

RFC 9002 section 7.2 recommended initial window: ``min(10*MDS, max(2*MDS, 14720))`` == ``10*1200 == 12000`` here.

### `MINIMUM_WINDOW`

```mojo
comptime MINIMUM_WINDOW = UInt64(2400)
```

Floor the window never drops below (RFC 9002 kMinimumWindow).
