Skip to documentation
SLOP

tiny.quic.connection.recovery

Reference tiny.quic connection recovery

Defined in connection.

API (24)

Actions

Public operations.

Types and contracts

Public types and contracts.

Values and defaults

Public values and defaults.

No direct callersNo direct callsconnectionrecovery
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Source: lib/quic/src/connection/recovery/resend.zig:8

zig
/// Marks off the stretch of a stream that a loss has put back in the queue. The sending half of/// stream 0 and each space's CRYPTO stream hold one, and the send path reads it to empty that/// stretch before it takes anything new. Holding a single stretch keeps the bookkeeping to two/// numbers whatever the pattern of loss. Two losses with a settled stretch between them therefore/// widen into one, and the bytes in the middle travel a second time.pub const LostRange = struct {    start: u62 = 0,    end: u62 = 0,    /// Says whether anything is waiting to travel a second time, so the send path chooses between    /// resending and reaching for new bytes.    pub fn isEmpty(self: LostRange) bool {        std.debug.assert(self.start <= self.end);        return self.start == self.end;    }    /// Counts the bytes waiting to travel a second time, so the send path sizes a resent frame    /// against it.    pub fn length(self: LostRange) u62 {        std.debug.assert(self.start <= self.end);        return self.end - self.start;    }    /// Widens the stretch until it reaches over `count` bytes from `offset`. The loss path calls it    /// with the stretch each lost frame covered. A call carrying no bytes leaves the stretch as it    /// was.    pub fn add(self: *LostRange, offset: u62, count: u62) void {        std.debug.assert(count <= std.math.maxInt(u62) - offset);        if (count == 0) return;        const end = offset + count;        if (self.isEmpty()) {            self.* = .{ .start = offset, .end = end };        } else {            self.start = @min(self.start, offset);            self.end = @max(self.end, end);        }        std.debug.assert(self.start <= offset);        std.debug.assert(end <= self.end);    }    /// Pulls in whichever end of the stretch the settled bytes reach, so an acknowledgment arriving    /// after the loss keeps those bytes from traveling a third time. Bytes settled in the middle    /// change nothing, because a single stretch has no way to hold a hole. A stretch pulled in from    /// both ends until it is bare returns to empty.    pub fn acknowledge(self: *LostRange, offset: u62, count: u62) void {        std.debug.assert(count <= std.math.maxInt(u62) - offset);        if (self.isEmpty()) return;        const waiting = self.length();        const end = offset + count;        if (offset <= self.start and self.start < end) self.start = @min(end, self.end);        if (offset < self.end and self.end <= end) self.end = @max(offset, self.start);        if (self.start == self.end) self.* = .{};        std.debug.assert(self.start <= self.end);        std.debug.assert(self.length() <= waiting);    }    /// Drops `count` bytes off the front. The send path calls it once a packet carries the resent    /// bytes. The count stays within what was waiting.    pub fn advance(self: *LostRange, count: u62) void {        std.debug.assert(count <= self.length());        self.start += count;        if (self.start == self.end) self.* = .{};        std.debug.assert(self.start <= self.end);    }};

Source: lib/quic/src/connection/recovery/rtt.zig:16

zig
/// Holds what one connection has learned about how long the path takes, so the loss and probe rules/// can read its fields once `Connection.roundTrip` hands one back. The struct keeps the most recent/// measurement, the smallest seen, a smoothed value, a measure of spread, and whether anything has/// been measured yet.pub const Estimator = struct {    latest_ns: u64 = 0,    minimum_ns: u64 = 0,    smoothed_ns: u64 = initial_ns,    variation_ns: u64 = initial_ns / 2,    sampled: bool = false,    /// Folds one fresh measurement into what the connection already knows for each ACK that settles    /// a packet the peer owed an answer to. The very first measurement seeds the smallest and the    /// smoothed value with itself, and the spread with half of itself. Each later measurement moves    /// the smoothed value by an eighth and the spread by a quarter, so a single odd measurement    /// cannot swing either far. The caller settles the peer's delay before it calls, so the figure    /// arriving here has already met the cap and been reduced to zero when the space demands it.    /// That delay comes off the measurement only while the answer stays at or above the smallest    /// seen. Each product and sum holds at the maximum once it reaches it.    pub fn update(self: *Estimator, latest_ns: u64, delay_ns: u64) void {        std.debug.assert(latest_ns != 0);        self.latest_ns = latest_ns;        if (!self.sampled) {            self.sampled = true;            self.minimum_ns = latest_ns;            self.smoothed_ns = latest_ns;            self.variation_ns = latest_ns / 2;            return;        }        self.minimum_ns = @min(self.minimum_ns, latest_ns);        const adjusted_ns = adjusted(latest_ns, self.minimum_ns, delay_ns);        const deviation_ns = difference(self.smoothed_ns, adjusted_ns);        self.variation_ns = (self.variation_ns *| 3 +| deviation_ns) / 4;        self.smoothed_ns = (self.smoothed_ns *| 7 +| adjusted_ns) / 8;    }};

