netstack3_base/tcp/
timestamp.rs1use core::cmp::{Ord, Ordering, PartialOrd};
8use core::marker::PhantomData;
9use core::ops::Add;
10use core::time::Duration;
11use derivative::Derivative;
12
13#[derive(Debug, Derivative, Clone, Copy)]
26#[derivative(Eq(bound = ""), PartialEq(bound = ""))]
27pub struct Timestamp<U> {
28 timestamp: u32,
29 unit: PhantomData<U>,
30}
31
32#[derive(Debug, Clone, Copy)]
35pub enum Unitless {}
36
37#[derive(Debug, Clone, Copy)]
45pub enum Milliseconds {}
46
47impl<U> Timestamp<U> {
48 pub const fn new(timestamp: u32) -> Self {
50 Timestamp { timestamp, unit: PhantomData::<U> }
51 }
52
53 pub const fn get(&self) -> u32 {
55 let Timestamp { timestamp, unit: _ } = self;
56 *timestamp
57 }
58}
59
60impl Timestamp<Milliseconds> {
61 pub fn duration_since(&self, other: &Timestamp<Milliseconds>) -> Option<Duration> {
63 if self < other {
64 return None;
65 }
66 let diff = self.timestamp.wrapping_sub(other.timestamp);
71 Some(Duration::from_millis(diff.into()))
72 }
73}
74
75impl<U> Ord for Timestamp<U> {
76 fn cmp(&self, rhs: &Timestamp<U>) -> Ordering {
77 let Timestamp { timestamp: lhs, unit: _ } = self;
78 let Timestamp { timestamp: rhs, unit: _ } = rhs;
79 let delta = rhs.wrapping_sub(*lhs);
86 if delta == 0 {
87 Ordering::Equal
88 } else if delta > 0 && delta < (1 << 31) {
89 Ordering::Less
90 } else {
91 Ordering::Greater
92 }
93 }
94}
95
96impl<U> PartialOrd for Timestamp<U> {
97 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
98 Some(self.cmp(other))
99 }
100}
101
102impl Add<Duration> for Timestamp<Milliseconds> {
103 type Output = Self;
104
105 fn add(self, rhs: Duration) -> Self::Output {
106 let Timestamp { timestamp: lhs, unit } = self;
107 let rhs: u128 = rhs.as_millis();
108 let rhs = rhs as u32;
113 Timestamp { timestamp: lhs.wrapping_add(rhs), unit }
114 }
115}
116
117#[derive(Debug, PartialEq, Eq, Clone, Copy)]
119pub struct TimestampOption {
120 pub(super) ts_val: Timestamp<Unitless>,
122 pub(super) ts_echo_reply: Timestamp<Unitless>,
124}
125
126#[derive(Debug, PartialEq, Eq, Clone, Copy)]
129pub struct TxTimestampOption {
130 pub ts_val: Timestamp<Milliseconds>,
134 pub ts_echo_reply: Timestamp<Unitless>,
136}
137
138#[derive(Debug, PartialEq, Eq, Clone, Copy)]
141pub struct RxTimestampOption {
142 pub ts_val: Timestamp<Unitless>,
144 pub ts_echo_reply: Timestamp<Milliseconds>,
149}
150
151impl From<TxTimestampOption> for TimestampOption {
152 fn from(timestamp: TxTimestampOption) -> TimestampOption {
153 let TxTimestampOption { ts_val, ts_echo_reply } = timestamp;
154 let ts_val = Timestamp::<Unitless>::new(ts_val.timestamp);
156 TimestampOption { ts_val, ts_echo_reply }
157 }
158}
159
160impl From<TimestampOption> for RxTimestampOption {
161 fn from(timestamp: TimestampOption) -> RxTimestampOption {
162 let TimestampOption { ts_val, ts_echo_reply } = timestamp;
163 let ts_echo_reply = Timestamp::<Milliseconds>::new(ts_echo_reply.timestamp);
165 RxTimestampOption { ts_val, ts_echo_reply }
166 }
167}
168
169impl From<&packet_formats::tcp::options::TimestampOption> for TimestampOption {
170 fn from(timestamp: &packet_formats::tcp::options::TimestampOption) -> TimestampOption {
171 Self {
172 ts_val: Timestamp::new(timestamp.ts_val()),
173 ts_echo_reply: Timestamp::new(timestamp.ts_echo_reply()),
174 }
175 }
176}
177
178impl From<&TimestampOption> for packet_formats::tcp::options::TimestampOption {
179 fn from(timestamp: &TimestampOption) -> packet_formats::tcp::options::TimestampOption {
180 let TimestampOption { ts_val, ts_echo_reply } = timestamp;
181 packet_formats::tcp::options::TimestampOption::new(ts_val.get(), ts_echo_reply.get())
182 }
183}
184
185#[cfg(any(test, feature = "testutils"))]
186mod testutils {
187 use super::*;
188
189 impl TimestampOption {
190 pub const fn new(ts_val: Timestamp<Unitless>, ts_echo_reply: Timestamp<Unitless>) -> Self {
192 TimestampOption { ts_val, ts_echo_reply }
193 }
194 }
195
196 impl<U> Timestamp<U> {
197 pub const fn discard_unit(self) -> Timestamp<Unitless> {
199 let Timestamp { timestamp, unit: _ } = self;
200 Timestamp { timestamp, unit: PhantomData }
201 }
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 use test_case::test_case;
210
211 #[test_case(1, 2 => Ordering::Less; "less_than")]
212 #[test_case(1, 1 => Ordering::Equal; "equal_to")]
213 #[test_case(2, 1 => Ordering::Greater; "greater_than")]
214 #[test_case(u32::MAX, 0 => Ordering::Less; "wrapped")]
215 #[test_case(0, (1<<31) - 1 => Ordering::Less; "last_in_bounds")]
216 #[test_case(0, 1<<31 => Ordering::Greater; "first_out_of_bounds")]
217 fn timestamp_ordering(lhs: u32, rhs: u32) -> Ordering {
218 let lhs = Timestamp::<Unitless>::new(lhs);
219 let rhs = Timestamp::<Unitless>::new(rhs);
220 lhs.cmp(&rhs)
221 }
222
223 #[test_case(1, 1 => 2; "add")]
224 #[test_case(u32::MAX, 1 => 0; "wrap_in_add")]
225 #[test_case(1, (u32::MAX as u64) + 1 => 1; "wrap_in_duration")]
226 fn timestamp_add_millis(lhs: u32, rhs: u64) -> u32 {
227 let lhs = Timestamp::<Milliseconds>::new(lhs);
228 let rhs = Duration::from_millis(rhs);
229 let result = lhs + rhs;
230 result.get()
231 }
232
233 #[test_case(1, 1, Some(0); "same_time")]
234 #[test_case(1, 2, None; "after")]
235 #[test_case(2, 1, Some(1); "before")]
236 #[test_case(0, 1 << 31, Some(1 << 31); "before_boundary")]
237 #[test_case(0, (1 << 31) - 1, None; "after_boundary")]
238 fn timestamp_duration_since_millis(lhs: u32, rhs: u32, expected_duration: Option<u32>) {
239 let lhs = Timestamp::<Milliseconds>::new(lhs);
240 let rhs = Timestamp::<Milliseconds>::new(rhs);
241 let expected_duration = expected_duration.map(|d| Duration::from_millis(d.into()));
242 assert_eq!(lhs.duration_since(&rhs), expected_duration)
243 }
244}