Skip to main content

ant_quic/constrained/
arq.rs

1// Copyright 2024 Saorsa Labs Ltd.
2//
3// This Saorsa Network Software is licensed under the General Public License (GPL), version 3.
4// Please see the file LICENSE-GPL, or visit <http://www.gnu.org/licenses/> for the full text.
5//
6// Full details available at https://saorsalabs.com/licenses
7
8//! ARQ (Automatic Repeat Request) reliability layer
9//!
10//! Provides reliable delivery over unreliable transports using:
11//! - Sliding window for flow control
12//! - Cumulative acknowledgments
13//! - Retransmission timeout (RTO) with exponential backoff
14//! - Sequence number wrap-around handling
15
16use super::types::SequenceNumber;
17use std::collections::VecDeque;
18use std::time::{Duration, Instant};
19
20/// Default window size (number of unacknowledged packets allowed)
21pub const DEFAULT_WINDOW_SIZE: u8 = 8;
22
23/// Default retransmission timeout
24pub const DEFAULT_RTO: Duration = Duration::from_secs(2);
25
26/// Maximum retransmission timeout (after backoff)
27pub const MAX_RTO: Duration = Duration::from_secs(30);
28
29/// Maximum retransmission attempts before giving up
30pub const DEFAULT_MAX_RETRIES: u32 = 5;
31
32/// Configuration for the ARQ layer
33#[derive(Debug, Clone)]
34pub struct ArqConfig {
35    /// Window size (number of unacknowledged packets)
36    pub window_size: u8,
37    /// Initial retransmission timeout
38    pub initial_rto: Duration,
39    /// Maximum retransmission timeout
40    pub max_rto: Duration,
41    /// Maximum retransmission attempts
42    pub max_retries: u32,
43}
44
45impl Default for ArqConfig {
46    fn default() -> Self {
47        Self {
48            window_size: DEFAULT_WINDOW_SIZE,
49            initial_rto: DEFAULT_RTO,
50            max_rto: MAX_RTO,
51            max_retries: DEFAULT_MAX_RETRIES,
52        }
53    }
54}
55
56impl ArqConfig {
57    /// Create config optimized for BLE transport
58    pub fn for_ble() -> Self {
59        Self {
60            window_size: 4, // Smaller window for slower transport
61            initial_rto: Duration::from_millis(1500),
62            max_rto: Duration::from_secs(15),
63            max_retries: 5,
64        }
65    }
66
67    /// Create config optimized for LoRa transport
68    pub fn for_lora() -> Self {
69        Self {
70            window_size: 2, // Very small window for very slow transport
71            initial_rto: Duration::from_secs(10),
72            max_rto: Duration::from_secs(60),
73            max_retries: 3,
74        }
75    }
76}
77
78/// Entry in the send window tracking an unacknowledged packet
79#[derive(Debug, Clone)]
80pub struct SendEntry {
81    /// Sequence number of this packet
82    pub seq: SequenceNumber,
83    /// Packet data (for retransmission)
84    pub data: Vec<u8>,
85    /// When the packet was first sent (used for RTT estimation)
86    #[allow(dead_code)]
87    first_sent: Instant,
88    /// When the packet was last sent (for retransmission)
89    last_sent: Instant,
90    /// Number of transmissions (1 = first time, 2+ = retransmissions)
91    pub transmissions: u32,
92}
93
94impl SendEntry {
95    /// Create a new send entry
96    pub fn new(seq: SequenceNumber, data: Vec<u8>) -> Self {
97        let now = Instant::now();
98        Self {
99            seq,
100            data,
101            first_sent: now,
102            last_sent: now,
103            transmissions: 1,
104        }
105    }
106
107    /// Time since last transmission
108    pub fn time_since_sent(&self) -> Duration {
109        self.last_sent.elapsed()
110    }
111
112    /// Total time since first transmission
113    #[allow(dead_code)]
114    pub fn total_time(&self) -> Duration {
115        self.first_sent.elapsed()
116    }
117
118    /// Mark as retransmitted
119    pub fn mark_retransmitted(&mut self) {
120        self.last_sent = Instant::now();
121        self.transmissions += 1;
122    }
123}
124
125/// Sliding window for send-side reliability
126#[derive(Debug)]
127pub struct SendWindow {
128    /// Configuration
129    config: ArqConfig,
130    /// Next sequence number to use for new packets
131    next_seq: SequenceNumber,
132    /// Oldest unacknowledged sequence number
133    base_seq: SequenceNumber,
134    /// Queue of unacknowledged packets
135    unacked: VecDeque<SendEntry>,
136    /// Current RTO (adaptive)
137    current_rto: Duration,
138    /// Smoothed RTT estimate
139    srtt: Option<Duration>,
140}
141
142impl SendWindow {
143    /// Create a new send window
144    pub fn new(config: ArqConfig) -> Self {
145        Self {
146            current_rto: config.initial_rto,
147            config,
148            next_seq: SequenceNumber::new(0),
149            base_seq: SequenceNumber::new(0),
150            unacked: VecDeque::new(),
151            srtt: None,
152        }
153    }
154
155    /// Create with default config
156    pub fn with_defaults() -> Self {
157        Self::new(ArqConfig::default())
158    }
159
160    /// Get next sequence number to use
161    pub fn next_seq(&self) -> SequenceNumber {
162        self.next_seq
163    }
164
165    /// Check if window has room for more packets
166    pub fn can_send(&self) -> bool {
167        self.unacked.len() < self.config.window_size as usize
168    }
169
170    /// Check if window is full
171    pub fn is_full(&self) -> bool {
172        !self.can_send()
173    }
174
175    /// Number of packets currently in flight
176    pub fn in_flight(&self) -> usize {
177        self.unacked.len()
178    }
179
180    /// Alias for in_flight() - number of unacked packets
181    pub fn len(&self) -> usize {
182        self.in_flight()
183    }
184
185    /// Check if no packets are in flight
186    pub fn is_empty(&self) -> bool {
187        self.unacked.is_empty()
188    }
189
190    /// Add a packet to the send window
191    ///
192    /// Returns the sequence number assigned to the packet, or None if window is full.
193    pub fn send(&mut self, data: Vec<u8>) -> Option<SequenceNumber> {
194        if !self.can_send() {
195            return None;
196        }
197
198        let seq = self.next_seq;
199        self.next_seq = self.next_seq.next();
200        self.unacked.push_back(SendEntry::new(seq, data));
201
202        Some(seq)
203    }
204
205    /// Add a packet with a specific sequence number
206    ///
207    /// Used when the caller manages sequence numbers.
208    /// Returns error if window is full.
209    pub fn add(
210        &mut self,
211        seq: SequenceNumber,
212        data: Vec<u8>,
213    ) -> Result<(), super::types::ConstrainedError> {
214        if self.is_full() {
215            return Err(super::types::ConstrainedError::SendBufferFull);
216        }
217
218        self.unacked.push_back(SendEntry::new(seq, data));
219        Ok(())
220    }
221
222    /// Process a cumulative ACK
223    ///
224    /// Acknowledges all packets up to and including the given sequence number.
225    /// Returns the number of packets acknowledged.
226    pub fn acknowledge(&mut self, ack: SequenceNumber) -> usize {
227        let mut count = 0;
228
229        // Remove all packets with seq <= ack
230        while let Some(entry) = self.unacked.front() {
231            let dist = self.base_seq.distance_to(entry.seq);
232            let ack_dist = self.base_seq.distance_to(ack);
233
234            if dist <= ack_dist {
235                // This packet is acknowledged
236                if let Some(entry) = self.unacked.pop_front() {
237                    // Update RTT estimate
238                    if entry.transmissions == 1 {
239                        // Only use samples from non-retransmitted packets
240                        self.update_rtt(entry.time_since_sent());
241                    }
242                    count += 1;
243                }
244            } else {
245                break;
246            }
247        }
248
249        // Update base sequence
250        if count > 0 {
251            self.base_seq = ack.next();
252        }
253
254        count
255    }
256
257    /// Update RTT estimate using simplified Jacobson algorithm
258    ///
259    /// Uses exponential moving average for SRTT without RTTVAR tracking.
260    fn update_rtt(&mut self, sample: Duration) {
261        const ALPHA: f64 = 0.125; // 1/8 smoothing factor
262
263        if let Some(srtt) = self.srtt {
264            let srtt_secs = srtt.as_secs_f64();
265            let sample_secs = sample.as_secs_f64();
266
267            // SRTT = (1 - alpha) * SRTT + alpha * R
268            let new_srtt = (1.0 - ALPHA) * srtt_secs + ALPHA * sample_secs;
269
270            // RTTVAR not tracked for simplicity, use simpler RTO = 2 * SRTT
271            let new_rto = (2.0 * new_srtt).clamp(
272                self.config.initial_rto.as_secs_f64(),
273                self.config.max_rto.as_secs_f64(),
274            );
275
276            self.srtt = Some(Duration::from_secs_f64(new_srtt));
277            self.current_rto = Duration::from_secs_f64(new_rto);
278        } else {
279            // First sample
280            self.srtt = Some(sample);
281            self.current_rto = sample * 2;
282        }
283    }
284
285    /// Get current RTO
286    pub fn rto(&self) -> Duration {
287        self.current_rto
288    }
289
290    /// Get packets that need retransmission
291    ///
292    /// Returns a list of packets that have exceeded RTO and haven't exceeded max retries.
293    /// Returns None if any packet has exceeded max retries (connection should fail).
294    pub fn get_retransmissions(&mut self) -> Option<Vec<(SequenceNumber, Vec<u8>)>> {
295        let rto = self.current_rto;
296        let max_retries = self.config.max_retries;
297        let mut retransmits = Vec::new();
298
299        for entry in &mut self.unacked {
300            if entry.time_since_sent() > rto {
301                if entry.transmissions > max_retries {
302                    // Max retries exceeded
303                    return None;
304                }
305                retransmits.push((entry.seq, entry.data.clone()));
306                entry.mark_retransmitted();
307            }
308        }
309
310        // Apply exponential backoff after retransmissions
311        if !retransmits.is_empty() {
312            self.current_rto = (self.current_rto * 2).min(self.config.max_rto);
313        }
314
315        Some(retransmits)
316    }
317
318    /// Reset the window (for connection close/reset)
319    pub fn reset(&mut self) {
320        self.next_seq = SequenceNumber::new(0);
321        self.base_seq = SequenceNumber::new(0);
322        self.unacked.clear();
323        self.current_rto = self.config.initial_rto;
324        self.srtt = None;
325    }
326}
327
328/// Sliding window for receive-side reliability
329#[derive(Debug)]
330pub struct ReceiveWindow {
331    /// Window size
332    window_size: u8,
333    /// Next expected sequence number
334    next_expected: SequenceNumber,
335    /// Highest cumulative ACK we can send
336    cumulative_ack: SequenceNumber,
337    /// Out-of-order received packets (seq -> data)
338    out_of_order: VecDeque<(SequenceNumber, Vec<u8>)>,
339}
340
341impl ReceiveWindow {
342    /// Create a new receive window
343    pub fn new(window_size: u8) -> Self {
344        Self {
345            window_size,
346            next_expected: SequenceNumber::new(0),
347            cumulative_ack: SequenceNumber::new(0),
348            out_of_order: VecDeque::new(),
349        }
350    }
351
352    /// Create with default window size
353    pub fn with_defaults() -> Self {
354        Self::new(DEFAULT_WINDOW_SIZE)
355    }
356
357    /// Get the cumulative ACK to send
358    pub fn cumulative_ack(&self) -> SequenceNumber {
359        self.cumulative_ack
360    }
361
362    /// Check if a sequence number is within the receive window
363    pub fn is_in_window(&self, seq: SequenceNumber) -> bool {
364        self.next_expected.is_in_window(seq, self.window_size)
365    }
366
367    /// Receive a packet
368    ///
369    /// Returns the data if packet is in-order, or None if out-of-order (buffered).
370    /// Also returns any subsequently buffered packets that are now in-order.
371    pub fn receive(
372        &mut self,
373        seq: SequenceNumber,
374        data: Vec<u8>,
375    ) -> Option<Vec<(SequenceNumber, Vec<u8>)>> {
376        // Check if in window
377        if !self.is_in_window(seq) {
378            // Duplicate or out of window, ignore but update ACK
379            return None;
380        }
381
382        if seq == self.next_expected {
383            // In-order packet
384            let mut deliverable = vec![(seq, data)];
385            self.next_expected = self.next_expected.next();
386            self.cumulative_ack = seq;
387
388            // Check for buffered packets that are now in-order
389            while let Some(entry_idx) = self
390                .out_of_order
391                .iter()
392                .position(|(s, _)| *s == self.next_expected)
393            {
394                if let Some((s, d)) = self.out_of_order.remove(entry_idx) {
395                    deliverable.push((s, d));
396                    self.next_expected = self.next_expected.next();
397                    self.cumulative_ack = s;
398                }
399            }
400
401            Some(deliverable)
402        } else {
403            // Out-of-order, buffer it if not duplicate
404            if !self.out_of_order.iter().any(|(s, _)| *s == seq) {
405                // Keep buffer sorted
406                let pos = self
407                    .out_of_order
408                    .iter()
409                    .position(|(s, _)| {
410                        self.next_expected.distance_to(*s) > self.next_expected.distance_to(seq)
411                    })
412                    .unwrap_or(self.out_of_order.len());
413                self.out_of_order.insert(pos, (seq, data));
414            }
415            None
416        }
417    }
418
419    /// Reset the window
420    pub fn reset(&mut self) {
421        self.next_expected = SequenceNumber::new(0);
422        self.cumulative_ack = SequenceNumber::new(0);
423        self.out_of_order.clear();
424    }
425
426    /// Reset the window with a starting sequence number
427    pub fn reset_with_seq(&mut self, start_seq: SequenceNumber) {
428        self.next_expected = start_seq;
429        self.cumulative_ack = start_seq;
430        self.out_of_order.clear();
431    }
432
433    /// Get count of buffered out-of-order packets
434    pub fn buffered_count(&self) -> usize {
435        self.out_of_order.len()
436    }
437}
438
439#[cfg(test)]
440mod tests {
441    use super::*;
442
443    #[test]
444    fn test_arq_config_defaults() {
445        let config = ArqConfig::default();
446        assert_eq!(config.window_size, DEFAULT_WINDOW_SIZE);
447        assert_eq!(config.initial_rto, DEFAULT_RTO);
448    }
449
450    #[test]
451    fn test_arq_config_ble() {
452        let config = ArqConfig::for_ble();
453        assert!(config.window_size < DEFAULT_WINDOW_SIZE);
454        assert!(config.initial_rto < DEFAULT_RTO);
455    }
456
457    #[test]
458    fn test_send_entry() {
459        let entry = SendEntry::new(SequenceNumber::new(5), b"test".to_vec());
460        assert_eq!(entry.seq, SequenceNumber::new(5));
461        assert_eq!(entry.transmissions, 1);
462        assert!(entry.time_since_sent() < Duration::from_secs(1));
463    }
464
465    #[test]
466    fn test_send_window_basic() {
467        let mut window = SendWindow::with_defaults();
468        assert!(window.can_send());
469        assert_eq!(window.in_flight(), 0);
470
471        // Send a packet
472        let seq = window.send(b"hello".to_vec()).unwrap();
473        assert_eq!(seq, SequenceNumber::new(0));
474        assert_eq!(window.in_flight(), 1);
475
476        // Acknowledge it
477        let acked = window.acknowledge(SequenceNumber::new(0));
478        assert_eq!(acked, 1);
479        assert_eq!(window.in_flight(), 0);
480    }
481
482    #[test]
483    fn test_send_window_full() {
484        let config = ArqConfig {
485            window_size: 2,
486            ..Default::default()
487        };
488        let mut window = SendWindow::new(config);
489
490        // Fill the window
491        assert!(window.send(b"1".to_vec()).is_some());
492        assert!(window.send(b"2".to_vec()).is_some());
493        assert!(!window.can_send());
494        assert!(window.send(b"3".to_vec()).is_none());
495    }
496
497    #[test]
498    fn test_send_window_cumulative_ack() {
499        let mut window = SendWindow::with_defaults();
500
501        // Send 3 packets
502        window.send(b"1".to_vec());
503        window.send(b"2".to_vec());
504        window.send(b"3".to_vec());
505        assert_eq!(window.in_flight(), 3);
506
507        // ACK up to seq 1 acknowledges seq 0 and 1
508        let acked = window.acknowledge(SequenceNumber::new(1));
509        assert_eq!(acked, 2);
510        assert_eq!(window.in_flight(), 1);
511    }
512
513    #[test]
514    fn test_receive_window_in_order() {
515        let mut window = ReceiveWindow::with_defaults();
516
517        // Receive in order
518        let result = window.receive(SequenceNumber::new(0), b"first".to_vec());
519        assert!(result.is_some());
520        let packets = result.unwrap();
521        assert_eq!(packets.len(), 1);
522        assert_eq!(packets[0].1, b"first");
523
524        assert_eq!(window.cumulative_ack(), SequenceNumber::new(0));
525    }
526
527    #[test]
528    fn test_receive_window_out_of_order() {
529        let mut window = ReceiveWindow::with_defaults();
530
531        // Receive seq 1 first (out of order)
532        let result = window.receive(SequenceNumber::new(1), b"second".to_vec());
533        assert!(result.is_none());
534        assert_eq!(window.buffered_count(), 1);
535
536        // Now receive seq 0
537        let result = window.receive(SequenceNumber::new(0), b"first".to_vec());
538        assert!(result.is_some());
539        let packets = result.unwrap();
540        assert_eq!(packets.len(), 2);
541        assert_eq!(packets[0].1, b"first");
542        assert_eq!(packets[1].1, b"second");
543
544        assert_eq!(window.cumulative_ack(), SequenceNumber::new(1));
545        assert_eq!(window.buffered_count(), 0);
546    }
547
548    #[test]
549    fn test_receive_window_duplicate() {
550        let mut window = ReceiveWindow::with_defaults();
551
552        // Receive seq 0
553        window.receive(SequenceNumber::new(0), b"first".to_vec());
554
555        // Receive seq 0 again (duplicate)
556        let result = window.receive(SequenceNumber::new(0), b"first".to_vec());
557        assert!(result.is_none());
558    }
559
560    #[test]
561    fn test_receive_window_out_of_window() {
562        let config = ArqConfig {
563            window_size: 4,
564            ..Default::default()
565        };
566        let mut window = ReceiveWindow::new(config.window_size);
567
568        // Try to receive seq 10 when expecting 0 (out of window)
569        let result = window.receive(SequenceNumber::new(10), b"data".to_vec());
570        assert!(result.is_none());
571        assert_eq!(window.buffered_count(), 0);
572    }
573
574    #[test]
575    fn test_send_window_reset() {
576        let mut window = SendWindow::with_defaults();
577        window.send(b"data".to_vec());
578        assert_eq!(window.in_flight(), 1);
579
580        window.reset();
581        assert_eq!(window.in_flight(), 0);
582        assert_eq!(window.next_seq(), SequenceNumber::new(0));
583    }
584
585    #[test]
586    fn test_receive_window_reset() {
587        let mut window = ReceiveWindow::with_defaults();
588        window.receive(SequenceNumber::new(1), b"data".to_vec());
589        assert_eq!(window.buffered_count(), 1);
590
591        window.reset();
592        assert_eq!(window.buffered_count(), 0);
593    }
594}