Source: lib/quic/src/connection/recovery/loss.zig:39

zig
/// Works out the moment at which elapsed time will condemn a packet that went out at `sent_ns`, so/// the space keeps the answer as the deadline `nextTimeout` offers. The calculation adds the delay/// to the send time. A sum that runs over stops at the largest u64 value.pub fn deadlineNs(sent_ns: u64, delay_ns: u64) u64 {    return std.math.add(u64, sent_ns, delay_ns) catch std.math.maxInt(u64);}
Called byCallsNo direct callsconnection.recoveryexpiredtest sourcelib.quic.src.connection.recovery.loss.test_RF...2 an extreme estimate saturates the l...connection.recoverylossDeadlineNs
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/quic/src/connection/recovery/loss.zig:20

zig
/// Works out how long a packet is given before elapsed time condemns it, so the connection hands/// one delay to every space's detection pass. The calculation takes whichever of the two estimates/// is larger and stretches it by nine eighths. The answer stays at or above the one millisecond/// floor under the timing rules, the timer granularity. An estimate large enough to run the product/// over stops at the largest u64 value.pub fn delayNs(latest_ns: u64, smoothed_ns: u64) u64 {    const base = @max(latest_ns, smoothed_ns);    const scaled = @as(u128, base) * time_threshold_numerator / time_threshold_denominator;    const bounded = std.math.cast(u64, scaled) orelse std.math.maxInt(u64);    return @max(bounded, rtt.granularity_ns);}
Called byCallsNo direct callstest sourcelib.quic.src.connection.recovery.loss.test_RF...2 an extreme estimate saturates the l...test sourcelib.quic.src.connection.recovery.loss.test_RF...2 the loss delay is nine eighths of t...test sourcelib.quic.src.connection.recovery.loss.test_RF...2 the loss delay never falls below th...connection.recoverylossDelayNs
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/quic/src/connection/recovery/loss.zig:46

zig
/// Says whether elapsed time has already condemned a packet that went out at `sent_ns`, so the/// caller settles the time question outright and avoids comparing deadlines itself. The answer/// turns at the deadline itself, so a packet stands condemned from that moment on.pub fn expired(sent_ns: u64, now_ns: u64, delay_ns: u64) bool {    return now_ns >= deadlineNs(sent_ns, delay_ns);}
Called byCallstest sourcelib.quic.src.connection.recovery.loss.test_RF...2 an extreme estimate saturates the l...test sourcelib.quic.src.connection.recovery.loss.test_RF...2 the time threshold compares against...connection.recoverylossDeadlineNsconnection.recoveryexpired
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/quic/src/connection/recovery/loss.zig:7

zig
/// Three is how far under the largest acknowledged number a packet has to sit before reordering/// alone condemns it, so the constant fixes how many later packets have to be acknowledged before/// an earlier one is given up on. `reordered` measures that distance against it.pub const packet_threshold: u62 = 3;

Source: lib/quic/src/connection/recovery/loss.zig:31

zig
/// Says whether enough later packets have been acknowledged to condemn this one, so the detection/// pass runs this reordering test over each held packet. Three is enough to give up on a packet/// sitting that far under the largest acknowledged number. A number above the largest acknowledged/// one is safe.pub fn reordered(packet_number: u62, largest_acknowledged: u62) bool {    if (largest_acknowledged < packet_number) return false;    return largest_acknowledged - packet_number >= packet_threshold;}
Called byCallsNo direct callstest sourcelib.quic.src.connection.recovery.loss.test_RF...1 the packet threshold declares three...connection.recoveryreordered
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/quic/src/connection/recovery/loss.zig:13

