1#![cfg_attr(not(feature = "std"), no_std)]
2
3extern crate alloc;
4#[cfg(test)]
5extern crate approx;
6
7mod calibration;
8#[cfg_attr(target_arch = "aarch64", path = "aarch64.rs")]
9#[cfg_attr(all(target_os = "none", target_arch = "arm"), path = "cortexm.rs")]
10#[cfg_attr(target_arch = "riscv64", path = "riscv64.rs")]
11#[cfg_attr(
12 all(feature = "std", target_arch = "wasm32", target_os = "unknown"),
13 path = "wasm.rs"
14)]
15#[cfg_attr(target_arch = "x86_64", path = "x86_64.rs")]
16#[cfg_attr(
17 not(any(
18 target_arch = "x86_64",
19 target_arch = "aarch64",
20 all(target_os = "none", target_arch = "arm"),
21 target_arch = "riscv64",
22 all(feature = "std", target_arch = "wasm32", target_os = "unknown")
23 )),
24 path = "fallback.rs"
25)]
26mod raw_counter;
27
28pub use raw_counter::*;
29
30#[cfg(feature = "reflect")]
31use bevy_reflect::Reflect;
32use bincode::BorrowDecode;
33use bincode::de::BorrowDecoder;
34use bincode::de::Decoder;
35use bincode::enc::Encoder;
36use bincode::error::{DecodeError, EncodeError};
37use bincode::{Decode, Encode};
38use core::ops::{Add, Sub};
39use serde::{Deserialize, Serialize};
40
41use portable_atomic::{AtomicU64, Ordering};
43
44use alloc::format;
45use alloc::sync::Arc;
46use alloc::vec::Vec;
47use core::fmt::{Display, Formatter};
48use core::ops::{AddAssign, Div, Mul, SubAssign};
49
50#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
52pub struct CuInstant(u64);
53
54pub type Instant = CuInstant; impl CuInstant {
57 pub fn now() -> Self {
58 CuInstant(calibration::counter_to_nanos(read_raw_counter))
59 }
60
61 pub fn as_nanos(&self) -> u64 {
62 self.0
63 }
64}
65
66impl Sub for CuInstant {
67 type Output = CuDuration;
68
69 fn sub(self, other: CuInstant) -> CuDuration {
70 CuDuration(self.0.saturating_sub(other.0))
71 }
72}
73
74impl Sub<CuDuration> for CuInstant {
75 type Output = CuInstant;
76
77 fn sub(self, duration: CuDuration) -> CuInstant {
78 CuInstant(self.0.saturating_sub(duration.as_nanos()))
79 }
80}
81
82impl Add<CuDuration> for CuInstant {
83 type Output = CuInstant;
84
85 fn add(self, duration: CuDuration) -> CuInstant {
86 CuInstant(self.0.saturating_add(duration.as_nanos()))
87 }
88}
89
90#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
93#[cfg_attr(feature = "reflect", derive(Reflect))]
94pub struct CuDuration(pub u64);
95
96impl CuDuration {
97 pub const MIN: CuDuration = CuDuration(0u64);
99 pub const MAX: CuDuration = CuDuration(NONE_VALUE - 1);
101
102 pub fn max(self, other: CuDuration) -> CuDuration {
103 let Self(lhs) = self;
104 let Self(rhs) = other;
105 CuDuration(lhs.max(rhs))
106 }
107
108 pub fn min(self, other: CuDuration) -> CuDuration {
109 let Self(lhs) = self;
110 let Self(rhs) = other;
111 CuDuration(lhs.min(rhs))
112 }
113
114 pub fn as_nanos(&self) -> u64 {
115 let Self(nanos) = self;
116 *nanos
117 }
118
119 pub fn as_micros(&self) -> u64 {
120 let Self(nanos) = self;
121 nanos / 1_000
122 }
123
124 pub fn as_millis(&self) -> u64 {
125 let Self(nanos) = self;
126 nanos / 1_000_000
127 }
128
129 pub fn as_secs(&self) -> u64 {
130 let Self(nanos) = self;
131 nanos / 1_000_000_000
132 }
133
134 pub fn from_nanos(nanos: u64) -> Self {
135 CuDuration(nanos)
136 }
137
138 pub fn from_micros(micros: u64) -> Self {
139 CuDuration(micros * 1_000)
140 }
141
142 pub fn from_millis(millis: u64) -> Self {
143 CuDuration(millis * 1_000_000)
144 }
145
146 pub fn from_secs(secs: u64) -> Self {
147 CuDuration(secs * 1_000_000_000)
148 }
149}
150
151pub trait SaturatingSub {
153 fn saturating_sub(self, other: Self) -> Self;
154}
155
156impl SaturatingSub for CuDuration {
157 fn saturating_sub(self, other: Self) -> Self {
158 let Self(lhs) = self;
159 let Self(rhs) = other;
160 CuDuration(lhs.saturating_sub(rhs))
161 }
162}
163
164#[cfg(feature = "std")]
166impl From<std::time::Duration> for CuDuration {
167 fn from(duration: std::time::Duration) -> Self {
168 CuDuration(duration.as_nanos() as u64)
169 }
170}
171
172#[cfg(not(feature = "std"))]
173impl From<core::time::Duration> for CuDuration {
174 fn from(duration: core::time::Duration) -> Self {
175 CuDuration(duration.as_nanos() as u64)
176 }
177}
178
179#[cfg(feature = "std")]
180impl From<CuDuration> for std::time::Duration {
181 fn from(val: CuDuration) -> Self {
182 let CuDuration(nanos) = val;
183 std::time::Duration::from_nanos(nanos)
184 }
185}
186
187impl From<u64> for CuDuration {
188 fn from(duration: u64) -> Self {
189 CuDuration(duration)
190 }
191}
192
193impl From<CuDuration> for u64 {
194 fn from(val: CuDuration) -> Self {
195 let CuDuration(nanos) = val;
196 nanos
197 }
198}
199
200impl Sub for CuDuration {
201 type Output = Self;
202
203 fn sub(self, rhs: Self) -> Self::Output {
204 let CuDuration(lhs) = self;
205 let CuDuration(rhs) = rhs;
206 CuDuration(lhs - rhs)
207 }
208}
209
210impl Add for CuDuration {
211 type Output = Self;
212
213 fn add(self, rhs: Self) -> Self::Output {
214 let CuDuration(lhs) = self;
215 let CuDuration(rhs) = rhs;
216 CuDuration(lhs + rhs)
217 }
218}
219
220impl AddAssign for CuDuration {
221 fn add_assign(&mut self, rhs: Self) {
222 let CuDuration(lhs) = self;
223 let CuDuration(rhs) = rhs;
224 *lhs += rhs;
225 }
226}
227
228impl SubAssign for CuDuration {
229 fn sub_assign(&mut self, rhs: Self) {
230 let CuDuration(lhs) = self;
231 let CuDuration(rhs) = rhs;
232 *lhs -= rhs;
233 }
234}
235
236impl<T> Div<T> for CuDuration
239where
240 T: Into<u64>,
241{
242 type Output = Self;
243 fn div(self, rhs: T) -> Self {
244 let CuDuration(lhs) = self;
245 CuDuration(lhs / rhs.into())
246 }
247}
248
249impl<T> Mul<T> for CuDuration
253where
254 T: Into<u64>,
255{
256 type Output = CuDuration;
257
258 fn mul(self, rhs: T) -> CuDuration {
259 let CuDuration(lhs) = self;
260 CuDuration(lhs * rhs.into())
261 }
262}
263
264impl Mul<CuDuration> for u64 {
266 type Output = CuDuration;
267
268 fn mul(self, rhs: CuDuration) -> CuDuration {
269 let CuDuration(nanos) = rhs;
270 CuDuration(self * nanos)
271 }
272}
273
274impl Mul<CuDuration> for u32 {
276 type Output = CuDuration;
277
278 fn mul(self, rhs: CuDuration) -> CuDuration {
279 let CuDuration(nanos) = rhs;
280 CuDuration(self as u64 * nanos)
281 }
282}
283
284impl Mul<CuDuration> for i32 {
286 type Output = CuDuration;
287
288 fn mul(self, rhs: CuDuration) -> CuDuration {
289 let CuDuration(nanos) = rhs;
290 CuDuration(self as u64 * nanos)
291 }
292}
293
294impl Encode for CuDuration {
295 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
296 let CuDuration(nanos) = self;
297 nanos.encode(encoder)
298 }
299}
300
301impl<Context> Decode<Context> for CuDuration {
302 fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
303 Ok(CuDuration(u64::decode(decoder)?))
304 }
305}
306
307impl<'de, Context> BorrowDecode<'de, Context> for CuDuration {
308 fn borrow_decode<D: BorrowDecoder<'de>>(decoder: &mut D) -> Result<Self, DecodeError> {
309 Ok(CuDuration(u64::decode(decoder)?))
310 }
311}
312
313impl Display for CuDuration {
314 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
315 let Self(nanos) = *self;
316 if nanos >= 86_400_000_000_000 {
317 write!(f, "{:.3} d", nanos as f64 / 86_400_000_000_000.0)
318 } else if nanos >= 3_600_000_000_000 {
319 write!(f, "{:.3} h", nanos as f64 / 3_600_000_000_000.0)
320 } else if nanos >= 60_000_000_000 {
321 write!(f, "{:.3} m", nanos as f64 / 60_000_000_000.0)
322 } else if nanos >= 1_000_000_000 {
323 write!(f, "{:.3} s", nanos as f64 / 1_000_000_000.0)
324 } else if nanos >= 1_000_000 {
325 write!(f, "{:.3} ms", nanos as f64 / 1_000_000.0)
326 } else if nanos >= 1_000 {
327 write!(f, "{:.3} µs", nanos as f64 / 1_000.0)
328 } else {
329 write!(f, "{nanos} ns")
330 }
331 }
332}
333
334#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default)]
336#[cfg_attr(feature = "reflect", derive(Reflect))]
337#[repr(transparent)]
338pub struct CuTime(pub u64);
339
340impl CuTime {
341 pub const MIN: CuTime = CuTime(0u64);
342 pub const MAX: CuTime = CuTime(NONE_VALUE - 1);
343
344 pub fn max(self, other: CuTime) -> CuTime {
345 let Self(lhs) = self;
346 let Self(rhs) = other;
347 CuTime(lhs.max(rhs))
348 }
349
350 pub fn min(self, other: CuTime) -> CuTime {
351 let Self(lhs) = self;
352 let Self(rhs) = other;
353 CuTime(lhs.min(rhs))
354 }
355
356 pub fn as_nanos(&self) -> u64 {
357 let Self(nanos) = self;
358 *nanos
359 }
360
361 pub fn as_micros(&self) -> u64 {
362 self.as_nanos() / 1_000
363 }
364
365 pub fn as_millis(&self) -> u64 {
366 self.as_nanos() / 1_000_000
367 }
368
369 pub fn as_secs(&self) -> u64 {
370 self.as_nanos() / 1_000_000_000
371 }
372
373 pub fn from_nanos(nanos: u64) -> Self {
374 CuTime(nanos)
375 }
376
377 pub fn from_micros(micros: u64) -> Self {
378 CuTime::from(CuDuration::from_micros(micros))
379 }
380
381 pub fn from_millis(millis: u64) -> Self {
382 CuTime::from(CuDuration::from_millis(millis))
383 }
384
385 pub fn from_secs(secs: u64) -> Self {
386 CuTime::from(CuDuration::from_secs(secs))
387 }
388}
389
390impl SaturatingSub for CuTime {
391 fn saturating_sub(self, other: Self) -> Self {
392 let Self(lhs) = self;
393 let Self(rhs) = other;
394 CuTime(lhs.saturating_sub(rhs))
395 }
396}
397
398#[cfg(feature = "std")]
399impl From<std::time::Duration> for CuTime {
400 fn from(duration: std::time::Duration) -> Self {
401 CuTime::from(CuDuration::from(duration))
402 }
403}
404
405#[cfg(not(feature = "std"))]
406impl From<core::time::Duration> for CuTime {
407 fn from(duration: core::time::Duration) -> Self {
408 CuTime::from(CuDuration::from(duration))
409 }
410}
411
412#[cfg(feature = "std")]
413impl From<CuTime> for std::time::Duration {
414 fn from(val: CuTime) -> Self {
415 std::time::Duration::from_nanos(val.as_nanos())
416 }
417}
418
419impl From<u64> for CuTime {
420 fn from(time: u64) -> Self {
421 CuTime(time)
422 }
423}
424
425impl From<CuTime> for u64 {
426 fn from(val: CuTime) -> Self {
427 let CuTime(nanos) = val;
428 nanos
429 }
430}
431
432impl From<CuDuration> for CuTime {
433 fn from(duration: CuDuration) -> Self {
434 CuTime(duration.as_nanos())
435 }
436}
437
438impl From<CuTime> for CuDuration {
439 fn from(time: CuTime) -> Self {
440 CuDuration(time.as_nanos())
441 }
442}
443
444impl Add for CuTime {
445 type Output = Self;
446
447 fn add(self, rhs: Self) -> Self::Output {
448 CuTime(self.as_nanos().saturating_add(rhs.as_nanos()))
449 }
450}
451
452impl Add<CuDuration> for CuTime {
453 type Output = Self;
454
455 fn add(self, rhs: CuDuration) -> Self::Output {
456 CuTime(self.as_nanos().saturating_add(rhs.as_nanos()))
457 }
458}
459
460impl AddAssign<CuDuration> for CuTime {
461 fn add_assign(&mut self, rhs: CuDuration) {
462 *self = *self + rhs;
463 }
464}
465
466impl AddAssign for CuTime {
467 fn add_assign(&mut self, rhs: Self) {
468 *self = *self + rhs;
469 }
470}
471
472impl Sub<CuDuration> for CuTime {
473 type Output = Self;
474
475 fn sub(self, rhs: CuDuration) -> Self::Output {
476 CuTime(self.as_nanos().saturating_sub(rhs.as_nanos()))
477 }
478}
479
480impl SubAssign<CuDuration> for CuTime {
481 fn sub_assign(&mut self, rhs: CuDuration) {
482 *self = *self - rhs;
483 }
484}
485
486impl SubAssign for CuTime {
487 fn sub_assign(&mut self, rhs: Self) {
488 *self = CuTime(self.as_nanos().saturating_sub(rhs.as_nanos()));
489 }
490}
491
492impl Sub for CuTime {
493 type Output = CuDuration;
494
495 fn sub(self, rhs: Self) -> Self::Output {
496 CuDuration(self.as_nanos().saturating_sub(rhs.as_nanos()))
497 }
498}
499
500impl<T> Div<T> for CuTime
501where
502 T: Into<u64>,
503{
504 type Output = Self;
505
506 fn div(self, rhs: T) -> Self::Output {
507 CuTime(self.as_nanos() / rhs.into())
508 }
509}
510
511impl<T> Mul<T> for CuTime
512where
513 T: Into<u64>,
514{
515 type Output = Self;
516
517 fn mul(self, rhs: T) -> Self::Output {
518 CuTime(self.as_nanos() * rhs.into())
519 }
520}
521
522impl Mul<CuTime> for u64 {
523 type Output = CuTime;
524
525 fn mul(self, rhs: CuTime) -> Self::Output {
526 CuTime(self * rhs.as_nanos())
527 }
528}
529
530impl Mul<CuTime> for u32 {
531 type Output = CuTime;
532
533 fn mul(self, rhs: CuTime) -> Self::Output {
534 CuTime(self as u64 * rhs.as_nanos())
535 }
536}
537
538impl Mul<CuTime> for i32 {
539 type Output = CuTime;
540
541 fn mul(self, rhs: CuTime) -> Self::Output {
542 CuTime(self as u64 * rhs.as_nanos())
543 }
544}
545
546impl Encode for CuTime {
547 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
548 self.as_nanos().encode(encoder)
549 }
550}
551
552impl<Context> Decode<Context> for CuTime {
553 fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
554 Ok(CuTime(u64::decode(decoder)?))
555 }
556}
557
558impl<'de, Context> BorrowDecode<'de, Context> for CuTime {
559 fn borrow_decode<D: BorrowDecoder<'de>>(decoder: &mut D) -> Result<Self, DecodeError> {
560 Ok(CuTime(u64::decode(decoder)?))
561 }
562}
563
564impl Display for CuTime {
565 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
566 CuDuration(self.as_nanos()).fmt(f)
567 }
568}
569
570#[inline(always)]
573pub fn busy_wait_for(duration: CuDuration) {
574 busy_wait_until(CuInstant::now() + duration);
575}
576
577#[inline(always)]
580pub fn busy_wait_until(time: CuInstant) {
581 while CuInstant::now() < time {
582 core::hint::spin_loop();
583 }
584}
585
586#[derive(Copy, Clone, Debug, PartialEq, Encode, Decode, Serialize, Deserialize)]
588#[cfg_attr(feature = "reflect", derive(Reflect))]
589pub struct OptionCuTime(CuTime);
590
591const NONE_VALUE: u64 = 0xFFFFFFFFFFFFFFFF;
592
593impl OptionCuTime {
594 pub const NONE_SENTINEL_NANOS: u64 = NONE_VALUE;
595
596 #[inline]
597 pub fn is_none(&self) -> bool {
598 let Self(CuTime(nanos)) = self;
599 *nanos == NONE_VALUE
600 }
601
602 #[inline]
603 pub const fn none() -> Self {
604 OptionCuTime(CuTime(NONE_VALUE))
605 }
606
607 #[inline]
608 pub fn unwrap(self) -> CuTime {
609 if self.is_none() {
610 panic!("called `OptionCuTime::unwrap()` on a `None` value");
611 }
612 self.0
613 }
614}
615
616impl Display for OptionCuTime {
617 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
618 if self.is_none() {
619 write!(f, "None")
620 } else {
621 write!(f, "{}", self.0)
622 }
623 }
624}
625
626impl Default for OptionCuTime {
627 fn default() -> Self {
628 Self::none()
629 }
630}
631
632impl From<Option<CuTime>> for OptionCuTime {
633 #[inline]
634 fn from(duration: Option<CuTime>) -> Self {
635 match duration {
636 Some(duration) => OptionCuTime(duration),
637 None => OptionCuTime(CuTime(NONE_VALUE)),
638 }
639 }
640}
641
642impl From<OptionCuTime> for Option<CuTime> {
643 #[inline]
644 fn from(val: OptionCuTime) -> Self {
645 let OptionCuTime(CuTime(nanos)) = val;
646 if nanos == NONE_VALUE {
647 None
648 } else {
649 Some(CuTime(nanos))
650 }
651 }
652}
653
654impl From<CuTime> for OptionCuTime {
655 #[inline]
656 fn from(val: CuTime) -> Self {
657 Some(val).into()
658 }
659}
660
661#[derive(Debug, Clone, Copy, PartialEq, Eq)]
662pub enum ClockDebugScalarKind {
663 Time,
664 OptionalTime,
665 Duration,
666}
667
668#[derive(Debug, Clone, Copy, PartialEq, Eq)]
669pub struct ClockDebugScalarRegistration {
670 pub type_path: &'static str,
671 pub kind: ClockDebugScalarKind,
672}
673
674pub fn debug_scalar_registrations() -> Vec<ClockDebugScalarRegistration> {
675 alloc::vec![
676 ClockDebugScalarRegistration {
677 type_path: core::any::type_name::<CuDuration>(),
678 kind: ClockDebugScalarKind::Duration,
679 },
680 ClockDebugScalarRegistration {
681 type_path: core::any::type_name::<CuTime>(),
682 kind: ClockDebugScalarKind::Time,
683 },
684 ClockDebugScalarRegistration {
685 type_path: core::any::type_name::<OptionCuTime>(),
686 kind: ClockDebugScalarKind::OptionalTime,
687 },
688 ]
689}
690
691#[derive(Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize, PartialEq)]
693#[cfg_attr(feature = "reflect", derive(Reflect))]
694pub struct CuTimeRange {
695 pub start: CuTime,
696 pub end: CuTime,
697}
698
699impl From<&[CuTime]> for CuTimeRange {
702 fn from(slice: &[CuTime]) -> Self {
703 CuTimeRange {
704 start: *slice.iter().min().expect("Empty slice"),
705 end: *slice.iter().max().expect("Empty slice"),
706 }
707 }
708}
709
710#[derive(Default, Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize)]
712#[cfg_attr(feature = "reflect", derive(Reflect))]
713pub struct PartialCuTimeRange {
714 pub start: OptionCuTime,
715 pub end: OptionCuTime,
716}
717
718impl Display for PartialCuTimeRange {
719 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
720 let start = if self.start.is_none() {
721 "…"
722 } else {
723 &format!("{}", self.start)
724 };
725 let end = if self.end.is_none() {
726 "…"
727 } else {
728 &format!("{}", self.end)
729 };
730 write!(f, "[{start} – {end}]")
731 }
732}
733
734#[derive(Default, Clone, Debug, PartialEq, Encode, Decode, Serialize, Deserialize, Copy)]
737#[cfg_attr(feature = "reflect", derive(Reflect))]
738pub enum Tov {
739 #[default]
740 None,
741 Time(CuTime),
742 Range(CuTimeRange),
743}
744
745impl Display for Tov {
746 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
747 match self {
748 Tov::None => write!(f, "None"),
749 Tov::Time(t) => write!(f, "{t}"),
750 Tov::Range(r) => write!(f, "[{} – {}]", r.start, r.end),
751 }
752 }
753}
754
755impl From<Option<CuDuration>> for Tov {
756 fn from(duration: Option<CuDuration>) -> Self {
757 duration.map(CuTime::from).map_or(Tov::None, Tov::Time)
758 }
759}
760
761impl From<Option<CuTime>> for Tov {
762 fn from(time: Option<CuTime>) -> Self {
763 time.map_or(Tov::None, Tov::Time)
764 }
765}
766
767impl From<CuDuration> for Tov {
768 fn from(duration: CuDuration) -> Self {
769 Tov::Time(duration.into())
770 }
771}
772
773impl From<CuTime> for Tov {
774 fn from(time: CuTime) -> Self {
775 Tov::Time(time)
776 }
777}
778
779#[derive(Clone, Debug)]
781struct InternalClock {
782 mock_state: Option<Arc<AtomicU64>>,
785}
786
787#[cfg(all(
789 feature = "std",
790 not(all(target_arch = "wasm32", target_os = "unknown"))
791))]
792#[inline(always)]
793fn read_rtc_ns() -> u64 {
794 std::time::SystemTime::now()
795 .duration_since(std::time::UNIX_EPOCH)
796 .unwrap()
797 .as_nanos() as u64
798}
799
800#[cfg(all(feature = "std", target_arch = "wasm32", target_os = "unknown"))]
801#[inline(always)]
802fn read_rtc_ns() -> u64 {
803 read_raw_counter()
804}
805
806#[cfg(all(
807 feature = "std",
808 not(all(target_arch = "wasm32", target_os = "unknown"))
809))]
810#[inline(always)]
811fn sleep_ns(ns: u64) {
812 std::thread::sleep(std::time::Duration::from_nanos(ns));
813}
814
815#[cfg(all(feature = "std", target_arch = "wasm32", target_os = "unknown"))]
816#[inline(always)]
817fn sleep_ns(ns: u64) {
818 let start = read_raw_counter();
819 while read_raw_counter().saturating_sub(start) < ns {
820 core::hint::spin_loop();
821 }
822}
823
824impl InternalClock {
825 fn new(
826 read_rtc_ns: impl Fn() -> u64 + Send + Sync + 'static,
827 sleep_ns: impl Fn(u64) + Send + Sync + 'static,
828 ) -> Self {
829 initialize();
830
831 calibration::calibrate(read_raw_counter, read_rtc_ns, sleep_ns);
833 InternalClock { mock_state: None }
834 }
835
836 fn mock() -> (Self, Arc<AtomicU64>) {
837 let mock_state = Arc::new(AtomicU64::new(0));
838 let clock = InternalClock {
839 mock_state: Some(Arc::clone(&mock_state)),
840 };
841 (clock, mock_state)
842 }
843
844 fn now(&self) -> CuInstant {
845 if let Some(ref mock_state) = self.mock_state {
846 CuInstant(mock_state.load(Ordering::Relaxed))
847 } else {
848 CuInstant::now()
849 }
850 }
851
852 fn recent(&self) -> CuInstant {
853 self.now()
856 }
857}
858
859#[derive(Clone, Debug)]
863pub struct RobotClock {
864 inner: InternalClock,
865 ref_time: CuInstant,
866}
867
868#[derive(Debug, Clone)]
870pub struct RobotClockMock(Arc<AtomicU64>);
871
872impl RobotClockMock {
873 pub fn increment(&self, amount: CuDuration) {
874 let Self(mock_state) = self;
875 mock_state.fetch_add(amount.as_nanos(), Ordering::Relaxed);
876 }
877
878 pub fn decrement(&self, amount: CuDuration) {
881 let Self(mock_state) = self;
882 mock_state.fetch_sub(amount.as_nanos(), Ordering::Relaxed);
883 }
884
885 pub fn value(&self) -> u64 {
887 let Self(mock_state) = self;
888 mock_state.load(Ordering::Relaxed)
889 }
890
891 pub fn now(&self) -> CuTime {
893 let Self(mock_state) = self;
894 CuTime(mock_state.load(Ordering::Relaxed))
895 }
896
897 pub fn set_value(&self, value: u64) {
899 let Self(mock_state) = self;
900 mock_state.store(value, Ordering::Relaxed);
901 }
902}
903
904impl RobotClock {
905 #[cfg(feature = "std")]
909 pub fn new() -> Self {
910 let clock = InternalClock::new(read_rtc_ns, sleep_ns);
911 let ref_time = clock.now();
912 RobotClock {
913 inner: clock,
914 ref_time,
915 }
916 }
917
918 pub fn new_with_rtc(
921 read_rtc_ns: impl Fn() -> u64 + Send + Sync + 'static,
922 sleep_ns: impl Fn(u64) + Send + Sync + 'static,
923 ) -> Self {
924 let clock = InternalClock::new(read_rtc_ns, sleep_ns);
925 let ref_time = clock.now();
926 RobotClock {
927 inner: clock,
928 ref_time,
929 }
930 }
931
932 #[cfg(feature = "std")]
934 pub fn from_ref_time(ref_time_ns: u64) -> Self {
935 let clock = InternalClock::new(read_rtc_ns, sleep_ns);
936 let ref_time = clock.now() - CuDuration(ref_time_ns);
937 RobotClock {
938 inner: clock,
939 ref_time,
940 }
941 }
942
943 pub fn from_ref_time_with_rtc(
945 read_rtc_ns: fn() -> u64,
946 sleep_ns: fn(u64),
947 ref_time_ns: u64,
948 ) -> Self {
949 let clock = InternalClock::new(read_rtc_ns, sleep_ns);
950 let ref_time = clock.now() - CuDuration(ref_time_ns);
951 RobotClock {
952 inner: clock,
953 ref_time,
954 }
955 }
956
957 pub fn mock() -> (Self, RobotClockMock) {
960 let (clock, mock_state) = InternalClock::mock();
961 let ref_time = clock.now();
962 (
963 RobotClock {
964 inner: clock,
965 ref_time,
966 },
967 RobotClockMock(mock_state),
968 )
969 }
970
971 #[inline]
974 pub fn now(&self) -> CuTime {
975 CuTime::from_nanos((self.inner.now() - self.ref_time).as_nanos())
976 }
977
978 #[inline]
980 pub fn recent(&self) -> CuTime {
981 CuTime::from_nanos((self.inner.recent() - self.ref_time).as_nanos())
982 }
983}
984
985#[cfg(feature = "std")]
988impl Default for RobotClock {
989 fn default() -> Self {
990 Self::new()
991 }
992}
993
994pub trait ClockProvider {
996 fn get_clock(&self) -> RobotClock;
997}
998
999#[cfg(test)]
1000mod tests {
1001 use super::*;
1002 use approx::assert_relative_eq;
1003
1004 #[test]
1005 fn test_cuduration_comparison_operators() {
1006 let a = CuDuration(100);
1007 let b = CuDuration(200);
1008
1009 assert!(a < b);
1010 assert!(b > a);
1011 assert_ne!(a, b);
1012 assert_eq!(a, CuDuration(100));
1013 }
1014
1015 #[test]
1016 fn test_cuduration_arithmetic_operations() {
1017 let a = CuDuration(100);
1018 let b = CuDuration(50);
1019
1020 assert_eq!(a + b, CuDuration(150));
1021 assert_eq!(a - b, CuDuration(50));
1022 assert_eq!(a * 2u32, CuDuration(200));
1023 assert_eq!(a / 2u32, CuDuration(50));
1024 }
1025
1026 #[test]
1027 fn test_robot_clock_monotonic() {
1028 let clock = RobotClock::new();
1029 let t1 = clock.now();
1030 let t2 = clock.now();
1031 assert!(t2 >= t1);
1032 }
1033
1034 #[test]
1035 fn test_robot_clock_mock() {
1036 let (clock, mock) = RobotClock::mock();
1037 let t1 = clock.now();
1038 mock.increment(CuDuration::from_millis(100));
1039 let t2 = clock.now();
1040 assert!(t2 > t1);
1041 assert_eq!(t2 - t1, CuDuration(100_000_000)); }
1043
1044 #[test]
1045 fn test_robot_clock_clone_consistency() {
1046 let (clock1, mock) = RobotClock::mock();
1047 let clock2 = clock1.clone();
1048
1049 mock.set_value(1_000_000_000); assert_eq!(clock1.now(), clock2.now());
1051 }
1052
1053 #[test]
1054 fn test_from_ref_time() {
1055 let tolerance_ms = 10f64;
1056 let clock = RobotClock::from_ref_time(1_000_000_000);
1057 assert_relative_eq!(
1058 clock.now().as_millis() as f64,
1059 CuDuration::from_secs(1).as_millis() as f64,
1060 epsilon = tolerance_ms
1061 );
1062 }
1063
1064 #[test]
1065 fn longest_duration() {
1066 let maxcu = CuDuration(u64::MAX);
1067 assert_eq!(maxcu.as_nanos(), u64::MAX);
1068 let s = maxcu.as_secs();
1069 let y = s / 60 / 60 / 24 / 365;
1070 assert!(y >= 584); }
1072
1073 #[test]
1074 fn test_some_time_arithmetics() {
1075 let a: CuDuration = 10.into();
1076 let b: CuDuration = 20.into();
1077 let c = a + b;
1078 assert_eq!(c.0, 30);
1079 let d = b - a;
1080 assert_eq!(d.0, 10);
1081 }
1082
1083 #[test]
1084 fn test_build_range_from_slice() {
1085 let range = CuTimeRange::from(&[20.into(), 10.into(), 30.into()][..]);
1086 assert_eq!(range.start, 10.into());
1087 assert_eq!(range.end, 30.into());
1088 }
1089
1090 #[test]
1091 fn test_time_range_operations() {
1092 let start = CuTime::from(100u64);
1094 let end = CuTime::from(200u64);
1095 let range = CuTimeRange { start, end };
1096
1097 assert_eq!(range.start, start);
1098 assert_eq!(range.end, end);
1099
1100 let times = [
1102 CuTime::from(150u64),
1103 CuTime::from(120u64),
1104 CuTime::from(180u64),
1105 ];
1106 let range_from_slice = CuTimeRange::from(×[..]);
1107
1108 assert_eq!(range_from_slice.start, CuTime::from(120u64));
1110 assert_eq!(range_from_slice.end, CuTime::from(180u64));
1111 }
1112
1113 #[test]
1114 fn test_partial_time_range() {
1115 let start = CuTime::from(100u64);
1117 let end = CuTime::from(200u64);
1118
1119 let partial_range = PartialCuTimeRange {
1120 start: OptionCuTime::from(start),
1121 end: OptionCuTime::from(end),
1122 };
1123
1124 let opt_start: Option<CuTime> = partial_range.start.into();
1126 let opt_end: Option<CuTime> = partial_range.end.into();
1127
1128 assert_eq!(opt_start, Some(start));
1129 assert_eq!(opt_end, Some(end));
1130
1131 let partial_undefined = PartialCuTimeRange::default();
1133 assert!(partial_undefined.start.is_none());
1134 assert!(partial_undefined.end.is_none());
1135 }
1136
1137 #[test]
1138 fn test_tov_conversions() {
1139 let time = CuTime::from(100u64);
1141
1142 let tov_time: Tov = time.into();
1144 assert!(matches!(tov_time, Tov::Time(_)));
1145
1146 if let Tov::Time(t) = tov_time {
1147 assert_eq!(t, time);
1148 }
1149
1150 let some_time = Some(time);
1152 let tov_some: Tov = some_time.into();
1153 assert!(matches!(tov_some, Tov::Time(_)));
1154
1155 let none_time: Option<CuDuration> = None;
1156 let tov_none: Tov = none_time.into();
1157 assert!(matches!(tov_none, Tov::None));
1158
1159 let start = CuTime::from(100u64);
1161 let end = CuTime::from(200u64);
1162 let range = CuTimeRange { start, end };
1163 let tov_range = Tov::Range(range);
1164
1165 assert!(matches!(tov_range, Tov::Range(_)));
1166 }
1167
1168 #[cfg(feature = "std")]
1169 #[test]
1170 fn test_cuduration_display() {
1171 let nano = CuDuration(42);
1173 assert_eq!(nano.to_string(), "42 ns");
1174
1175 let micro = CuDuration(42_000);
1176 assert_eq!(micro.to_string(), "42.000 µs");
1177
1178 let milli = CuDuration(42_000_000);
1179 assert_eq!(milli.to_string(), "42.000 ms");
1180
1181 let sec = CuDuration(1_500_000_000);
1182 assert_eq!(sec.to_string(), "1.500 s");
1183
1184 let min = CuDuration(90_000_000_000);
1185 assert_eq!(min.to_string(), "1.500 m");
1186
1187 let hour = CuDuration(3_600_000_000_000);
1188 assert_eq!(hour.to_string(), "1.000 h");
1189
1190 let day = CuDuration(86_400_000_000_000);
1191 assert_eq!(day.to_string(), "1.000 d");
1192 }
1193
1194 #[test]
1195 fn test_robot_clock_precision() {
1196 let clock = RobotClock::new();
1199
1200 let recent = clock.recent();
1202 let now = clock.now();
1203
1204 assert!(recent <= now);
1206
1207 let ref_time_ns = 1_000_000_000; let clock = RobotClock::from_ref_time(ref_time_ns);
1210
1211 let now = clock.now();
1213 let now_ns: u64 = now.into();
1214
1215 let tolerance_ns = 50_000_000; assert!(now_ns >= ref_time_ns);
1218 assert!(now_ns < ref_time_ns + tolerance_ns);
1219 }
1220
1221 #[test]
1222 fn test_mock_clock_advanced_operations() {
1223 let (clock, mock) = RobotClock::mock();
1225
1226 assert_eq!(clock.now(), CuTime::from(0));
1228
1229 mock.increment(CuDuration::from_secs(10));
1231 assert_eq!(
1232 clock.now(),
1233 CuTime::from_nanos(CuDuration::from_secs(10).as_nanos())
1234 );
1235
1236 mock.decrement(CuDuration::from_secs(5));
1238 assert_eq!(
1239 clock.now(),
1240 CuTime::from_nanos(CuDuration::from_secs(5).as_nanos())
1241 );
1242
1243 mock.set_value(30_000_000_000); assert_eq!(
1246 clock.now(),
1247 CuTime::from_nanos(CuDuration::from_secs(30).as_nanos())
1248 );
1249
1250 assert_eq!(
1252 mock.now(),
1253 CuTime::from_nanos(CuDuration::from_secs(30).as_nanos())
1254 );
1255 assert_eq!(mock.value(), 30_000_000_000);
1256 }
1257
1258 #[test]
1259 fn test_cuduration_min_max() {
1260 assert_eq!(CuDuration::MIN, CuDuration(0));
1262
1263 let a = CuDuration(100);
1265 let b = CuDuration(200);
1266
1267 assert_eq!(a.min(b), a);
1268 assert_eq!(a.max(b), b);
1269 assert_eq!(b.min(a), a);
1270 assert_eq!(b.max(a), b);
1271
1272 assert_eq!(a.min(a), a);
1274 assert_eq!(a.max(a), a);
1275
1276 assert_eq!(a.min(CuDuration::MIN), CuDuration::MIN);
1278 assert_eq!(a.max(CuDuration::MAX), CuDuration::MAX);
1279 }
1280
1281 #[test]
1282 fn test_clock_provider_trait() {
1283 struct TestClockProvider {
1285 clock: RobotClock,
1286 }
1287
1288 impl ClockProvider for TestClockProvider {
1289 fn get_clock(&self) -> RobotClock {
1290 self.clock.clone()
1291 }
1292 }
1293
1294 let (clock, mock) = RobotClock::mock();
1296 let provider = TestClockProvider { clock };
1297
1298 let provider_clock = provider.get_clock();
1300 assert_eq!(provider_clock.now(), CuTime::from(0));
1301
1302 mock.increment(CuDuration::from_secs(5));
1304 assert_eq!(
1305 provider_clock.now(),
1306 CuTime::from_nanos(CuDuration::from_secs(5).as_nanos())
1307 );
1308 }
1309}