srt_protocol/packet/
time.rs

1use std::{
2    cmp::Ordering,
3    convert::TryInto,
4    fmt,
5    num::Wrapping,
6    ops::{Add, Div, Mul, Neg, Sub},
7    time::{Duration, Instant},
8    u32,
9};
10
11/// Timestamp in us after creation
12/// These wrap every 2^32 microseconds
13#[derive(Copy, Clone, PartialEq, Eq)]
14pub struct TimeStamp(Wrapping<u32>);
15
16/// Signed duration in us, e.g. RTT
17#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]
18pub struct TimeSpan(i32);
19
20impl TimeSpan {
21    pub const MAX: TimeSpan = TimeSpan::from_micros(i32::MAX);
22    pub const MIN: TimeSpan = TimeSpan::from_micros(i32::MIN);
23    pub const ZERO: TimeSpan = TimeSpan::from_micros(0);
24
25    pub fn from_interval(begin: Instant, end: Instant) -> Self {
26        if begin <= end {
27            Self::ZERO + (end - begin)
28        } else {
29            Self::ZERO - (begin - end)
30        }
31    }
32
33    pub const fn from_micros(us: i32) -> Self {
34        Self(us)
35    }
36
37    pub const fn from_millis(us: i32) -> Self {
38        Self(us * 1_000)
39    }
40
41    pub const fn as_micros(self) -> i32 {
42        self.0
43    }
44
45    pub const fn abs(self) -> Self {
46        Self(self.0.abs())
47    }
48
49    pub fn as_secs_f64(self) -> f64 {
50        self.0 as f64 / 1e6
51    }
52}
53
54impl TimeStamp {
55    pub const MAX: TimeStamp = TimeStamp::from_micros(u32::MAX);
56    pub const MIN: TimeStamp = TimeStamp::from_micros(u32::MIN);
57
58    pub const fn from_micros(us: u32) -> Self {
59        Self(Wrapping(us))
60    }
61
62    pub const fn as_micros(self) -> u32 {
63        (self.0).0
64    }
65}
66
67impl fmt::Debug for TimeStamp {
68    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
69        let time = (self.0).0;
70        let mins = time / 1_000_000 / 60 % 60;
71        let secs = time / 1_000_000 % 60;
72        let micros = time % 1_000_000;
73        write!(f, "{mins:02}:{secs:02}.{micros:06}")
74    }
75}
76
77impl PartialOrd<TimeStamp> for TimeStamp {
78    fn partial_cmp(&self, other: &TimeStamp) -> Option<Ordering> {
79        Some(self.cmp(other))
80    }
81}
82
83impl Ord for TimeStamp {
84    fn cmp(&self, other: &Self) -> Ordering {
85        // this is a "best effort" implementation, and goes for close
86        // if timestamps are very far apart, this will not work (and cannot)
87        (*self - *other).as_micros().cmp(&0)
88    }
89}
90
91impl Add<TimeSpan> for TimeStamp {
92    type Output = TimeStamp;
93
94    fn add(self, rhs: TimeSpan) -> Self::Output {
95        if rhs < TimeSpan::ZERO {
96            TimeStamp(self.0 - Wrapping(rhs.0.unsigned_abs()))
97        } else {
98            TimeStamp(self.0 + Wrapping(rhs.0 as u32))
99        }
100    }
101}
102
103impl Sub<TimeSpan> for TimeStamp {
104    type Output = TimeStamp;
105
106    fn sub(self, rhs: TimeSpan) -> Self::Output {
107        if rhs < TimeSpan::ZERO {
108            TimeStamp(self.0 + Wrapping(rhs.0.unsigned_abs()))
109        } else {
110            TimeStamp(self.0 - Wrapping(rhs.0 as u32))
111        }
112    }
113}
114
115impl Add<Duration> for TimeStamp {
116    type Output = TimeStamp;
117
118    fn add(self, rhs: Duration) -> Self::Output {
119        Self(self.0 + Wrapping(rhs.as_micros() as u32))
120    }
121}
122
123impl Sub<Duration> for TimeStamp {
124    type Output = TimeStamp;
125
126    fn sub(self, rhs: Duration) -> Self::Output {
127        Self(self.0 - Wrapping(rhs.as_micros() as u32))
128    }
129}
130
131impl Sub<TimeStamp> for TimeStamp {
132    type Output = TimeSpan;
133
134    fn sub(self, rhs: TimeStamp) -> TimeSpan {
135        // This is also a "best effort" implementation, and cannot be precise
136        let pos_sub = self.0 - rhs.0;
137        let neg_sub = rhs.0 - self.0;
138        if pos_sub < neg_sub {
139            TimeSpan(pos_sub.0 as i32)
140        } else {
141            -TimeSpan(neg_sub.0 as i32)
142        }
143    }
144}
145
146impl fmt::Debug for TimeSpan {
147    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
148        let sign = if self.0 < 0 { "-" } else { "" };
149        let mins = self.0.abs() / 1_000_000 / 60 % 60;
150        let secs = self.0.abs() / 1_000_000 % 60;
151        let micros = self.0.abs() % 1_000_000;
152        write!(f, "{sign}{mins:02}:{secs:02}.{micros:06}")
153    }
154}
155
156impl From<Duration> for TimeSpan {
157    fn from(duration: Duration) -> TimeSpan {
158        TimeSpan::from_micros(duration.as_micros().try_into().unwrap())
159    }
160}
161
162impl Neg for TimeSpan {
163    type Output = TimeSpan;
164
165    fn neg(self) -> Self::Output {
166        Self(-self.0)
167    }
168}
169
170impl Mul<i32> for TimeSpan {
171    type Output = TimeSpan;
172
173    fn mul(self, rhs: i32) -> Self::Output {
174        Self(self.0 * rhs)
175    }
176}
177
178impl Mul<TimeSpan> for i32 {
179    type Output = TimeSpan;
180
181    fn mul(self, rhs: TimeSpan) -> Self::Output {
182        TimeSpan(self * rhs.0)
183    }
184}
185
186impl Div<i32> for TimeSpan {
187    type Output = TimeSpan;
188
189    fn div(self, rhs: i32) -> Self::Output {
190        Self(self.0 / rhs)
191    }
192}
193
194impl Add<TimeSpan> for TimeSpan {
195    type Output = TimeSpan;
196
197    fn add(self, rhs: TimeSpan) -> Self::Output {
198        Self(self.0 + rhs.0)
199    }
200}
201
202impl Sub<TimeSpan> for TimeSpan {
203    type Output = TimeSpan;
204
205    fn sub(self, rhs: TimeSpan) -> Self::Output {
206        Self(self.0 - rhs.0)
207    }
208}
209
210impl Add<Duration> for TimeSpan {
211    type Output = TimeSpan;
212
213    fn add(self, rhs: Duration) -> Self::Output {
214        Self(self.0 + rhs.as_micros() as i32)
215    }
216}
217
218impl Sub<Duration> for TimeSpan {
219    type Output = TimeSpan;
220
221    fn sub(self, rhs: Duration) -> Self::Output {
222        Self(self.0 - rhs.as_micros() as i32)
223    }
224}
225
226impl Add<TimeSpan> for Duration {
227    type Output = Duration;
228
229    fn add(self, rhs: TimeSpan) -> Self::Output {
230        if rhs > TimeSpan::ZERO {
231            self + Duration::from_micros(rhs.as_micros() as u64)
232        } else {
233            self - Duration::from_micros(u64::from(rhs.as_micros().unsigned_abs()))
234        }
235    }
236}
237
238impl Sub<TimeSpan> for Duration {
239    type Output = Duration;
240
241    fn sub(self, rhs: TimeSpan) -> Self::Output {
242        self.add(-rhs)
243    }
244}
245
246impl Add<TimeSpan> for Instant {
247    type Output = Instant;
248
249    fn add(self, rhs: TimeSpan) -> Self::Output {
250        let micros = rhs.as_micros() as i64;
251        if micros > 0 {
252            self + Duration::from_micros(micros as u64)
253        } else {
254            self.checked_sub(Duration::from_micros(micros.unsigned_abs()))
255                .unwrap()
256        }
257    }
258}
259
260impl Sub<TimeSpan> for Instant {
261    type Output = Instant;
262
263    fn sub(self, rhs: TimeSpan) -> Self::Output {
264        let micros = rhs.as_micros() as i64;
265        if micros > 0 {
266            self + Duration::from_micros(micros as u64)
267        } else {
268            self.checked_sub(Duration::from_micros(micros.unsigned_abs()))
269                .unwrap()
270        }
271    }
272}
273
274#[cfg(test)]
275mod timestamp {
276    use super::*;
277
278    #[test]
279    #[allow(clippy::eq_op)]
280    fn timestamp_operators() {
281        let ts = TimeStamp::from_micros(u32::MAX >> 1);
282        let a = ts + Duration::from_micros(10);
283        let b = ts + Duration::from_micros(11);
284
285        assert_eq!(a - a, TimeSpan::ZERO);
286        assert_eq!(b - a, TimeSpan::from_micros(1));
287        assert_eq!(a - b, TimeSpan::from_micros(-1));
288        assert!(b > a);
289
290        let d = Duration::from_micros(10);
291        assert_eq!(d + TimeSpan::from_micros(1), Duration::from_micros(11));
292        assert_eq!(d + TimeSpan::from_micros(1), d - TimeSpan::from_micros(-1));
293
294        let max = TimeStamp::MIN - TimeSpan::from_micros(1);
295        let min = TimeStamp::MAX + TimeSpan::from_micros(1);
296        assert_eq!(max.as_micros(), u32::MAX);
297        assert_eq!(min.as_micros(), u32::MIN);
298        assert_eq!(max - min, TimeSpan::from_micros(-1));
299        assert_eq!(min - max, TimeSpan::from_micros(1));
300        assert!(max > a);
301        assert!(b < max);
302        // this is counter intuitive, but a modulo counter wraps
303        //  so max + 1 == min and max + 1 > max
304        assert!(min > max);
305        assert!(max < min);
306    }
307
308    #[test]
309    fn debug_fmt() {
310        assert_eq!("00:00.000001", format!("{:?}", TimeStamp::from_micros(1)));
311        assert_eq!(
312            "01:02.030040",
313            format!("{:?}", TimeStamp::from_micros(62030040))
314        );
315    }
316}