zig
pub const time_threshold_denominator: u64 = 8;

Source: lib/quic/src/connection/recovery/loss.zig:12

zig
/// Nine eighths of a round trip is held as a numerator beside a denominator of eight, so the/// constant fixes how much of a round trip a packet is given before elapsed time alone condemns it./// `delayNs` scales the larger of the two estimates by that fraction.pub const time_threshold_numerator: u64 = 9;

Source: lib/quic/src/connection/recovery/pto.zig:7

zig
/// The wait may double sixteen times as probes go unanswered, and past that count the wait holds/// steady. Holding the wait there keeps the shift under the 64 bits the period is held in, which a/// compile-time check enforces.pub const backoff_max: u32 = 16;

Source: lib/quic/src/connection/recovery/pto.zig:39

zig
/// Adds the wait to the instant it is measured from so the connection keeps the answer as the probe/// deadline `nextTimeout` offers. A sum that runs over stops at the largest u64 value. The instant/// measured from is when the space last sent a packet the peer owes an answer to, or, with nothing/// outstanding, the anchor the anti-deadlock probe holds.pub fn deadlineNs(sent_ns: u64, period_ns: u64) u64 {    const deadline_ns = std.math.add(u64, sent_ns, period_ns) catch std.math.maxInt(u64);    std.debug.assert(deadline_ns >= sent_ns);    return deadline_ns;}
Called byCallsNo direct callstest sourcelib.quic.src.connection.recovery.pto.test_RFC...1 an extreme estimate saturates the p...connection.recoveryprobeDeadlineNs
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/quic/src/connection/recovery/pto.zig:25

zig
/// Works out how long to wait for an answer for one space, then doubles that wait once per/// unanswered probe up to the ceiling. The connection works out one wait per space and arms the/// soonest deadline they produce. The wait itself is the smoothed estimate, plus four times the/// variation, plus whatever delay the peer has told this endpoint to expect. The variation term is/// held at or above the one millisecond floor under the timing rules, the timer granularity. Only/// the application space passes a nonzero delay, and only after the handshake is confirmed, so/// every other caller passes zero. Each sum and each shift holds at the maximum once it reaches it.pub fn periodNs(smoothed_ns: u64, variation_ns: u64, max_ack_delay_ns: u64, count: u32) u64 {    const variance_ns = @max(variation_ns *| 4, rtt.granularity_ns);    const base_ns = smoothed_ns +| variance_ns +| max_ack_delay_ns;    std.debug.assert(base_ns >= rtt.granularity_ns);    const shift: u6 = @intCast(@min(count, backoff_max));    const period_ns = base_ns <<| shift;    std.debug.assert(period_ns >= base_ns);    return period_ns;}
Called byCallsNo direct callstest sourcelib.quic.src.connection.recovery.pto.test_RFC...1 an extreme estimate saturates the p...test sourcelib.quic.src.connection.recovery.pto.test_RFC...1 consecutive expiries double the per...test sourcelib.quic.src.connection.recovery.pto.test_RFC...1 the backoff stops at the pinned max...test sourcelib.quic.src.connection.recovery.pto.test_RFC...1 the period sums the estimate, four ...test sourcelib.quic.src.connection.recovery.pto.test_RFC...1 the variation term never falls belo...connection.recoveryprobePeriodNs
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/quic/src/connection/recovery/pto.zig:16

zig
/// Two packets, each one the peer owes an answer to, fix what the connection owes the peer when a/// probe deadline passes with packets still outstanding. With nothing outstanding the connection/// owes one.pub const probe_packets: u8 = 2;
Called byCallsNo direct callersconnection.recovery.LostRangeisEmptyconnection.recovery.LostRangelengthconnection.recovery.LostRangeacknowledge
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersconnection.recovery.LostRangeisEmptyconnection.recovery.LostRangeadd
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersconnection.recovery.LostRangelengthconnection.recovery.LostRangeadvance
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsconnection.recovery.LostRangeacknowledgeconnection.recovery.LostRangeaddconnection.recovery.LostRangeisEmpty
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callsconnection.recovery.LostRangeacknowledgeconnection.recovery.LostRangeadvanceconnection.recovery.LostRangelength
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callersprivate sourcelib.quic.src.connection.recovery.rttadjustedprivate sourcelib.quic.src.connection.recovery.rttdifferenceconnection.recovery.Estimatorupdate
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/quic/src/connection/recovery/rtt.zig:60

