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(micros: u64) -> Duration {
68 Duration {
69 ticks: div_ceil(micros * (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 /// Creates a duration corresponding to the specified Hz.
94 /// NOTE: Giving this function a hz >= the TICK_HZ of your platform will clamp the Duration to 1
95 /// tick. Doing so will not deadlock, but will certainly not produce the desired output.
96 pub const fn from_hz(hz: u64) -> Duration {
97 let ticks = {
98 if hz >= TICK_HZ {
99 1
100 } else {
101 (TICK_HZ + hz / 2) / hz
102 }
103 };
104 Duration { ticks }
105 }
106
107 /// Adds one Duration to another, returning a new Duration or None in the event of an overflow.
108 pub fn checked_add(self, rhs: Duration) -> Option<Duration> {
109 self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks })
110 }
111
112 /// Subtracts one Duration to another, returning a new Duration or None in the event of an overflow.
113 pub fn checked_sub(self, rhs: Duration) -> Option<Duration> {
114 self.ticks.checked_sub(rhs.ticks).map(|ticks| Duration { ticks })
115 }
116
117 /// Multiplies one Duration by a scalar u32, returning a new Duration or None in the event of an overflow.
118 pub fn checked_mul(self, rhs: u32) -> Option<Duration> {
119 self.ticks.checked_mul(rhs as _).map(|ticks| Duration { ticks })
120 }
121
122 /// Divides one Duration a scalar u32, returning a new Duration or None in the event of an overflow.
123 pub fn checked_div(self, rhs: u32) -> Option<Duration> {
124 self.ticks.checked_div(rhs as _).map(|ticks| Duration { ticks })
125 }
126}
127
128impl Add for Duration {
129 type Output = Duration;
130
131 fn add(self, rhs: Duration) -> Duration {
132 self.checked_add(rhs).expect(msg:"overflow when adding durations")
133 }
134}
135
136impl AddAssign for Duration {
137 fn add_assign(&mut self, rhs: Duration) {
138 *self = *self + rhs;
139 }
140}
141
142impl Sub for Duration {
143 type Output = Duration;
144
145 fn sub(self, rhs: Duration) -> Duration {
146 self.checked_sub(rhs).expect(msg:"overflow when subtracting durations")
147 }
148}
149
150impl SubAssign for Duration {
151 fn sub_assign(&mut self, rhs: Duration) {
152 *self = *self - rhs;
153 }
154}
155
156impl Mul<u32> for Duration {
157 type Output = Duration;
158
159 fn mul(self, rhs: u32) -> Duration {
160 self.checked_mul(rhs)
161 .expect(msg:"overflow when multiplying duration by scalar")
162 }
163}
164
165impl Mul<Duration> for u32 {
166 type Output = Duration;
167
168 fn mul(self, rhs: Duration) -> Duration {
169 rhs * self
170 }
171}
172
173impl MulAssign<u32> for Duration {
174 fn mul_assign(&mut self, rhs: u32) {
175 *self = *self * rhs;
176 }
177}
178
179impl Div<u32> for Duration {
180 type Output = Duration;
181
182 fn div(self, rhs: u32) -> Duration {
183 self.checked_div(rhs)
184 .expect(msg:"divide by zero error when dividing duration by scalar")
185 }
186}
187
188impl DivAssign<u32> for Duration {
189 fn div_assign(&mut self, rhs: u32) {
190 *self = *self / rhs;
191 }
192}
193
194impl<'a> fmt::Display for Duration {
195 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
196 write!(f, "{} ticks", self.ticks)
197 }
198}
199
200#[inline]
201const fn div_ceil(num: u64, den: u64) -> u64 {
202 (num + den - 1) / den
203}
204
205impl TryFrom<core::time::Duration> for Duration {
206 type Error = <u64 as TryFrom<u128>>::Error;
207
208 /// Converts using [`Duration::from_micros`]. Fails if value can not be represented as u64.
209 fn try_from(value: core::time::Duration) -> Result<Self, Self::Error> {
210 Ok(Self::from_micros(value.as_micros().try_into()?))
211 }
212}
213
214impl From<Duration> for core::time::Duration {
215 /// Converts using [`Duration::as_micros`].
216 fn from(value: Duration) -> Self {
217 core::time::Duration::from_micros(value.as_micros())
218 }
219}
220