nx_quic_recovery.nx source
↩ module page · 57 lines · 2916 B
1// nx_quic_recovery.nx -- RUNG 7 of the sovereign QUIC transport: loss detection + congestion control
2// (RFC 9002). RTT estimation (sec 5), packet/time-threshold loss detection (sec 6), and NewReno
3// congestion control (sec 7) -- the machinery that decides WHEN a datagram is lost and HOW FAST to send,
4// which is exactly where FEC vs ARQ diverges on the lossy Belarus<->Texas link. All integer (microseconds,
5// bytes) -- no float, the Nishi way. license_tier: ORIGINAL
6import "nx_syscalls.nx"
7
8// ---- RTT estimation (RFC 9002 sec 5). state st[]: [smoothed_rtt, rttvar, min_rtt, has_sample] (microseconds)
9func quic_rtt_init(st: *i64) -> i64 { st[0]=0; st[1]=0; st[2]=0; st[3]=0; return 0 }
10func quic_rtt_sample(st: *i64, latest_rtt: i64, ack_delay: i64) -> i64 {
11 if st[3] == 0 { st[2] = latest_rtt } else { if latest_rtt < st[2] { st[2] = latest_rtt } }
12 if st[3] == 0 {
13 st[0] = latest_rtt // smoothed_rtt = first sample
14 st[1] = latest_rtt / 2 // rttvar = sample/2
15 st[3] = 1
16 return 0
17 }
18 var adjusted: i64 = latest_rtt
19 if latest_rtt >= st[2] + ack_delay { adjusted = latest_rtt - ack_delay }
20 var diff: i64 = st[0] - adjusted; if diff < 0 { diff = 0 - diff }
21 st[1] = (3 * st[1] + diff) / 4 // rttvar = 3/4 rttvar + 1/4 |smoothed-adjusted|
22 st[0] = (7 * st[0] + adjusted) / 8 // smoothed = 7/8 smoothed + 1/8 adjusted
23 return 0
24}
25
26// ---- NewReno congestion control (RFC 9002 sec 7). cc[]: [cwnd, ssthresh] (bytes)
27const QC_MAX_DATAGRAM: i64 = 1200
28const QC_INIT_CWND: i64 = 12000 // min(10*max, max(2*max, 14720)) = min(12000,14720) = 12000
29const QC_SSTHRESH_INF: i64 = 9223372036854775807
30func quic_cc_init(cc: *i64) -> i64 { cc[0] = QC_INIT_CWND; cc[1] = QC_SSTHRESH_INF; return 0 }
31func quic_cc_on_ack(cc: *i64, bytes_acked: i64) -> i64 {
32 if cc[0] < cc[1] { cc[0] = cc[0] + bytes_acked } // slow start
33 else { cc[0] = cc[0] + (QC_MAX_DATAGRAM * bytes_acked) / cc[0] } // congestion avoidance
34 return 0
35}
36func quic_cc_on_loss(cc: *i64) -> i64 {
37 cc[1] = cc[0] / 2 // ssthresh = cwnd * kLossReductionFactor(0.5)
38 cc[0] = cc[1]
39 let minw: i64 = 2 * QC_MAX_DATAGRAM // kMinimumWindow = 2 * max_datagram_size
40 if cc[0] < minw { cc[0] = minw }
41 return 0
42}
43
44// ---- loss detection (RFC 9002 sec 6)
45const QC_PKT_THRESHOLD: i64 = 3 // kPacketThreshold
46func quic_pkt_lost_by_threshold(pn: i64, largest_acked: i64) -> i64 {
47 if largest_acked - pn >= QC_PKT_THRESHOLD { return 1 }
48 return 0
49}
50// time-threshold loss_delay = 9/8 * max(smoothed_rtt, latest_rtt) (kTimeThreshold = 9/8)
51func quic_loss_delay(smoothed_rtt: i64, latest_rtt: i64) -> i64 {
52 var m: i64 = smoothed_rtt
53 if latest_rtt > m { m = latest_rtt }
54 return (9 * m) / 8
55}
56
57func main() -> i64 { return 0 }