embassy_time/
duration.rs

1use core::fmt;
2use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
3
4use super::{GCD_1K, GCD_1M, TICK_HZ};
5use crate::GCD_1G;
6
7#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
8#[cfg_attr(feature = "defmt", derive(defmt::Format))]
9/// Represents the difference between two [Instant](struct.Instant.html)s
10pub struct Duration {
11    pub(crate) ticks: u64,
12}
13
14impl Duration {
15    /// The smallest value that can be represented by the `Duration` type.
16    pub const MIN: Duration = Duration { ticks: u64::MIN };
17    /// The largest value that can be represented by the `Duration` type.
18    pub const MAX: Duration = Duration { ticks: u64::MAX };
19
20    /// Tick count of the `Duration`.
21    pub const fn as_ticks(&self) -> u64 {
22        self.ticks
23    }
24
25    /// Convert the `Duration` to seconds, rounding down.
26    pub const fn as_secs(&self) -> u64 {
27        self.ticks / TICK_HZ
28    }
29
30    /// Convert the `Duration` to milliseconds, rounding down.
31    pub const fn as_millis(&self) -> u64 {
32        self.ticks * (1000 / GCD_1K) / (TICK_HZ / GCD_1K)
33    }
34
35    /// Convert the `Duration` to microseconds, rounding down.
36    pub const fn as_micros(&self) -> u64 {
37        self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M)
38    }
39
40    /// Creates a duration from the specified number of clock ticks
41    pub const fn from_ticks(ticks: u64) -> Duration {
42        Duration { ticks }
43    }
44
45    /// Creates a duration from the specified number of seconds, rounding up.
46    pub const fn from_secs(secs: u64) -> Duration {
47        Duration { ticks: secs * TICK_HZ }
48    }
49
50    /// Creates a duration from the specified number of milliseconds, rounding up.
51    pub const fn from_millis(millis: u64) -> Duration {
52        Duration {
53            ticks: div_ceil(millis * (TICK_HZ / GCD_1K), 1000 / GCD_1K),
54        }
55    }
56
57    /// Creates a duration from the specified number of microseconds, rounding up.
58    /// NOTE: Delays this small may be inaccurate.
59    pub const fn from_micros(micros: u64) -> Duration {
60        Duration {
61            ticks: div_ceil(micros * (TICK_HZ / GCD_1M), 1_000_000 / GCD_1M),
62        }
63    }
64
65    /// Creates a duration from the specified number of nanoseconds, rounding up.
66    /// NOTE: Delays this small may be inaccurate.
67    pub const fn from_nanos(nanoseconds: u64) -> Duration {
68        Duration {
69            ticks: div_ceil(nanoseconds * (TICK_HZ / GCD_1G), 1_000_000_000 / GCD_1G),
70        }
71    }
72
73    /// Creates a duration from the specified number of seconds, rounding down.
74    pub const fn from_secs_floor(secs: u64) -> Duration {
75        Duration { ticks: secs * TICK_HZ }
76    }
77
78    /// Creates a duration from the specified number of milliseconds, rounding down.
79    pub const fn from_millis_floor(millis: u64) -> Duration {
80        Duration {
81            ticks: millis * (TICK_HZ / GCD_1K) / (1000 / GCD_1K),
82        }
83    }
84
85    /// Creates a duration from the specified number of microseconds, rounding down.
86    /// NOTE: Delays this small may be inaccurate.
87    pub const fn from_micros_floor(micros: u64) -> Duration {
88        Duration {
89            ticks: micros * (TICK_HZ / GCD_1M) / (1_000_000 / GCD_1M),
90        }
91    }
92
93    /// Try to create a duration from the specified number of seconds, rounding up.
94    /// Fails if the number of seconds is too large.
95    pub const fn try_from_secs(secs: u64) -> Option<Duration> {
96        let Some(ticks) = secs.checked_mul(TICK_HZ) else {
97            return None;
98        };
99        Some(Duration { ticks })
100    }
101
102    /// Try to create a duration from the specified number of milliseconds, rounding up.
103    /// Fails if the number of milliseconds is too large.
104    pub const fn try_from_millis(millis: u64) -> Option<Duration> {
105        let Some(value) = millis.checked_mul(TICK_HZ / GCD_1K) else {
106            return None;
107        };
108        Some(Duration {
109            ticks: div_ceil(value, 1000 / GCD_1K),
110        })
111    }
112
113    /// Try to create a duration from the specified number of microseconds, rounding up.
114    /// Fails if the number of microseconds is too large.
115    /// NOTE: Delays this small may be inaccurate.
116    pub const fn try_from_micros(micros: u64) -> Option<Duration> {
117        let Some(value) = micros.checked_mul(TICK_HZ / GCD_1M) else {
118            return None;
119        };
120        Some(Duration {
121            ticks: div_ceil(value, 1_000_000 / GCD_1M),
122        })
123    }
124
125    /// Try to create a duration from the specified number of nanoseconds, rounding up.
126    /// Fails if the number of nanoseconds is too large.
127    /// NOTE: Delays this small may be inaccurate.
128    pub const fn try_from_nanos(nanoseconds: u64) -> Option<Duration> {
129        let Some(value) = nanoseconds.checked_mul(TICK_HZ / GCD_1G) else {
130            return None;
131        };
132        Some(Duration {
133            ticks: div_ceil(value, 1_000_000_000 / GCD_1G),
134        })
135    }
136
137    /// Try to create a duration from the specified number of seconds, rounding down.
138    /// Fails if the number of seconds is too large.
139    pub const fn try_from_secs_floor(secs: u64) -> Option<Duration> {
140        let Some(ticks) = secs.checked_mul(TICK_HZ) else {
141            return None;
142        };
143        Some(Duration { ticks })
144    }
145
146    /// Try to create a duration from the specified number of milliseconds, rounding down.
147    /// Fails if the number of milliseconds is too large.
148    pub const fn try_from_millis_floor(millis: u64) -> Option<Duration> {
149        let Some(value) = millis.checked_mul(TICK_HZ / GCD_1K) else {
150            return None;
151        };
152        Some(Duration {
153            ticks: value / (1000 / GCD_1K),
154        })
155    }
156
157    /// Try to create a duration from the specified number of microseconds, rounding down.
158    /// Fails if the number of microseconds is too large.
159    /// NOTE: Delays this small may be inaccurate.
160    pub const fn try_from_micros_floor(micros: u64) -> Option<Duration> {
161        let Some(value) = micros.checked_mul(TICK_HZ / GCD_1M) else {
162            return None;
163        };
164        Some(Duration {
165            ticks: value / (1_000_000 / GCD_1M),
166        })
167    }
168
169    /// Creates a duration corresponding to the specified Hz.
170    /// NOTE: Giving this function a hz >= the TICK_HZ of your platform will clamp the Duration to 1
171    /// tick. Doing so will not deadlock, but will certainly not produce the desired output.
172    pub const fn from_hz(hz: u64) -> Duration {
173        let ticks = {
174            if hz >= TICK_HZ {
175                1
176            } else {
177                (TICK_HZ + hz / 2) / hz
178            }
179        };
180        Duration { ticks }
181    }
182
183    /// Adds one Duration to another, returning a new Duration or None in the event of an overflow.
184    pub fn checked_add(self, rhs: Duration) -> Option<Duration> {
185        self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks })
186    }
187
188    /// Subtracts one Duration to another, returning a new Duration or None in the event of an overflow.
189    pub fn checked_sub(self, rhs: Duration) -> Option<Duration> {
190        self.ticks.checked_sub(rhs.ticks).map(|ticks| Duration { ticks })
191    }
192
193    /// Multiplies one Duration by a scalar u32, returning a new Duration or None in the event of an overflow.
194    pub fn checked_mul(self, rhs: u32) -> Option<Duration> {
195        self.ticks.checked_mul(rhs as _).map(|ticks| Duration { ticks })
196    }
197
198    /// Divides one Duration a scalar u32, returning a new Duration or None in the event of an overflow.
199    pub fn checked_div(self, rhs: u32) -> Option<Duration> {
200        self.ticks.checked_div(rhs as _).map(|ticks| Duration { ticks })
201    }
202}
203
204impl Add for Duration {
205    type Output = Duration;
206
207    fn add(self, rhs: Duration) -> Duration {
208        self.checked_add(rhs).expect("overflow when adding durations")
209    }
210}
211
212impl AddAssign for Duration {
213    fn add_assign(&mut self, rhs: Duration) {
214        *self = *self + rhs;
215    }
216}
217
218impl Sub for Duration {
219    type Output = Duration;
220
221    fn sub(self, rhs: Duration) -> Duration {
222        self.checked_sub(rhs).expect("overflow when subtracting durations")
223    }
224}
225
226impl SubAssign for Duration {
227    fn sub_assign(&mut self, rhs: Duration) {
228        *self = *self - rhs;
229    }
230}
231
232impl Mul<u32> for Duration {
233    type Output = Duration;
234
235    fn mul(self, rhs: u32) -> Duration {
236        self.checked_mul(rhs)
237            .expect("overflow when multiplying duration by scalar")
238    }
239}
240
241impl Mul<Duration> for u32 {
242    type Output = Duration;
243
244    fn mul(self, rhs: Duration) -> Duration {
245        rhs * self
246    }
247}
248
249impl MulAssign<u32> for Duration {
250    fn mul_assign(&mut self, rhs: u32) {
251        *self = *self * rhs;
252    }
253}
254
255impl Div<u32> for Duration {
256    type Output = Duration;
257
258    fn div(self, rhs: u32) -> Duration {
259        self.checked_div(rhs)
260            .expect("divide by zero error when dividing duration by scalar")
261    }
262}
263
264impl DivAssign<u32> for Duration {
265    fn div_assign(&mut self, rhs: u32) {
266        *self = *self / rhs;
267    }
268}
269
270impl<'a> fmt::Display for Duration {
271    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
272        write!(f, "{} ticks", self.ticks)
273    }
274}
275
276#[inline]
277const fn div_ceil(num: u64, den: u64) -> u64 {
278    (num + den - 1) / den
279}
280
281impl TryFrom<core::time::Duration> for Duration {
282    type Error = <u64 as TryFrom<u128>>::Error;
283
284    /// Converts using [`Duration::from_micros`]. Fails if value can not be represented as u64.
285    fn try_from(value: core::time::Duration) -> Result<Self, Self::Error> {
286        Ok(Self::from_micros(value.as_micros().try_into()?))
287    }
288}
289
290impl From<Duration> for core::time::Duration {
291    /// Converts using [`Duration::as_micros`].
292    fn from(value: Duration) -> Self {
293        core::time::Duration::from_micros(value.as_micros())
294    }
295}