s2n_quic_core/ack/
transmission.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::{ack, packet::number::PacketNumber};
5use core::ops::RangeInclusive;
6
7pub type AckRange = RangeInclusive<PacketNumber>;
8
9#[derive(Clone, Debug, Default)]
10pub struct Set {
11    /// A stable ack-eliciting transmission
12    ///
13    /// In this case, "stable" means the oldest transmission that
14    /// hasn't been acked by the peer.
15    ///
16    /// This transmission is stored to ensure the packet number ranges
17    /// are always either removed or declared lost. Without it,
18    /// the TX packet number would be a moving target
19    /// and packet number ranges would never be removed.
20    stable: Option<Transmission>,
21
22    /// The latest ack-eliciting transmission
23    latest: Option<Transmission>,
24}
25
26impl Set {
27    /// Called when an ACK frame is bundled with an ack eliciting packet
28    #[inline]
29    pub fn on_transmit(&mut self, transmission: Transmission) {
30        self.latest = Some(transmission);
31
32        // only set the stable transmission if it's not set
33        if self.stable.is_none() {
34            self.stable = Some(transmission);
35        }
36    }
37
38    /// Called when a set of packets was acknowledged or lost
39    #[inline]
40    pub fn on_update<A: ack::Set>(&mut self, ack_set: &A) -> Option<AckRange> {
41        if let Some(ack_range) = self
42            .latest
43            .as_ref()
44            .and_then(|transmission| transmission.ack_range(ack_set))
45        {
46            // the latest was either ACKed or lost so there is nothing pending
47            self.stable = None;
48            self.latest = None;
49            return Some(ack_range);
50        }
51
52        if let Some(ack_range) = self
53            .stable
54            .as_ref()
55            .and_then(|transmission| transmission.ack_range(ack_set))
56        {
57            // assign the latest transmission to stable
58            self.stable = self.latest;
59            return Some(ack_range);
60        }
61
62        None
63    }
64
65    /// Returns `true` if there are no pending ack-eliciting transmissions
66    /// Only used by tests
67    #[cfg(test)]
68    pub fn is_empty(&self) -> bool {
69        self.stable.is_none() && self.latest.is_none()
70    }
71}
72
73#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd)]
74pub struct Transmission {
75    pub sent_in_packet: PacketNumber,
76    pub largest_received_packet_number_acked: PacketNumber,
77}
78
79impl Transmission {
80    /// Called when a set of packets was acknowledged or lost
81    #[inline]
82    pub fn ack_range<A: ack::Set>(&self, ack_set: &A) -> Option<AckRange> {
83        //= https://www.rfc-editor.org/rfc/rfc9000#section-13.2.4
84        //# When a packet containing an ACK frame is acknowledged, the receiver can stop
85        //# acknowledging packets less than or equal to the Largest Acknowledged
86        //# field in the sent ACK frame.
87        if ack_set.contains(self.sent_in_packet) {
88            // create a range from 0..=largest
89            let pn_zero = self
90                .largest_received_packet_number_acked
91                .space()
92                .new_packet_number(Default::default());
93
94            Some(pn_zero..=self.largest_received_packet_number_acked)
95        } else {
96            None
97        }
98    }
99}
100
101#[cfg(test)]
102mod tests {
103    use super::*;
104    use crate::ack::testing::transmissions_iter;
105
106    /// This test is meant to simulate an immediate ACK rate from a peer
107    #[test]
108    fn latest_ack_test() {
109        let mut set = Set::default();
110        assert!(set.is_empty());
111
112        let mut transmissions = transmissions_iter();
113
114        for transmission_count in 0..10 {
115            let stable = transmissions.next().unwrap();
116            set.on_transmit(stable);
117
118            // simulate a few transmissions before ACKing
119            let mut latest = stable;
120            for _ in 0..transmission_count {
121                latest = transmissions.next().unwrap();
122                set.on_transmit(latest);
123            }
124
125            // ACKing `latest` simulates an immediate ACK from peer
126            assert_eq!(
127                set.on_update(&latest.sent_in_packet).unwrap().end(),
128                &latest.largest_received_packet_number_acked
129            );
130
131            // ACKing the latest removes all items
132            assert!(set.is_empty());
133        }
134    }
135
136    /// This test is meant to simulate a delayed ACK rate from a peer
137    #[test]
138    fn stable_ack_test() {
139        let mut set = Set::default();
140        assert!(set.is_empty());
141
142        let mut transmissions = transmissions_iter();
143
144        // create an initial transmission
145        let mut stable = transmissions.next().unwrap();
146        set.on_transmit(stable);
147
148        for transmission_count in 0..10 {
149            // simulate a few transmissions before ACKing
150            let mut latest = stable;
151            for _ in 0..transmission_count {
152                latest = transmissions.next().unwrap();
153                set.on_transmit(latest);
154            }
155
156            // ACKing `stable` simulates a delayed ACK from peer
157            assert_eq!(
158                set.on_update(&stable.sent_in_packet).unwrap().end(),
159                &stable.largest_received_packet_number_acked
160            );
161
162            // on update latest is moved into stable
163            stable = latest;
164
165            // if there weren't any transmissions between, the set
166            // should be empty
167            if transmission_count == 0 {
168                assert!(set.is_empty());
169                set.on_transmit(stable);
170            }
171        }
172    }
173
174    #[test]
175    #[cfg_attr(miri, ignore)] // miri is unable to read the file system
176    fn size_of_snapshots() {
177        use core::mem::size_of;
178        use insta::assert_debug_snapshot;
179
180        assert_debug_snapshot!("Transmission", size_of::<Transmission>());
181        assert_debug_snapshot!("Set", size_of::<Set>());
182    }
183}