pw_time_core/
pw_time_core.rs1#![no_std]
15
16use core::marker::PhantomData;
26use core::ops::{Add, Sub};
27
28#[cfg(test)]
29#[unsafe(no_mangle)]
30unsafe extern "C-unwind" fn pw_assert_HandleFailure() -> ! {
31 panic!("pw_assert failed");
32}
33
34pub trait Clock: Sized {
36 const TICKS_PER_SEC: u64;
38
39 fn now() -> Instant<Self>;
41}
42
43#[derive(Debug)]
48pub struct Instant<Clock: crate::Clock> {
49 ticks: u64,
50 _phantom: PhantomData<Clock>,
51}
52
53impl<Clock: crate::Clock> Instant<Clock> {
54 pub const MAX: Self = Self::from_ticks(u64::MAX);
56 pub const MIN: Self = Self::from_ticks(u64::MIN);
58
59 #[must_use]
61 pub const fn from_ticks(ticks: u64) -> Self {
62 Self {
63 ticks,
64 _phantom: PhantomData,
65 }
66 }
67
68 #[must_use]
70 pub const fn ticks(&self) -> u64 {
71 self.ticks
72 }
73
74 #[must_use]
76 pub const fn checked_add_duration(self, duration: Duration<Clock>) -> Option<Self> {
77 if let Some(ticks) = self.ticks.checked_add(duration.ticks) {
78 Some(Self {
79 ticks,
80 _phantom: PhantomData,
81 })
82 } else {
83 None
84 }
85 }
86
87 #[must_use]
89 pub const fn checked_sub_duration(self, duration: Duration<Clock>) -> Option<Self> {
90 if let Some(ticks) = self.ticks.checked_sub(duration.ticks) {
91 Some(Self {
92 ticks,
93 _phantom: PhantomData,
94 })
95 } else {
96 None
97 }
98 }
99}
100
101impl<Clock: crate::Clock> Copy for Instant<Clock> {}
103
104impl<Clock: crate::Clock> Clone for Instant<Clock> {
106 fn clone(&self) -> Self {
107 *self
108 }
109}
110
111impl<Clock: crate::Clock> Ord for Instant<Clock> {
113 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
114 self.ticks.cmp(&other.ticks)
115 }
116}
117
118impl<Clock: crate::Clock> PartialOrd for Instant<Clock> {
120 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
121 Some(self.cmp(other))
122 }
123}
124
125impl<Clock: crate::Clock> Eq for Instant<Clock> {}
127
128impl<Clock: crate::Clock> PartialEq for Instant<Clock> {
130 fn eq(&self, other: &Self) -> bool {
131 self.ticks == other.ticks
132 }
133}
134
135impl<Clock: crate::Clock> Sub<Instant<Clock>> for Instant<Clock> {
136 type Output = Duration<Clock>;
137
138 fn sub(self, rhs: Instant<Clock>) -> Self::Output {
139 let ticks = self.ticks.saturating_sub(rhs.ticks);
141 Self::Output {
142 ticks,
143 _phantom: PhantomData,
144 }
145 }
146}
147
148impl<Clock: crate::Clock> Add<Duration<Clock>> for Instant<Clock> {
149 type Output = Instant<Clock>;
150
151 fn add(self, rhs: Duration<Clock>) -> Self::Output {
152 let time = self.checked_add_duration(rhs);
153 if time.is_none() {
154 pw_assert::panic!("Instant - Duration overflow");
155 }
156 time.unwrap()
157 }
158}
159
160impl<Clock: crate::Clock> Sub<Duration<Clock>> for Instant<Clock> {
161 type Output = Instant<Clock>;
162
163 fn sub(self, rhs: Duration<Clock>) -> Self::Output {
164 let time = self.checked_sub_duration(rhs);
165 if time.is_none() {
166 pw_assert::panic!("Instant - Duration overflow")
167 }
168 time.unwrap()
169 }
170}
171
172#[derive(Debug)]
177pub struct Duration<Clock: crate::Clock> {
178 ticks: u64,
179 _phantom: PhantomData<Clock>,
180}
181
182impl<Clock: crate::Clock> Duration<Clock> {
183 pub const MAX: Self = Self {
185 ticks: u64::MAX,
186 _phantom: PhantomData,
187 };
188
189 pub const MIN: Self = Self {
191 ticks: u64::MIN,
192 _phantom: PhantomData,
193 };
194
195 #[must_use]
197 pub const fn ticks(self) -> u64 {
198 self.ticks
199 }
200
201 #[must_use]
203 pub const fn from_secs(secs: u64) -> Self {
204 Self {
205 ticks: secs * Clock::TICKS_PER_SEC,
206 _phantom: PhantomData,
207 }
208 }
209
210 #[must_use]
212 pub const fn from_millis(millis: u64) -> Self {
213 Self {
214 ticks: millis * Clock::TICKS_PER_SEC / 1000,
215 _phantom: PhantomData,
216 }
217 }
218
219 #[must_use]
221 pub const fn from_micros(micros: u64) -> Self {
222 Self {
223 ticks: micros * Clock::TICKS_PER_SEC / 1_000_000,
224 _phantom: PhantomData,
225 }
226 }
227
228 #[must_use]
230 pub const fn from_nanos(nanos: u64) -> Self {
231 Self {
232 ticks: nanos * Clock::TICKS_PER_SEC / 1_000_000_000,
233 _phantom: PhantomData,
234 }
235 }
236
237 #[must_use]
239 pub fn as_secs(self) -> u64 {
240 self.ticks / Clock::TICKS_PER_SEC
241 }
242
243 #[must_use]
245 pub fn as_millis(self) -> u128 {
246 u128::from(self.ticks) * 1000 / u128::from(Clock::TICKS_PER_SEC)
247 }
248
249 #[must_use]
251 pub fn as_micros(self) -> u128 {
252 u128::from(self.ticks) * 1_000_000 / u128::from(Clock::TICKS_PER_SEC)
253 }
254
255 #[must_use]
257 pub fn as_nanos(self) -> u128 {
258 u128::from(self.ticks) * 1_000_000_000 / u128::from(Clock::TICKS_PER_SEC)
259 }
260
261 #[must_use]
263 pub const fn checked_add(self, rhs: Duration<Clock>) -> Option<Self> {
264 if let Some(ticks) = self.ticks.checked_add(rhs.ticks) {
265 Some(Self {
266 ticks,
267 _phantom: PhantomData,
268 })
269 } else {
270 None
271 }
272 }
273
274 #[must_use]
276 pub const fn checked_sub(self, rhs: Duration<Clock>) -> Option<Self> {
277 if let Some(ticks) = self.ticks.checked_sub(rhs.ticks) {
278 Some(Self {
279 ticks,
280 _phantom: PhantomData,
281 })
282 } else {
283 None
284 }
285 }
286}
287
288impl<Clock: crate::Clock> Copy for Duration<Clock> {}
290
291impl<Clock: crate::Clock> Clone for Duration<Clock> {
293 fn clone(&self) -> Self {
294 *self
295 }
296}
297
298impl<Clock: crate::Clock> Ord for Duration<Clock> {
300 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
301 self.ticks.cmp(&other.ticks)
302 }
303}
304
305impl<Clock: crate::Clock> PartialOrd for Duration<Clock> {
307 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
308 Some(self.cmp(other))
309 }
310}
311
312impl<Clock: crate::Clock> Eq for Duration<Clock> {}
314
315impl<Clock: crate::Clock> PartialEq for Duration<Clock> {
317 fn eq(&self, other: &Self) -> bool {
318 self.ticks == other.ticks
319 }
320}
321
322impl<Clock: crate::Clock> Sub<Duration<Clock>> for Duration<Clock> {
323 type Output = Duration<Clock>;
324
325 fn sub(self, rhs: Duration<Clock>) -> Self::Output {
326 let time = self.checked_sub(rhs);
327 if time.is_none() {
328 pw_assert::panic!("Duration subtraction overflow")
329 }
330 time.unwrap()
331 }
332}
333
334impl<Clock: crate::Clock> Add<Duration<Clock>> for Duration<Clock> {
335 type Output = Duration<Clock>;
336
337 fn add(self, rhs: Duration<Clock>) -> Self::Output {
338 let time = self.checked_add(rhs);
339 if time.is_none() {
340 pw_assert::panic!("Duration addition overflow")
341 }
342 time.unwrap()
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use super::*;
349
350 #[derive(Debug)]
351 struct TestClock;
352
353 impl Clock for TestClock {
354 const TICKS_PER_SEC: u64 = 1_000;
355 fn now() -> Instant<Self> {
356 Instant::from_ticks(0)
357 }
358 }
359
360 #[derive(Debug)]
361 struct HighResTestClock;
362
363 impl Clock for HighResTestClock {
364 const TICKS_PER_SEC: u64 = 1_000_000_000;
365 fn now() -> Instant<Self> {
366 Instant::from_ticks(0)
367 }
368 }
369
370 #[test]
371 fn duration_constructors_return_correct_values() {
372 assert_eq!(Duration::<TestClock>::from_secs(1234).ticks(), 1_234_000);
373 assert_eq!(Duration::<TestClock>::from_millis(1234).ticks(), 1_234);
374 assert_eq!(Duration::<TestClock>::from_micros(1234).ticks(), 1);
375 assert_eq!(Duration::<TestClock>::from_nanos(1234).ticks(), 0);
376
377 assert_eq!(Duration::<HighResTestClock>::from_nanos(1234).ticks(), 1234);
378 }
379
380 #[test]
381 fn duration_accessors_return_correct_values() {
382 let dur = Duration::<TestClock>::from_secs(123);
383 assert_eq!(dur.as_secs(), 123u64);
384 assert_eq!(dur.as_millis(), 123_000u128);
385 assert_eq!(dur.as_micros(), 123_000_000u128);
386 assert_eq!(dur.as_nanos(), 123_000_000_000u128);
387 }
388
389 #[test]
390 fn duration_checked_addition_returns_correct_values() {
391 let ten_ms = Duration::<TestClock>::from_millis(10);
392 let one_ms = Duration::<TestClock>::from_millis(1);
393
394 assert_eq!(
395 ten_ms.checked_add(one_ms),
396 Some(Duration::<TestClock>::from_millis(11))
397 );
398
399 assert_eq!(
400 one_ms.checked_add(ten_ms),
401 Some(Duration::<TestClock>::from_millis(11))
402 );
403
404 assert_eq!(Duration::<TestClock>::MAX.checked_add(one_ms), None);
405 }
406
407 #[test]
408 fn duration_checked_subtraction_returns_correct_values() {
409 let ten_ms = Duration::<TestClock>::from_millis(10);
410 let one_ms = Duration::<TestClock>::from_millis(1);
411
412 assert_eq!(
413 ten_ms.checked_sub(one_ms),
414 Some(Duration::<TestClock>::from_millis(9))
415 );
416
417 assert_eq!(one_ms.checked_sub(ten_ms), None);
418
419 assert_eq!(Duration::<TestClock>::MIN.checked_sub(one_ms), None);
420 }
421
422 #[test]
423 fn instant_subtraction_returns_correct_values() {
424 let ten_ms = Instant::from_ticks(10 * <TestClock as Clock>::TICKS_PER_SEC / 1000);
425 let one_ms = Instant::from_ticks(<TestClock as Clock>::TICKS_PER_SEC / 1000);
426
427 assert_eq!(ten_ms - one_ms, Duration::<TestClock>::from_millis(9));
428 assert_eq!(one_ms - ten_ms, Duration::<TestClock>::from_millis(0));
429 }
430
431 #[test]
432 fn instant_checked_duration_addition_returns_correct_values() {
433 let instant_eleven_ms =
434 Instant::<TestClock>::from_ticks(11 * <TestClock as Clock>::TICKS_PER_SEC / 1000);
435 let instant_ten_ms =
436 Instant::<TestClock>::from_ticks(10 * <TestClock as Clock>::TICKS_PER_SEC / 1000);
437
438 let duration_one_ms = Duration::<TestClock>::from_millis(1);
439
440 assert_eq!(
441 instant_ten_ms.checked_add_duration(duration_one_ms),
442 Some(instant_eleven_ms)
443 );
444
445 assert_eq!(
446 Instant::<TestClock>::MAX.checked_add_duration(duration_one_ms),
447 None
448 );
449 }
450
451 #[test]
452 fn instant_checked_duration_subtraction_returns_correct_values() {
453 let instant_ten_ms =
454 Instant::<TestClock>::from_ticks(10 * <TestClock as Clock>::TICKS_PER_SEC / 1000);
455 let instant_nine_ms =
456 Instant::<TestClock>::from_ticks(9 * <TestClock as Clock>::TICKS_PER_SEC / 1000);
457
458 let duration_one_ms = Duration::<TestClock>::from_millis(1);
459
460 assert_eq!(
461 instant_ten_ms.checked_sub_duration(duration_one_ms),
462 Some(instant_nine_ms)
463 );
464
465 assert_eq!(
466 Instant::<TestClock>::MIN.checked_sub_duration(duration_one_ms),
467 None
468 );
469 }
470}