1#![cfg_attr(
8 not(any(test, feature = "std")),
9 no_std
10)]
11
12use core::ops::Div;
13use sealed::{Promote, RollingSince};
14
15#[cfg(any(test, doctest, feature = "std"))]
16pub mod std_timer;
17
18pub trait RollingTimer {
19 type Tick: RollingSince + Promote + Div<Output = Self::Tick>;
20
21 const TICKS_PER_SECOND: Self::Tick;
22
23 fn get_ticks(&self) -> Self::Tick;
25
26 fn is_initialized(&self) -> bool;
28
29 fn ticks_since(&self, rhs: Self::Tick) -> Self::Tick {
36 self.get_ticks().since(rhs)
37 }
38
39 fn seconds_since(&self, rhs: Self::Tick) -> Self::Tick {
43 self.ticks_since(rhs) / Self::TICKS_PER_SECOND
44 }
45
46 fn millis_since(&self, rhs: Self::Tick) -> Self::Tick {
53 let delta_tick = self.ticks_since(rhs);
54 delta_tick.mul_then_div(
55 <Self::Tick as RollingSince>::MILLIS_PER_SECOND,
56 Self::TICKS_PER_SECOND,
57 )
58 }
59
60 fn micros_since(&self, rhs: Self::Tick) -> Self::Tick {
67 let delta_tick = self.ticks_since(rhs);
68 delta_tick.mul_then_div(
69 <Self::Tick as RollingSince>::MICROS_PER_SECOND,
70 Self::TICKS_PER_SECOND,
71 )
72 }
73}
74
75mod sealed {
76 use core::convert::TryInto;
77 use core::ops::{Div, Mul};
78
79 pub trait Promote: Sized + Copy {
80 type NextSize: From<Self>
81 + Ord
82 + TryInto<Self>
83 + Mul<Output = Self::NextSize>
84 + Div<Output = Self::NextSize>;
85 const MAX_VAL: Self::NextSize;
86
87 fn promote(&self) -> Self::NextSize {
88 (*self).into()
89 }
90 fn saturate_demote(other: Self::NextSize) -> Self {
91 match Self::MAX_VAL.min(other).try_into() {
92 Ok(t) => t,
93 Err(_) => unsafe { core::hint::unreachable_unchecked() },
94 }
95 }
96
97 fn mul_then_div(&self, mul: Self, div: Self) -> Self {
98 Self::saturate_demote((self.promote() * mul.promote()) / div.promote())
99 }
100 }
101
102 pub trait RollingSince {
103 const MILLIS_PER_SECOND: Self;
104 const MICROS_PER_SECOND: Self;
105 fn since(&self, other: Self) -> Self;
106 }
107
108 impl Promote for u32 {
109 type NextSize = u64;
110 const MAX_VAL: u64 = 0xFFFF_FFFF;
111 }
112
113 #[cfg(feature = "u128")]
114 impl Promote for u64 {
115 type NextSize = u128;
116 const MAX_VAL: u128 = 0xFFFF_FFFF_FFFF_FFFF;
117 }
118
119 impl RollingSince for u32 {
120 const MILLIS_PER_SECOND: u32 = 1_000;
121 const MICROS_PER_SECOND: u32 = 1_000_000;
122 fn since(&self, other: u32) -> u32 {
123 self.wrapping_sub(other)
124 }
125 }
126
127 #[cfg(feature = "u128")]
128 impl RollingSince for u64 {
129 const MILLIS_PER_SECOND: u64 = 1_000;
130 const MICROS_PER_SECOND: u64 = 1_000_000;
131 fn since(&self, other: u64) -> u64 {
132 self.wrapping_sub(other)
133 }
134 }
135}
136
137#[cfg(test)]
138mod test {
139 use super::*;
140 use core::sync::atomic::{AtomicU32, Ordering};
141
142 struct TestTimer(&'static AtomicU32);
143 impl RollingTimer for TestTimer {
144 type Tick = u32;
145 const TICKS_PER_SECOND: Self::Tick = 10;
146
147 fn is_initialized(&self) -> bool {
148 true
149 }
150
151 fn get_ticks(&self) -> u32 {
152 self.0.load(Ordering::SeqCst)
153 }
154 }
155
156 #[test]
157 fn simple_wrap() {
158 assert_eq!(0x10u32.since(0xFFFFFFF0), 0x20);
159 assert_eq!(0xFFFFFFF0u32.since(0x10), 0xFFFFFFE0);
160 }
161
162 #[test]
163 fn timer_test() {
164 static TIMER: AtomicU32 = AtomicU32::new(20);
165 let timer = TestTimer(&TIMER);
166
167 assert_eq!(timer.ticks_since(0), 20);
168 assert_eq!(timer.seconds_since(0), 2);
169 assert_eq!(timer.millis_since(0), 2000);
170 assert_eq!(timer.micros_since(0), 2000000);
171
172 TIMER.store(0xFFFF_FFFF, Ordering::SeqCst);
173
174 assert_eq!(timer.ticks_since(0), 0xFFFF_FFFF);
175 assert_eq!(timer.seconds_since(0), 0xFFFF_FFFF / 10);
176
177 assert_eq!(timer.millis_since(0), 0xFFFF_FFFF);
179 assert_eq!(timer.micros_since(0), 0xFFFF_FFFF);
180
181 TIMER.store(0x4000_0000, Ordering::SeqCst);
182
183 assert_eq!(timer.ticks_since(0xC000_0000), 0x8000_0000);
184 assert_eq!(timer.seconds_since(0xC000_0000), 0x8000_0000 / 10);
185
186 assert_eq!(timer.millis_since(0xC000_0000), 0xFFFF_FFFF);
188 assert_eq!(timer.micros_since(0xC000_0000), 0xFFFF_FFFF);
189 }
190}