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