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
13use packet_formats::tcp::options::TcpOption;
14
15#[derive(Debug, Derivative, Clone, Copy)]
28#[derivative(Eq(bound = ""), PartialEq(bound = ""))]
29pub struct Timestamp<U> {
30 timestamp: u32,
31 unit: PhantomData<U>,
32}
33
34#[derive(Debug, Clone, Copy)]
37pub enum Unitless {}
38
39#[derive(Debug, Clone, Copy)]
47pub enum Milliseconds {}
48
49impl<U> Timestamp<U> {
50 pub const fn new(timestamp: u32) -> Self {
52 Timestamp { timestamp, unit: PhantomData::<U> }
53 }
54
55 pub const fn get(&self) -> u32 {
57 let Timestamp { timestamp, unit: _ } = self;
58 *timestamp
59 }
60}
61
62impl<U> Ord for Timestamp<U> {
63 fn cmp(&self, rhs: &Timestamp<U>) -> Ordering {
64 let Timestamp { timestamp: lhs, unit: _ } = self;
65 let Timestamp { timestamp: rhs, unit: _ } = rhs;
66 let delta = rhs.wrapping_sub(*lhs);
73 if delta == 0 {
74 Ordering::Equal
75 } else if delta > 0 && delta < (1 << 31) {
76 Ordering::Less
77 } else {
78 Ordering::Greater
79 }
80 }
81}
82
83impl<U> PartialOrd for Timestamp<U> {
84 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
85 Some(self.cmp(other))
86 }
87}
88
89impl Add<Duration> for Timestamp<Milliseconds> {
90 type Output = Self;
91
92 fn add(self, rhs: Duration) -> Self::Output {
93 let Timestamp { timestamp: lhs, unit } = self;
94 let rhs: u128 = rhs.as_millis();
95 let rhs = rhs as u32;
100 Timestamp { timestamp: lhs.wrapping_add(rhs), unit }
101 }
102}
103
104#[derive(Debug, PartialEq, Eq, Clone, Copy)]
106pub struct TimestampOption {
107 pub(super) ts_val: Timestamp<Unitless>,
109 pub(super) ts_echo_reply: Timestamp<Unitless>,
111}
112
113#[derive(Debug, PartialEq, Eq, Clone, Copy)]
116pub struct TxTimestampOption {
117 pub ts_val: Timestamp<Milliseconds>,
121 pub ts_echo_reply: Timestamp<Unitless>,
123}
124
125#[derive(Debug, PartialEq, Eq, Clone, Copy)]
128pub struct RxTimestampOption {
129 pub ts_val: Timestamp<Unitless>,
131 pub ts_echo_reply: Timestamp<Milliseconds>,
136}
137
138impl From<TxTimestampOption> for TimestampOption {
139 fn from(timestamp: TxTimestampOption) -> TimestampOption {
140 let TxTimestampOption { ts_val, ts_echo_reply } = timestamp;
141 let ts_val = Timestamp::<Unitless>::new(ts_val.timestamp);
143 TimestampOption { ts_val, ts_echo_reply }
144 }
145}
146
147impl From<TimestampOption> for RxTimestampOption {
148 fn from(timestamp: TimestampOption) -> RxTimestampOption {
149 let TimestampOption { ts_val, ts_echo_reply } = timestamp;
150 let ts_echo_reply = Timestamp::<Milliseconds>::new(ts_echo_reply.timestamp);
152 RxTimestampOption { ts_val, ts_echo_reply }
153 }
154}
155
156impl<'a> Into<TcpOption<'a>> for TimestampOption {
157 fn into(self) -> TcpOption<'a> {
158 let TimestampOption { ts_val, ts_echo_reply } = self;
159 TcpOption::Timestamp { ts_val: ts_val.get(), ts_echo_reply: ts_echo_reply.get() }
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 use test_case::test_case;
168
169 #[test_case(1, 2 => Ordering::Less; "less_than")]
170 #[test_case(1, 1 => Ordering::Equal; "equal_to")]
171 #[test_case(2, 1 => Ordering::Greater; "greater_than")]
172 #[test_case(u32::MAX, 0 => Ordering::Less; "wrapped")]
173 #[test_case(0, (1<<31) - 1 => Ordering::Less; "last_in_bounds")]
174 #[test_case(0, 1<<31 => Ordering::Greater; "first_out_of_bounds")]
175 fn timestamp_ordering(lhs: u32, rhs: u32) -> Ordering {
176 let lhs = Timestamp::<Unitless>::new(lhs);
177 let rhs = Timestamp::<Unitless>::new(rhs);
178 lhs.cmp(&rhs)
179 }
180
181 #[test_case(1, 1 => 2; "add")]
182 #[test_case(u32::MAX, 1 => 0; "wrap_in_add")]
183 #[test_case(1, (u32::MAX as u64) + 1 => 1; "wrap_in_duration")]
184 fn timestamp_add_millis(lhs: u32, rhs: u64) -> u32 {
185 let lhs = Timestamp::<Milliseconds>::new(lhs);
186 let rhs = Duration::from_millis(rhs);
187 let result = lhs + rhs;
188 result.get()
189 }
190}