s2n_quic_core/recovery/
loss.rs1use crate::{packet::number::PacketNumber, time::Timestamp};
5use core::time::Duration;
6
7pub const K_PACKET_THRESHOLD: u64 = 3;
14
15#[derive(Debug, PartialEq, Eq)]
16pub enum Outcome {
17 NotLostYet { lost_time: Timestamp },
20 Lost,
22}
23
24pub fn detect(
27 time_threshold: Duration,
28 time_sent: Timestamp,
29 packet_number_threshold: u64,
30 packet_number: PacketNumber,
31 largest_acked_packet_number: PacketNumber,
32 now: Timestamp,
33) -> Outcome {
34 debug_assert!(
38 largest_acked_packet_number > packet_number,
39 "only packets sent before the largest acknowledged packet may be considered lost"
40 );
41
42 let packet_lost_time = time_sent + time_threshold;
45
46 let time_threshold_exceeded = packet_lost_time.has_elapsed(now);
48
49 let packet_number_threshold_exceeded = largest_acked_packet_number
50 .checked_distance(packet_number)
51 .expect("largest_acked_packet_number > packet_number")
52 >= packet_number_threshold;
53
54 if time_threshold_exceeded || packet_number_threshold_exceeded {
65 return Outcome::Lost;
66 }
67
68 Outcome::NotLostYet {
69 lost_time: packet_lost_time,
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use crate::{packet::number::PacketNumberSpace, time::testing::now};
77
78 #[allow(clippy::assertions_on_constants)]
90 #[test]
91 fn packet_reorder_threshold_at_least_three() {
92 assert!(K_PACKET_THRESHOLD >= 3);
93 }
94
95 #[test]
96 fn time_threshold() {
97 let time_threshold = Duration::from_secs(5);
98 let packet_number = new_packet_number(1);
99 let largest_acked_packet_number =
101 new_packet_number(packet_number.as_u64() + K_PACKET_THRESHOLD - 1);
102
103 let time_sent = now();
104 let current_time = time_sent + time_threshold;
105
106 let outcome = detect(
107 time_threshold,
108 time_sent,
109 K_PACKET_THRESHOLD,
110 packet_number,
111 largest_acked_packet_number,
112 current_time,
113 );
114
115 assert_eq!(Outcome::Lost, outcome);
116
117 let time_sent = now();
118 let current_time = time_sent + time_threshold - Duration::from_secs(1);
119
120 let outcome = detect(
121 time_threshold,
122 time_sent,
123 K_PACKET_THRESHOLD,
124 packet_number,
125 largest_acked_packet_number,
126 current_time,
127 );
128
129 assert_eq!(
130 Outcome::NotLostYet {
131 lost_time: current_time + Duration::from_secs(1)
132 },
133 outcome
134 );
135 }
136
137 #[test]
138 fn packet_number_threshold() {
139 let time_threshold = Duration::from_secs(5);
140 let time_sent = now();
141 let current_time = time_sent + time_threshold - Duration::from_secs(1);
143
144 let packet_number = new_packet_number(1);
145 let largest_acked_packet_number =
147 new_packet_number(packet_number.as_u64() + K_PACKET_THRESHOLD);
148
149 let outcome = detect(
150 time_threshold,
151 time_sent,
152 K_PACKET_THRESHOLD,
153 packet_number,
154 largest_acked_packet_number,
155 current_time,
156 );
157
158 assert_eq!(Outcome::Lost, outcome);
159
160 let largest_acked_packet_number =
162 new_packet_number(packet_number.as_u64() + K_PACKET_THRESHOLD - 1);
163
164 let outcome = detect(
165 time_threshold,
166 time_sent,
167 K_PACKET_THRESHOLD,
168 packet_number,
169 largest_acked_packet_number,
170 current_time,
171 );
172
173 assert_eq!(
174 Outcome::NotLostYet {
175 lost_time: current_time + Duration::from_secs(1)
176 },
177 outcome
178 );
179 }
180
181 fn new_packet_number(packet_number: u64) -> PacketNumber {
182 PacketNumberSpace::ApplicationData.new_packet_number(packet_number.try_into().unwrap())
183 }
184}