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<U> Ord for Timestamp<U> {
61 fn cmp(&self, rhs: &Timestamp<U>) -> Ordering {
62 let Timestamp { timestamp: lhs, unit: _ } = self;
63 let Timestamp { timestamp: rhs, unit: _ } = rhs;
64 let delta = rhs.wrapping_sub(*lhs);
71 if delta == 0 {
72 Ordering::Equal
73 } else if delta > 0 && delta < (1 << 31) {
74 Ordering::Less
75 } else {
76 Ordering::Greater
77 }
78 }
79}
80
81impl<U> PartialOrd for Timestamp<U> {
82 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
83 Some(self.cmp(other))
84 }
85}
86
87impl Add<Duration> for Timestamp<Milliseconds> {
88 type Output = Self;
89
90 fn add(self, rhs: Duration) -> Self::Output {
91 let Timestamp { timestamp: lhs, unit } = self;
92 let rhs: u128 = rhs.as_millis();
93 let rhs = rhs as u32;
98 Timestamp { timestamp: lhs.wrapping_add(rhs), unit }
99 }
100}
101
102#[derive(Debug, PartialEq, Eq, Clone, Copy)]
104pub struct TimestampOption {
105 pub(super) ts_val: Timestamp<Unitless>,
107 pub(super) ts_echo_reply: Timestamp<Unitless>,
109}
110
111#[derive(Debug, PartialEq, Eq, Clone, Copy)]
114pub struct TxTimestampOption {
115 pub ts_val: Timestamp<Milliseconds>,
119 pub ts_echo_reply: Timestamp<Unitless>,
121}
122
123#[derive(Debug, PartialEq, Eq, Clone, Copy)]
126pub struct RxTimestampOption {
127 pub ts_val: Timestamp<Unitless>,
129 pub ts_echo_reply: Timestamp<Milliseconds>,
134}
135
136impl From<TxTimestampOption> for TimestampOption {
137 fn from(timestamp: TxTimestampOption) -> TimestampOption {
138 let TxTimestampOption { ts_val, ts_echo_reply } = timestamp;
139 let ts_val = Timestamp::<Unitless>::new(ts_val.timestamp);
141 TimestampOption { ts_val, ts_echo_reply }
142 }
143}
144
145impl From<TimestampOption> for RxTimestampOption {
146 fn from(timestamp: TimestampOption) -> RxTimestampOption {
147 let TimestampOption { ts_val, ts_echo_reply } = timestamp;
148 let ts_echo_reply = Timestamp::<Milliseconds>::new(ts_echo_reply.timestamp);
150 RxTimestampOption { ts_val, ts_echo_reply }
151 }
152}
153
154impl From<&packet_formats::tcp::options::TimestampOption> for TimestampOption {
155 fn from(timestamp: &packet_formats::tcp::options::TimestampOption) -> TimestampOption {
156 Self {
157 ts_val: Timestamp::new(timestamp.ts_val()),
158 ts_echo_reply: Timestamp::new(timestamp.ts_echo_reply()),
159 }
160 }
161}
162
163impl From<&TimestampOption> for packet_formats::tcp::options::TimestampOption {
164 fn from(timestamp: &TimestampOption) -> packet_formats::tcp::options::TimestampOption {
165 let TimestampOption { ts_val, ts_echo_reply } = timestamp;
166 packet_formats::tcp::options::TimestampOption::new(ts_val.get(), ts_echo_reply.get())
167 }
168}
169
170#[cfg(any(test, feature = "testutils"))]
171mod testutils {
172 use super::*;
173
174 impl TimestampOption {
175 pub const fn new(ts_val: Timestamp<Unitless>, ts_echo_reply: Timestamp<Unitless>) -> Self {
177 TimestampOption { ts_val, ts_echo_reply }
178 }
179 }
180
181 impl<U> Timestamp<U> {
182 pub const fn discard_unit(self) -> Timestamp<Unitless> {
184 let Timestamp { timestamp, unit: _ } = self;
185 Timestamp { timestamp, unit: PhantomData }
186 }
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 use test_case::test_case;
195
196 #[test_case(1, 2 => Ordering::Less; "less_than")]
197 #[test_case(1, 1 => Ordering::Equal; "equal_to")]
198 #[test_case(2, 1 => Ordering::Greater; "greater_than")]
199 #[test_case(u32::MAX, 0 => Ordering::Less; "wrapped")]
200 #[test_case(0, (1<<31) - 1 => Ordering::Less; "last_in_bounds")]
201 #[test_case(0, 1<<31 => Ordering::Greater; "first_out_of_bounds")]
202 fn timestamp_ordering(lhs: u32, rhs: u32) -> Ordering {
203 let lhs = Timestamp::<Unitless>::new(lhs);
204 let rhs = Timestamp::<Unitless>::new(rhs);
205 lhs.cmp(&rhs)
206 }
207
208 #[test_case(1, 1 => 2; "add")]
209 #[test_case(u32::MAX, 1 => 0; "wrap_in_add")]
210 #[test_case(1, (u32::MAX as u64) + 1 => 1; "wrap_in_duration")]
211 fn timestamp_add_millis(lhs: u32, rhs: u64) -> u32 {
212 let lhs = Timestamp::<Milliseconds>::new(lhs);
213 let rhs = Duration::from_millis(rhs);
214 let result = lhs + rhs;
215 result.get()
216 }
217}