moonbitstack/moonquic/recovery does not have a README file

    Control

    pub(open) trait Control {
    fn window(Self) -> Int64
    fn in_flight(Self) -> Int64
    fn on_sent(Self, Int64) -> Unit
    fn on_ack(Self, Int64) -> Unit
    fn on_lost(Self, Int64) -> Unit
    fn on_congestion(Self, at~ : Int64, now~ : Int64) -> Unit
    fn on_persistent(Self) -> Unit
    fn on_ack_at(Self, Int64, now~ : Int64) -> Unit = _
    fn on_rtt(Self, sample~ : Int64, now~ : Int64) -> Unit = _
    fn on_new_path(Self) -> Unit = _
    fn pacing_rate(Self) -> Double? = _
    }

    A congestion controller (RFC 9002 §7): how much may be in flight, and what to make of each packet sent, acknowledged or lost.

    NewReno is the one RFC 9002 specifies, and the reason this is a trait rather than that struct is §7's own opening — an endpoint may use any controller, and Cubic and BBR answer the same questions. Implementing this is all it takes; nothing here changes.

    Bbr

    pub struct Bbr {
    window : Int64
    in_flight : Int64
    btl_bw : Double
    min_rtt : Int64
    min_rtt_at : Int64
    mode : Mode
    delivered : Int64
    sample_at : Int64
    sample_delivered : Int64
    full_bw : Double
    full_bw_count : Int
    cycle : Int
    cycle_at : Int64
    probe_rtt_at : Int64
    probe_rtt_done : Bool
    policy : Policy
    }

    A BBR congestion controller, an implementation of Control.
    impl Control for Bbr

    Bbr::bandwidth

    fn Bbr::bandwidth(self : Bbr) -> Double

    The maximum delivery rate seen, in bytes per microsecond.

    Bbr::bdp

    fn Bbr::bdp(self : Bbr) -> Int64

    The bandwidth-delay product in bytes: how much the path can hold in flight at once at the best rate and the smallest round trip seen.

    Bbr::in_flight

    fn Bbr::in_flight(self : Bbr) -> Int64

    Bbr::min_rtt

    fn Bbr::min_rtt(self : Bbr) -> Int64

    The smallest round trip seen, in microseconds.

    Bbr::minimum

    fn Bbr::minimum(self : Bbr) -> Int64

    The smallest window the controller will set (RFC 9002 §7.2).

    Bbr::mode

    fn Bbr::mode(self : Bbr) -> Mode

    Where the controller is in its probing cycle.

    Bbr::new

    fn Bbr::new(policy? : Policy) -> Bbr

    A fresh BBR controller at RFC 9002 §7.2's initial window, in startup.

    Bbr::on_ack

    fn Bbr::on_ack(self : Bbr, size : Int64) -> Unit

    Bbr::on_ack_at

    fn Bbr::on_ack_at(self : Bbr, size : Int64, now~ : Int64) -> Unit

    Bbr::on_congestion

    fn Bbr::on_congestion(self : Bbr, at~ : Int64, now~ : Int64) -> Unit

    Bbr::on_lost

    fn Bbr::on_lost(self : Bbr, size : Int64) -> Unit

    Bbr::on_new_path

    fn Bbr::on_new_path(self : Bbr) -> Unit

    The defaulted methods of Control this controller inherits, named so the promotion to methods on Bbr is explicit rather than implied by the impl.

    Bbr::on_persistent

    fn Bbr::on_persistent(self : Bbr) -> Unit

    Bbr::on_rtt

    fn Bbr::on_rtt(self : Bbr, sample~ : Int64, now~ : Int64) -> Unit

    Bbr::on_sent

    fn Bbr::on_sent(self : Bbr, size : Int64) -> Unit

    Bbr::pacing_rate

    fn Bbr::pacing_rate(self : Bbr) -> Double?

    Bbr::target

    fn Bbr::target(self : Bbr) -> Int64

    The window the model asks for: the bandwidth-delay product times the mode's window gain, three datagrams of headroom, floored at the minimum. A loss leaves it unchanged, which is what tells BBR from NewReno. While the round trip is being sampled the window is held at four datagrams whatever the model says, so the queue the last cycle built can drain.

    Bbr::window

    fn Bbr::window(self : Bbr) -> Int64

    Cubic

    pub struct Cubic {
    window : Int64
    ssthresh : Int64
    in_flight : Int64
    recovery : Int64
    w_max : Int64
    epoch : Int64
    k : Double
    rtt : Int64
    policy : Policy
    }

    A CUBIC congestion controller (RFC 9438), an implementation of Control.
    impl Control for Cubic

    Cubic::in_flight

    fn Cubic::in_flight(self : Cubic) -> Int64

    Cubic::minimum

    fn Cubic::minimum(self : Cubic) -> Int64

    The smallest window the controller will reduce to (RFC 9002 §7.2).

    Cubic::new

    fn Cubic::new(policy? : Policy) -> Cubic

    A fresh CUBIC controller at RFC 9002 §7.2's initial window: the same start as NewReno, since how a controller begins is not what makes it CUBIC.

    Cubic::on_ack

    fn Cubic::on_ack(self : Cubic, size : Int64) -> Unit

    Cubic::on_ack_at

    fn Cubic::on_ack_at(self : Cubic, size : Int64, now~ : Int64) -> Unit

    Cubic::on_congestion

    fn Cubic::on_congestion(self : Cubic, at~ : Int64, now~ : Int64) -> Unit

    Cubic::on_lost

    fn Cubic::on_lost(self : Cubic, size : Int64) -> Unit

    Cubic::on_new_path

    fn Cubic::on_new_path(self : Cubic) -> Unit

    The defaulted methods of Control this controller inherits, named so the promotion to methods on Cubic is explicit rather than implied by the impl.

    Cubic::on_persistent

    fn Cubic::on_persistent(self : Cubic) -> Unit

    Cubic::on_rtt

    fn Cubic::on_rtt(self : Cubic, sample~ : Int64, now~ : Int64) -> Unit

    Cubic::on_sent

    fn Cubic::on_sent(self : Cubic, size : Int64) -> Unit

    Cubic::pacing_rate

    fn Cubic::pacing_rate(_ : Cubic) -> Double?

    Cubic::recovering

    fn Cubic::recovering(self : Cubic, at : Int64) -> Bool

    Whether a packet sent at at falls inside the recovery period already under way.

    Cubic::ssthresh

    fn Cubic::ssthresh(self : Cubic) -> Int64

    The slow-start threshold in bytes.

    Cubic::target

    fn Cubic::target(self : Cubic, now : Int64) -> Int64

    The window the cubic and the Reno-friendly estimate ask for at now, in bytes — the larger of the two (RFC 9438 §4.2, §4.3). A test reproduces this arithmetic itself so a change to the curve is visible as a changed window.

    Cubic::window

    fn Cubic::window(self : Cubic) -> Int64

    Flight

    pub struct Flight {
    packets : Array[Sent]
    largest_acked : Int64
    }

    What is outstanding in one packet-number space, and the largest number acknowledged so far — -1 before any ACK has arrived.

    Flight::largest_acked

    fn Flight::largest_acked(self : Flight) -> Int64

    The largest packet number acknowledged, or -1 before any ACK.

    Flight::lost

    fn Flight::lost(self : Flight, now : Int64, packets~ : Int64, time~ : Int64) -> Array[Sent]

    Declare lost every outstanding packet that either sits at least packets behind the largest acknowledged (RFC 9002 §6.1.1) or was sent longer than time ago while a later packet has been acknowledged (§6.1.2), and stop tracking them.

    The records come back whole, sizes included, because the congestion controller has to take those bytes out of flight and only the caller knows which controller that is.

    Flight::new

    fn Flight::new() -> Flight

    A fresh flight, with nothing outstanding.

    Flight::on_ack

    fn Flight::on_ack(self : Flight, ranges : Array[(Int64, Int64)]) -> Int64

    Process an ACK's acknowledged ranges: drop every outstanding packet that falls in one, advance the largest acknowledged, and answer with the bytes taken out of flight.

    Flight::on_sent

    fn Flight::on_sent(self : Flight, pn : Int64, at~ : Int64, size~ : Int64) -> Unit

    Record that an ack-eliciting packet went out.

    Flight::outstanding

    fn Flight::outstanding(self : Flight) -> Array[Int64]

    The packet numbers still outstanding.

    Flight::time_of

    fn Flight::time_of(self : Flight, pn : Int64) -> Int64?

    When the outstanding packet numbered pn was sent, or None if it is not outstanding.

    Mode

    pub(all) enum Mode {
    Startup
    Drain
    ProbeBw
    ProbeRtt
    } derive(Eq,
    Debug
    )

    Where a BBR sender is in its bandwidth-probing cycle.

    Mode::equal

    fn Mode::equal(Mode, Mode) -> Bool

    Mode::not_equal

    fn Mode::not_equal(x : Mode, y : Mode) -> Bool

    Mode::to_repr

    NewReno

    pub struct NewReno {
    window : Int64
    ssthresh : Int64
    in_flight : Int64
    recovery : Int64
    policy : Policy
    }

    NewReno, the controller RFC 9002 §7 specifies.
    impl Control for NewReno

    NewReno::in_flight

    fn NewReno::in_flight(self : NewReno) -> Int64

    NewReno::minimum

    fn NewReno::minimum(self : NewReno) -> Int64

    The smallest window the controller will reduce to (RFC 9002 §7.2).

    NewReno::new

    fn NewReno::new(policy? : Policy) -> NewReno

    A fresh controller (RFC 9002 §7.2): the initial window is min(10·datagram, max(2·datagram, 14720)), the slow-start threshold is unbounded, and nothing is in flight or in recovery.

    NewReno::on_ack

    fn NewReno::on_ack(self : NewReno, size : Int64) -> Unit

    NewReno::on_ack_at

    fn NewReno::on_ack_at(self : NewReno, size : Int64, now~ : Int64) -> Unit

    The defaulted methods of Control this controller inherits, named so the promotion to methods on NewReno is explicit rather than implied by the impl.

    NewReno::on_congestion

    fn NewReno::on_congestion(self : NewReno, at~ : Int64, now~ : Int64) -> Unit

    NewReno::on_lost

    fn NewReno::on_lost(self : NewReno, size : Int64) -> Unit

    NewReno::on_new_path

    fn NewReno::on_new_path(self : NewReno) -> Unit

    The defaulted methods of Control this controller inherits, named so the promotion to methods on NewReno is explicit rather than implied by the impl.

    NewReno::on_persistent

    fn NewReno::on_persistent(self : NewReno) -> Unit

    NewReno::on_rtt

    fn NewReno::on_rtt(_ : NewReno, sample~ : Int64, now~ : Int64) -> Unit

    The defaulted methods of Control this controller inherits, named so the promotion to methods on NewReno is explicit rather than implied by the impl.

    NewReno::on_sent

    fn NewReno::on_sent(self : NewReno, size : Int64) -> Unit

    NewReno::pacing_rate

    fn NewReno::pacing_rate(_ : NewReno) -> Double?

    NewReno::recovering

    fn NewReno::recovering(self : NewReno, at : Int64) -> Bool

    Whether a packet sent at at falls inside the recovery period already under way.

    NewReno::ssthresh

    fn NewReno::ssthresh(self : NewReno) -> Int64

    The slow-start threshold in bytes.

    NewReno::window

    fn NewReno::window(self : NewReno) -> Int64

    Pacer

    pub struct Pacer {
    // private fields
    }

    A token bucket over bytes, at a rate in bytes per microsecond. A rate of zero means the connection is not paced and everything is allowed.

    Pacer::allow

    fn Pacer::allow(self : Pacer, now : Int64, bytes : Int64) -> Bool

    Whether bytes may go at now. Unpaced, always.

    Pacer::new

    fn Pacer::new(burst_bytes? : Int64) -> Pacer

    A pacer that lets everything through until a rate is set.

    Pacer::next_at

    fn Pacer::next_at(self : Pacer, now : Int64, bytes : Int64) -> Int64?

    When bytes will have accrued, or None when it already has or nothing is paced. A driver arms a timer on this and comes back for the packet.

    Pacer::rate

    fn Pacer::rate(self : Pacer) -> Double

    The rate in bytes per microsecond; zero when unpaced.

    Pacer::set_rate

    fn Pacer::set_rate(self : Pacer, rate : Double) -> Unit

    Set the rate, in bytes per microsecond. Zero turns pacing off. The bucket starts full, so the first burst after a quiet period is not delayed.

    Pacer::spend

    fn Pacer::spend(self : Pacer, now : Int64, bytes : Int64) -> Unit

    Take bytes out of the bucket.

    Policy

    pub(all) struct Policy {
    packets : Int64
    time : Double
    granularity : Int64
    initial_rtt : Int64
    persistent : Int64
    reduction : Double
    datagram : Int64
    ack_delay : Int64
    }

    The constants RFC 9002 leaves as tunables, and the two connection parameters the timers are computed from. Every duration is microseconds and every size is bytes.

    The presets are the values the RFC itself recommends (§6.1.1, §6.1.2, §6.2.2, §7.2, §7.3, §7.6) together with RFC 9000 §18.2's default max_ack_delay and RFC 9000 §14's minimum datagram size, which is the one every path is required to carry.

    Policy::new

    fn Policy::new(packets? : Int64, time? : Double, granularity? : Int64, initial_rtt? : Int64, persistent? : Int64, reduction? : Double, datagram? : Int64, ack_delay? : Int64) -> Policy

    A policy by name, each part defaulting to RFC 9002's recommendation.

    packets is kPacketThreshold, time kTimeThreshold, granularity kGranularity, initial_rtt kInitialRtt, persistent kPersistentCongestionThreshold, reduction kLossReductionFactor. datagram is the sender's maximum datagram size and ack_delay the peer's advertised max_ack_delay, both of which a real connection learns from its transport parameters rather than keeps at the default.

    Rtt

    pub struct Rtt {
    latest : Int64
    min : Int64
    smoothed : Int64
    variation : Int64
    sampled : Bool
    }

    An endpoint's round-trip estimate for one packet-number space (RFC 9002 §5).

    Rtt::latest

    fn Rtt::latest(self : Rtt) -> Int64

    The most recent sample.

    Rtt::min

    fn Rtt::min(self : Rtt) -> Int64

    The smallest sample seen, which is the closest this endpoint has come to measuring the path itself rather than the path plus the peer's delays.

    Rtt::new

    fn Rtt::new() -> Rtt

    A fresh estimate, with no sample yet.

    Rtt::pto

    fn Rtt::pto(self : Rtt, policy? : Policy) -> Int64

    The Probe Timeout: smoothed + max(4·variation, granularity) + ack_delay (RFC 9002 §6.2.1), or twice the policy's initial RTT before the first sample (§6.2.2).

    Rtt::reset

    fn Rtt::reset(self : Rtt) -> Unit

    Back to no sample, for a path nothing is known about.

    Rtt::sample

    fn Rtt::sample(self : Rtt, latest : Int64, ack_delay~ : Int64, policy? : Policy) -> Unit

    Fold in a round-trip sample (RFC 9002 §5.3).

    ack_delay is what the peer said it waited before acknowledging; it is capped at the policy's ack_delay and subtracted only while doing so keeps the sample at or above the minimum seen, so a peer cannot talk the estimate below the path's real latency. The first sample seeds the estimate outright; later ones move the weighted average.

    Rtt::sampled

    fn Rtt::sampled(self : Rtt) -> Bool

    Whether any sample has arrived. Before the first one the timers fall back to the policy's initial RTT, which is the whole of what an endpoint knows at that point.

    Rtt::smoothed

    fn Rtt::smoothed(self : Rtt) -> Int64

    The smoothed estimate the timers are built on.

    Rtt::threshold

    fn Rtt::threshold(self : Rtt, policy? : Policy) -> Int64

    The loss time threshold: max(time · max(smoothed, latest), granularity) (RFC 9002 §6.1.2).

    Rtt::variation

    fn Rtt::variation(self : Rtt) -> Int64

    The mean deviation of the samples.

    Sent

    pub(all) struct Sent {
    pn : Int64
    at : Int64
    size : Int64
    } derive(Eq,
    Debug
    )

    A sent, not yet acknowledged ack-eliciting packet (RFC 9002 §A.1): its number, when it went out, and how many bytes it put in flight.

    Sent::equal

    fn Sent::equal(Sent, Sent) -> Bool

    Sent::not_equal

    fn Sent::not_equal(x : Sent, y : Sent) -> Bool

    Sent::to_repr

    State

    pub struct State {
    rtt : Rtt
    flight : Flight
    control : &Control
    policy : Policy
    pto_count : Int
    last_sent : Int64
    }

    A sender's recovery state for one packet-number space: the round-trip estimate, what is outstanding, and the congestion controller, driven by the two events a sender has — a packet went out, an ACK came back.

    State::can_send

    fn State::can_send(self : State, bytes : Int64) -> Bool

    Whether bytes more may go out without exceeding the congestion window.

    State::in_flight

    fn State::in_flight(self : State) -> Int64

    The bytes currently in flight.

    State::new

    fn State::new(policy? : Policy, control? : &Control) -> State

    A fresh state. Leave control out for NewReno under the same policy; give one to use another controller.

    State::on_ack

    fn State::on_ack(self : State, frame :
    Frame
    , now : Int64, ack_delay~ : Int64) -> Array[Int64] raise
    Refused

    An ACK arrived (RFC 9002 §5–§7): sample the round trip off the largest newly acknowledged packet, free the acknowledged bytes, then run loss detection over both thresholds. Answers with the packet numbers declared lost — the frames to send again.

    ack_delay is what the ACK frame said, in microseconds; the frame's own field is in the peer's exponent and decoding it is the connection's job, not this one's.

    One round of loss is one congestion signal however many packets it covers, and a round spanning more than persistent probe timeouts is persistent congestion (§7.6).

    State::on_new_path

    fn State::on_new_path(self : State) -> Unit

    The connection moved to a new path (RFC 9000 §9.4): the round trip and the window start over. What is in flight stays tracked, since those packets can still be acknowledged.

    State::on_pto

    fn State::on_pto(self : State) -> Unit

    The probe timer fired (RFC 9002 §6.2.4): back off for the next arming. Sending the probes is the caller's, because what to put in them is the connection's business.

    State::on_sent

    fn State::on_sent(self : State, pn : Int64, at~ : Int64, size~ : Int64) -> Unit

    An ack-eliciting packet went out: track it for acknowledgement and charge the window.

    State::outstanding

    fn State::outstanding(self : State) -> Array[Int64]

    The packet numbers still outstanding.

    State::pacing_rate

    fn State::pacing_rate(self : State) -> Double

    The rate to pace at, in bytes per microsecond (RFC 9002 §7.7): what the controller says when it models the path, else the window over one and a quarter round trips, which is what keeps a window from arriving as a burst.

    State::pto

    fn State::pto(self : State) -> Int64

    The probe timeout in microseconds, before backoff.

    State::pto_count

    fn State::pto_count(self : State) -> Int

    How many probe timeouts have fired without an acknowledgement since the last one that did — the backoff exponent.

    State::pto_deadline

    fn State::pto_deadline(self : State) -> Int64?

    When the probe timer fires: the last ack-eliciting packet's send time plus the timeout backed off by 2^pto_count (RFC 9002 §6.2.1). None when nothing is outstanding, which is the timer disarmed.

    State::rtt

    fn State::rtt(self : State) -> Rtt

    The round-trip estimate this space has built.

    State::window

    fn State::window(self : State) -> Int64

    The congestion window in bytes.

    can_send

    fn can_send(control : &Control, bytes : Int64) -> Bool

    Whether bytes more fit in the window.

    policy

    let policy : Policy

    RFC 9002's recommended constants.