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 field_type: &'static str,
672 pub kind: ClockDebugScalarKind,
673}
674
675pub fn debug_scalar_registrations() -> Vec<ClockDebugScalarRegistration> {
676 alloc::vec![
677 ClockDebugScalarRegistration {
678 type_path: core::any::type_name::<CuDuration>(),
679 field_type: "integer",
680 kind: ClockDebugScalarKind::Duration,
681 },
682 ClockDebugScalarRegistration {
683 type_path: core::any::type_name::<CuTime>(),
684 field_type: "integer",
685 kind: ClockDebugScalarKind::Time,
686 },
687 ClockDebugScalarRegistration {
688 type_path: core::any::type_name::<OptionCuTime>(),
689 field_type: "integer",
690 kind: ClockDebugScalarKind::OptionalTime,
691 },
692 ]
693}
694
695#[derive(Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize, PartialEq)]
697#[cfg_attr(feature = "reflect", derive(Reflect))]
698pub struct CuTimeRange {
699 pub start: CuTime,
700 pub end: CuTime,
701}
702
703impl From<&[CuTime]> for CuTimeRange {
706 fn from(slice: &[CuTime]) -> Self {
707 CuTimeRange {
708 start: *slice.iter().min().expect("Empty slice"),
709 end: *slice.iter().max().expect("Empty slice"),
710 }
711 }
712}
713
714#[derive(Default, Copy, Clone, Debug, Encode, Decode, Serialize, Deserialize)]
716#[cfg_attr(feature = "reflect", derive(Reflect))]
717pub struct PartialCuTimeRange {
718 pub start: OptionCuTime,
719 pub end: OptionCuTime,
720}
721
722impl Display for PartialCuTimeRange {
723 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
724 let start = if self.start.is_none() {
725 "…"
726 } else {
727 &format!("{}", self.start)
728 };
729 let end = if self.end.is_none() {
730 "…"
731 } else {
732 &format!("{}", self.end)
733 };
734 write!(f, "[{start} – {end}]")
735 }
736}
737
738#[derive(Default, Clone, Debug, PartialEq, Encode, Decode, Serialize, Deserialize, Copy)]
741#[cfg_attr(feature = "reflect", derive(Reflect))]
742pub enum Tov {
743 #[default]
744 None,
745 Time(CuTime),
746 Range(CuTimeRange),
747}
748
749impl Display for Tov {
750 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
751 match self {
752 Tov::None => write!(f, "None"),
753 Tov::Time(t) => write!(f, "{t}"),
754 Tov::Range(r) => write!(f, "[{} – {}]", r.start, r.end),
755 }
756 }
757}
758
759impl From<Option<CuDuration>> for Tov {
760 fn from(duration: Option<CuDuration>) -> Self {
761 duration.map(CuTime::from).map_or(Tov::None, Tov::Time)
762 }
763}
764
765impl From<Option<CuTime>> for Tov {
766 fn from(time: Option<CuTime>) -> Self {
767 time.map_or(Tov::None, Tov::Time)
768 }
769}
770
771impl From<CuDuration> for Tov {
772 fn from(duration: CuDuration) -> Self {
773 Tov::Time(duration.into())
774 }
775}
776
777impl From<CuTime> for Tov {
778 fn from(time: CuTime) -> Self {
779 Tov::Time(time)
780 }
781}
782
783#[derive(Clone, Debug)]
785struct InternalClock {
786 mock_state: Option<Arc<AtomicU64>>,
789}
790
791#[cfg(all(
793 feature = "std",
794 not(all(target_arch = "wasm32", target_os = "unknown"))
795))]
796#[inline(always)]
797fn read_rtc_ns() -> u64 {
798 std::time::SystemTime::now()
799 .duration_since(std::time::UNIX_EPOCH)
800 .unwrap()
801 .as_nanos() as u64
802}
803
804#[cfg(all(feature = "std", target_arch = "wasm32", target_os = "unknown"))]
805#[inline(always)]
806fn read_rtc_ns() -> u64 {
807 read_raw_counter()
808}
809
810#[cfg(all(
811 feature = "std",
812 not(all(target_arch = "wasm32", target_os = "unknown"))
813))]
814#[inline(always)]
815fn sleep_ns(ns: u64) {
816 std::thread::sleep(std::time::Duration::from_nanos(ns));
817}
818
819#[cfg(all(feature = "std", target_arch = "wasm32", target_os = "unknown"))]
820#[inline(always)]
821fn sleep_ns(ns: u64) {
822 let start = read_raw_counter();
823 while read_raw_counter().saturating_sub(start) < ns {
824 core::hint::spin_loop();
825 }
826}
827
828impl InternalClock {
829 fn new(
830 read_rtc_ns: impl Fn() -> u64 + Send + Sync + 'static,
831 sleep_ns: impl Fn(u64) + Send + Sync + 'static,
832 ) -> Self {
833 initialize();
834
835 calibration::calibrate(read_raw_counter, read_rtc_ns, sleep_ns);
837 InternalClock { mock_state: None }
838 }
839
840 fn mock() -> (Self, Arc<AtomicU64>) {
841 let mock_state = Arc::new(AtomicU64::new(0));
842 let clock = InternalClock {
843 mock_state: Some(Arc::clone(&mock_state)),
844 };
845 (clock, mock_state)
846 }
847
848 fn now(&self) -> CuInstant {
849 if let Some(ref mock_state) = self.mock_state {
850 CuInstant(mock_state.load(Ordering::Relaxed))
851 } else {
852 CuInstant::now()
853 }
854 }
855
856 fn recent(&self) -> CuInstant {
857 self.now()
860 }
861}
862
863#[derive(Clone, Debug)]
867pub struct RobotClock {
868 inner: InternalClock,
869 ref_time: CuInstant,
870}
871
872#[derive(Debug, Clone)]
874pub struct RobotClockMock(Arc<AtomicU64>);
875
876impl RobotClockMock {
877 pub fn increment(&self, amount: CuDuration) {
878 let Self(mock_state) = self;
879 mock_state.fetch_add(amount.as_nanos(), Ordering::Relaxed);
880 }
881
882 pub fn decrement(&self, amount: CuDuration) {
885 let Self(mock_state) = self;
886 mock_state.fetch_sub(amount.as_nanos(), Ordering::Relaxed);
887 }
888
889 pub fn value(&self) -> u64 {
891 let Self(mock_state) = self;
892 mock_state.load(Ordering::Relaxed)
893 }
894
895 pub fn now(&self) -> CuTime {
897 let Self(mock_state) = self;
898 CuTime(mock_state.load(Ordering::Relaxed))
899 }
900
901 pub fn set_value(&self, value: u64) {
903 let Self(mock_state) = self;
904 mock_state.store(value, Ordering::Relaxed);
905 }
906}
907
908impl RobotClock {
909 #[cfg(feature = "std")]
913 pub fn new() -> Self {
914 let clock = InternalClock::new(read_rtc_ns, sleep_ns);
915 let ref_time = clock.now();
916 RobotClock {
917 inner: clock,
918 ref_time,
919 }
920 }
921
922 pub fn new_with_rtc(
925 read_rtc_ns: impl Fn() -> u64 + Send + Sync + 'static,
926 sleep_ns: impl Fn(u64) + Send + Sync + 'static,
927 ) -> Self {
928 let clock = InternalClock::new(read_rtc_ns, sleep_ns);
929 let ref_time = clock.now();
930 RobotClock {
931 inner: clock,
932 ref_time,
933 }
934 }
935
936 #[cfg(feature = "std")]
938 pub fn from_ref_time(ref_time_ns: u64) -> Self {
939 let clock = InternalClock::new(read_rtc_ns, sleep_ns);
940 let ref_time = clock.now() - CuDuration(ref_time_ns);
941 RobotClock {
942 inner: clock,
943 ref_time,
944 }
945 }
946
947 pub fn from_ref_time_with_rtc(
949 read_rtc_ns: fn() -> u64,
950 sleep_ns: fn(u64),
951 ref_time_ns: u64,
952 ) -> Self {
953 let clock = InternalClock::new(read_rtc_ns, sleep_ns);
954 let ref_time = clock.now() - CuDuration(ref_time_ns);
955 RobotClock {
956 inner: clock,
957 ref_time,
958 }
959 }
960
961 pub fn mock() -> (Self, RobotClockMock) {
964 let (clock, mock_state) = InternalClock::mock();
965 let ref_time = clock.now();
966 (
967 RobotClock {
968 inner: clock,
969 ref_time,
970 },
971 RobotClockMock(mock_state),
972 )
973 }
974
975 #[inline]
978 pub fn now(&self) -> CuTime {
979 CuTime::from_nanos((self.inner.now() - self.ref_time).as_nanos())
980 }
981
982 #[inline]
984 pub fn recent(&self) -> CuTime {
985 CuTime::from_nanos((self.inner.recent() - self.ref_time).as_nanos())
986 }
987}
988
989#[cfg(feature = "std")]
992impl Default for RobotClock {
993 fn default() -> Self {
994 Self::new()
995 }
996}
997
998pub trait ClockProvider {
1000 fn get_clock(&self) -> RobotClock;
1001}
1002
1003#[cfg(test)]
1004mod tests {
1005 use super::*;
1006 use approx::assert_relative_eq;
1007
1008 #[test]
1009 fn test_cuduration_comparison_operators() {
1010 let a = CuDuration(100);
1011 let b = CuDuration(200);
1012
1013 assert!(a < b);
1014 assert!(b > a);
1015 assert_ne!(a, b);
1016 assert_eq!(a, CuDuration(100));
1017 }
1018
1019 #[test]
1020 fn test_cuduration_arithmetic_operations() {
1021 let a = CuDuration(100);
1022 let b = CuDuration(50);
1023
1024 assert_eq!(a + b, CuDuration(150));
1025 assert_eq!(a - b, CuDuration(50));
1026 assert_eq!(a * 2u32, CuDuration(200));
1027 assert_eq!(a / 2u32, CuDuration(50));
1028 }
1029
1030 #[test]
1031 fn test_robot_clock_monotonic() {
1032 let clock = RobotClock::new();
1033 let t1 = clock.now();
1034 let t2 = clock.now();
1035 assert!(t2 >= t1);
1036 }
1037
1038 #[test]
1039 fn test_robot_clock_mock() {
1040 let (clock, mock) = RobotClock::mock();
1041 let t1 = clock.now();
1042 mock.increment(CuDuration::from_millis(100));
1043 let t2 = clock.now();
1044 assert!(t2 > t1);
1045 assert_eq!(t2 - t1, CuDuration(100_000_000)); }
1047
1048 #[test]
1049 fn test_robot_clock_clone_consistency() {
1050 let (clock1, mock) = RobotClock::mock();
1051 let clock2 = clock1.clone();
1052
1053 mock.set_value(1_000_000_000); assert_eq!(clock1.now(), clock2.now());
1055 }
1056
1057 #[test]
1058 fn test_from_ref_time() {
1059 let tolerance_ms = 10f64;
1060 let clock = RobotClock::from_ref_time(1_000_000_000);
1061 assert_relative_eq!(
1062 clock.now().as_millis() as f64,
1063 CuDuration::from_secs(1).as_millis() as f64,
1064 epsilon = tolerance_ms
1065 );
1066 }
1067
1068 #[test]
1069 fn longest_duration() {
1070 let maxcu = CuDuration(u64::MAX);
1071 assert_eq!(maxcu.as_nanos(), u64::MAX);
1072 let s = maxcu.as_secs();
1073 let y = s / 60 / 60 / 24 / 365;
1074 assert!(y >= 584); }
1076
1077 #[test]
1078 fn test_some_time_arithmetics() {
1079 let a: CuDuration = 10.into();
1080 let b: CuDuration = 20.into();
1081 let c = a + b;
1082 assert_eq!(c.0, 30);
1083 let d = b - a;
1084 assert_eq!(d.0, 10);
1085 }
1086
1087 #[test]
1088 fn test_build_range_from_slice() {
1089 let range = CuTimeRange::from(&[20.into(), 10.into(), 30.into()][..]);
1090 assert_eq!(range.start, 10.into());
1091 assert_eq!(range.end, 30.into());
1092 }
1093
1094 #[test]
1095 fn test_time_range_operations() {
1096 let start = CuTime::from(100u64);
1098 let end = CuTime::from(200u64);
1099 let range = CuTimeRange { start, end };
1100
1101 assert_eq!(range.start, start);
1102 assert_eq!(range.end, end);
1103
1104 let times = [
1106 CuTime::from(150u64),
1107 CuTime::from(120u64),
1108 CuTime::from(180u64),
1109 ];
1110 let range_from_slice = CuTimeRange::from(×[..]);
1111
1112 assert_eq!(range_from_slice.start, CuTime::from(120u64));
1114 assert_eq!(range_from_slice.end, CuTime::from(180u64));
1115 }
1116
1117 #[test]
1118 fn test_partial_time_range() {
1119 let start = CuTime::from(100u64);
1121 let end = CuTime::from(200u64);
1122
1123 let partial_range = PartialCuTimeRange {
1124 start: OptionCuTime::from(start),
1125 end: OptionCuTime::from(end),
1126 };
1127
1128 let opt_start: Option<CuTime> = partial_range.start.into();
1130 let opt_end: Option<CuTime> = partial_range.end.into();
1131
1132 assert_eq!(opt_start, Some(start));
1133 assert_eq!(opt_end, Some(end));
1134
1135 let partial_undefined = PartialCuTimeRange::default();
1137 assert!(partial_undefined.start.is_none());
1138 assert!(partial_undefined.end.is_none());
1139 }
1140
1141 #[test]
1142 fn test_tov_conversions() {
1143 let time = CuTime::from(100u64);
1145
1146 let tov_time: Tov = time.into();
1148 assert!(matches!(tov_time, Tov::Time(_)));
1149
1150 if let Tov::Time(t) = tov_time {
1151 assert_eq!(t, time);
1152 }
1153
1154 let some_time = Some(time);
1156 let tov_some: Tov = some_time.into();
1157 assert!(matches!(tov_some, Tov::Time(_)));
1158
1159 let none_time: Option<CuDuration> = None;
1160 let tov_none: Tov = none_time.into();
1161 assert!(matches!(tov_none, Tov::None));
1162
1163 let start = CuTime::from(100u64);
1165 let end = CuTime::from(200u64);
1166 let range = CuTimeRange { start, end };
1167 let tov_range = Tov::Range(range);
1168
1169 assert!(matches!(tov_range, Tov::Range(_)));
1170 }
1171
1172 #[cfg(feature = "std")]
1173 #[test]
1174 fn test_cuduration_display() {
1175 let nano = CuDuration(42);
1177 assert_eq!(nano.to_string(), "42 ns");
1178
1179 let micro = CuDuration(42_000);
1180 assert_eq!(micro.to_string(), "42.000 µs");
1181
1182 let milli = CuDuration(42_000_000);
1183 assert_eq!(milli.to_string(), "42.000 ms");
1184
1185 let sec = CuDuration(1_500_000_000);
1186 assert_eq!(sec.to_string(), "1.500 s");
1187
1188 let min = CuDuration(90_000_000_000);
1189 assert_eq!(min.to_string(), "1.500 m");
1190
1191 let hour = CuDuration(3_600_000_000_000);
1192 assert_eq!(hour.to_string(), "1.000 h");
1193
1194 let day = CuDuration(86_400_000_000_000);
1195 assert_eq!(day.to_string(), "1.000 d");
1196 }
1197
1198 #[test]
1199 fn test_robot_clock_precision() {
1200 let clock = RobotClock::new();
1203
1204 let recent = clock.recent();
1206 let now = clock.now();
1207
1208 assert!(recent <= now);
1210
1211 let ref_time_ns = 1_000_000_000; let clock = RobotClock::from_ref_time(ref_time_ns);
1214
1215 let now = clock.now();
1217 let now_ns: u64 = now.into();
1218
1219 let tolerance_ns = 50_000_000; assert!(now_ns >= ref_time_ns);
1222 assert!(now_ns < ref_time_ns + tolerance_ns);
1223 }
1224
1225 #[test]
1226 fn test_mock_clock_advanced_operations() {
1227 let (clock, mock) = RobotClock::mock();
1229
1230 assert_eq!(clock.now(), CuTime::from(0));
1232
1233 mock.increment(CuDuration::from_secs(10));
1235 assert_eq!(
1236 clock.now(),
1237 CuTime::from_nanos(CuDuration::from_secs(10).as_nanos())
1238 );
1239
1240 mock.decrement(CuDuration::from_secs(5));
1242 assert_eq!(
1243 clock.now(),
1244 CuTime::from_nanos(CuDuration::from_secs(5).as_nanos())
1245 );
1246
1247 mock.set_value(30_000_000_000); assert_eq!(
1250 clock.now(),
1251 CuTime::from_nanos(CuDuration::from_secs(30).as_nanos())
1252 );
1253
1254 assert_eq!(
1256 mock.now(),
1257 CuTime::from_nanos(CuDuration::from_secs(30).as_nanos())
1258 );
1259 assert_eq!(mock.value(), 30_000_000_000);
1260 }
1261
1262 #[test]
1263 fn test_cuduration_min_max() {
1264 assert_eq!(CuDuration::MIN, CuDuration(0));
1266
1267 let a = CuDuration(100);
1269 let b = CuDuration(200);
1270
1271 assert_eq!(a.min(b), a);
1272 assert_eq!(a.max(b), b);
1273 assert_eq!(b.min(a), a);
1274 assert_eq!(b.max(a), b);
1275
1276 assert_eq!(a.min(a), a);
1278 assert_eq!(a.max(a), a);
1279
1280 assert_eq!(a.min(CuDuration::MIN), CuDuration::MIN);
1282 assert_eq!(a.max(CuDuration::MAX), CuDuration::MAX);
1283 }
1284
1285 #[test]
1286 fn test_clock_provider_trait() {
1287 struct TestClockProvider {
1289 clock: RobotClock,
1290 }
1291
1292 impl ClockProvider for TestClockProvider {
1293 fn get_clock(&self) -> RobotClock {
1294 self.clock.clone()
1295 }
1296 }
1297
1298 let (clock, mock) = RobotClock::mock();
1300 let provider = TestClockProvider { clock };
1301
1302 let provider_clock = provider.get_clock();
1304 assert_eq!(provider_clock.now(), CuTime::from(0));
1305
1306 mock.increment(CuDuration::from_secs(5));
1308 assert_eq!(
1309 provider_clock.now(),
1310 CuTime::from_nanos(CuDuration::from_secs(5).as_nanos())
1311 );
1312 }
1313}