zig
/// Turns one ACK frame's delay field into nanoseconds, because an ACK frame carries its delay/// scaled by the sender's own exponent. The field counts microseconds shifted left by the peer's/// exponent. A figure too large for 64 bits stops at the largest u64 value.pub fn delayNs(encoded: u62, exponent: u5) u64 {    const microseconds = @as(u128, encoded) << exponent;    const nanoseconds = microseconds * std.time.ns_per_us;    return std.math.cast(u64, nanoseconds) orelse std.math.maxInt(u64);}
Called byCallsNo direct callsconnection.recoveryreportedDelayNstest sourcelib.quic.src.connection.recovery.rtt.test_RFC...3 the delay field scales by the peer ...connection.recoveryackDelayNs
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/quic/src/connection/recovery/rtt.zig:10

zig
/// A connection assumes the path costs 333 milliseconds until it measures, so the first probe/// deadline rests on this value before any acknowledgment arrives. A fresh estimator opens with/// that duration as its smoothed value and half of it as its variation.pub const initial_ns: u64 = 333 * std.time.ns_per_ms;

Source: lib/quic/src/connection/recovery/rtt.zig:71

zig
/// Works out how much of the gap the peer says it spent before answering. The call settles the two/// questions that decide whether an ACK's delay counts at all, so the caller hands the answer/// straight to `update`. Initial and Handshake acknowledgments carry no delay field, and the caller/// passes false to say so, so the call yields zero. A cap applies only after the handshake is/// confirmed, so the caller passes none before then.pub fn reportedDelayNs(reported: bool, encoded: u62, exponent: u5, maximum_ns: ?u64) u64 {    if (!reported) return 0;    const delay_ns = delayNs(encoded, exponent);    const maximum = maximum_ns orelse return delay_ns;    return @min(delay_ns, maximum);}
Called byCallstest sourcelib.quic.src.connection.recovery.rtt.test_RFC...3 Initial and Handshake acknowledgmen...test sourcelib.quic.src.connection.recovery.rtt.test_RFC...3 max ack delay caps the reported del...connection.recoveryackDelayNsconnection.recoveryreportedDelayNs
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/quic/src/connection/recovery/rtt.zig:52

zig
/// Measures the gap between a packet going out and its answer coming back, so the connection can/// turn one settled packet into a measurement. A clock that has not moved yields nothing to/// measure, and the call offers none.pub fn sample(sent_ns: u64, received_ns: u64) ?u64 {    if (received_ns <= sent_ns) return null;    return received_ns - sent_ns;}
Called byCallsNo direct callstest sourcelib.quic.src.connection.recovery.rtt.test_RFC...1 a sample needs the clock to advanceconnection.recoverysample
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/quic/src/connection/recovery/root.zig

zig
const loss = @import("loss.zig");const pto = @import("pto.zig");const resend = @import("resend.zig");const rtt = @import("rtt.zig");pub const granularity_ns = rtt.granularity_ns;pub const initial_rtt_ns = rtt.initial_ns;pub const Estimator = rtt.Estimator;pub const sample = rtt.sample;pub const ackDelayNs = rtt.delayNs;pub const reportedDelayNs = rtt.reportedDelayNs;pub const packet_threshold = loss.packet_threshold;pub const time_threshold_numerator = loss.time_threshold_numerator;pub const time_threshold_denominator = loss.time_threshold_denominator;pub const lossDelayNs = loss.delayNs;pub const reordered = loss.reordered;pub const lossDeadlineNs = loss.deadlineNs;pub const expired = loss.expired;pub const backoff_max = pto.backoff_max;pub const probe_packets = pto.probe_packets;pub const probePeriodNs = pto.periodNs;pub const probeDeadlineNs = pto.deadlineNs;pub const LostRange = resend.LostRange;

Source: lib/quic/src/connection/root.zig:12

zig
pub const recovery = @import("recovery/root.zig");

Source: lib/quic/src/connection/recovery/rtt.zig:5

zig
/// One millisecond establishes how fine the timing rules are willing to be, so the loss delay and/// the probe wait are each held at or above it.pub const granularity_ns: u64 = std.time.ns_per_ms;

Audit

Definitions25
Public names25
Members7
Version26.7.0
Revisiondaab053ee433