timer_no_std/
lib.rs

1#![deny(warnings)]
2#![doc(test(attr(deny(warnings))))]
3#![doc(test(attr(allow(dead_code))))]
4#![doc(test(attr(allow(unused_variables))))]
5#![allow(unknown_lints)]
6#![allow(clippy::multiple_bound_locations)]
7
8#![no_std]
9
10#[cfg(target_os="dos")]
11use core::arch::asm;
12use core::mem::replace;
13#[cfg(all(not(target_os="dos"), not(windows)))]
14use libc::{c_long, time_t};
15use num_traits::Num;
16#[cfg(target_os="dos")]
17use pc_timer::Timer;
18
19#[cfg(not(target_os="dos"))]
20pub struct MonoClock(());
21
22#[cfg(target_os="dos")]
23pub struct MonoClock(Timer);
24
25impl MonoClock {
26    /// # Safety
27    ///
28    /// This function may not be called while another [`MonoClock`] instance is alive.
29    ///
30    /// Also, if compiled with `cfg(target_os="dos")` it should be guaranteed that
31    /// it is compiled for an effectively single-core processor.
32    pub unsafe fn new() -> Self {
33        Self::new_raw()
34    }
35
36    #[cfg(not(target_os="dos"))]
37    unsafe fn new_raw() -> Self {
38        MonoClock(())
39    }
40
41    #[cfg(target_os="dos")]
42    unsafe fn new_raw() -> Self {
43        MonoClock(Timer::new(125))
44    }
45
46    pub fn sleep_ms_u8(&self, ms: u8) { self.sleep_ms(ms); }
47
48    pub fn sleep_ms_u16(&self, ms: u16) { self.sleep_ms(ms); }
49
50    pub fn sleep_ms_u32(&self, ms: u32) { self.sleep_ms(ms); }
51
52    pub fn sleep_ms_u64(&self, ms: u64) { self.sleep_ms(ms); }
53
54    #[cfg(target_os="dos")]
55    pub fn time(&self) -> MonoTime {
56        MonoTime {
57            ticks: self.0.ticks(),
58        }
59    }
60
61    #[cfg(all(not(target_os="dos"), windows))]
62    pub fn time(&self) -> MonoTime {
63        use winapi::um::sysinfoapi::GetTickCount64;
64
65        MonoTime {
66            ticks: unsafe { GetTickCount64() },
67        }
68    }
69
70    #[cfg(all(not(target_os="dos"), not(windows)))]
71    pub fn time(&self) -> MonoTime {
72        use core::mem::MaybeUninit;
73        use libc::{CLOCK_MONOTONIC, clock_gettime};
74
75        let mut time = MaybeUninit::uninit();
76        assert_eq!(unsafe { clock_gettime(CLOCK_MONOTONIC, time.as_mut_ptr()) }, 0);
77        let time = unsafe { time.assume_init() };
78        MonoTime {
79            s: time.tv_sec,
80            ms: (time.tv_nsec / 1_000_000) as i16,
81        }
82    }
83
84    #[cfg(target_os="dos")]
85    #[inline]
86    fn sleep_ms<T: Num + Copy>(&self, mut ms: T) where u64: TryInto<T>, T: TryInto<u64> {
87        while ms != T::zero() {
88            let (sleep, last) = if let Ok(ms_u64) = ms.try_into() {
89                (ms_u64, T::zero())
90            } else {
91                (u64::MAX, ms - u64::MAX.try_into().unwrap_or_else(|_| unreachable!()))
92            };
93            let start = self.time();
94            loop {
95                if self.time().delta_ms_u64(start).unwrap() > sleep { break; }
96                for _ in 0 .. 64 {
97                    unsafe { asm!("nop"); }
98                }
99            }
100            ms = last;
101        }
102    }
103
104    #[cfg(all(not(target_os="dos"), windows))]
105    #[inline]
106    fn sleep_ms<T: Num + Copy>(&self, mut ms: T) where u32: TryInto<T>, T: TryInto<u32> {
107        use winapi::um::synchapi::Sleep;
108
109        while ms != T::zero() {
110            let (sleep, last) = if let Ok(ms_u32) = ms.try_into() {
111                (ms_u32, T::zero())
112            } else {
113                (u32::MAX, ms - u32::MAX.try_into().unwrap_or_else(|_| unreachable!()))
114            };
115            unsafe { Sleep(sleep); }
116            ms = last;
117        }
118    }
119
120    #[cfg(all(not(target_os="dos"), not(windows)))]
121    #[inline]
122    fn sleep_ms<T: Num + Copy>(
123        &self,
124        mut ms: T
125    ) where u16: TryInto<T>, T: TryInto<time_t>, time_t: TryInto<T>, T: TryInto<c_long> {
126        use core::ptr::null_mut;
127        use libc::{nanosleep, timespec};
128
129        while ms != T::zero() {
130            let (sleep_s, sleep_ms, last): (time_t, c_long, T) = if let Ok(thousand) = 1000u16.try_into() {
131                let s = ms / thousand;
132                if let Ok(s_time_t) = s.try_into() {
133                    (
134                        s_time_t,
135                        (ms % thousand).try_into().unwrap_or_else(|_| unreachable!()),
136                        T::zero()
137                    )
138                } else {
139                    (
140                        time_t::MAX,
141                        (ms % thousand).try_into().unwrap_or_else(|_| unreachable!()),
142                        (s - time_t::MAX.try_into().unwrap_or_else(|_| unreachable!())) * thousand 
143                    )
144                }
145            } else {
146                (0, ms.try_into().unwrap_or_else(|_| unreachable!()), T::zero())
147            };
148            let sleep = timespec { tv_sec: sleep_s, tv_nsec: sleep_ms * 1_000_000 };
149            unsafe { nanosleep(&sleep as *const _, null_mut()); }
150            ms = last;
151        }
152    }
153}
154
155#[cfg(any(target_os="dos", windows))]
156#[derive(Debug, Clone, Copy)]
157pub struct MonoTime {
158    ticks: u64,
159}
160
161#[cfg(all(not(target_os="dos"), not(windows)))]
162#[derive(Debug, Clone, Copy)]
163pub struct MonoTime {
164    s: time_t,
165    ms: i16,
166}
167
168impl MonoTime {
169    pub fn delta_ms_u8(self, prev: MonoTime) -> Option<u8> { self.delta_ms(prev) }
170
171    pub fn delta_ms_u16(self, prev: MonoTime) -> Option<u16> { self.delta_ms(prev) }
172
173    pub fn delta_ms_u32(self, prev: MonoTime) -> Option<u32> { self.delta_ms(prev) }
174
175    pub fn delta_ms_u64(self, prev: MonoTime) -> Option<u64> { self.delta_ms(prev) }
176
177    pub fn delta_ms_u128(self, prev: MonoTime) -> Option<u128> { self.delta_ms(prev) }
178
179    pub fn split_ms_u8(&mut self, clock: &MonoClock) -> Option<u8> {
180        let prev = replace(self, clock.time());
181        self.delta_ms_u8(prev)
182    }
183
184    pub fn split_ms_u16(&mut self, clock: &MonoClock) -> Option<u16> {
185        let prev = replace(self, clock.time());
186        self.delta_ms_u16(prev)
187    }
188
189    pub fn split_ms_u32(&mut self, clock: &MonoClock) -> Option<u32> {
190        let prev = replace(self, clock.time());
191        self.delta_ms_u32(prev)
192    }
193
194    pub fn split_ms_u64(&mut self, clock: &MonoClock) -> Option<u64> {
195        let prev = replace(self, clock.time());
196        self.delta_ms_u64(prev)
197    }
198
199    pub fn split_ms_u128(&mut self, clock: &MonoClock) -> Option<u128> {
200        let prev = replace(self, clock.time());
201        self.delta_ms_u128(prev)
202    }
203
204    #[cfg(target_os="dos")]
205    #[inline]
206    fn delta_ms<T>(self, prev: MonoTime) -> Option<T> where u64: TryInto<T> {
207        self.ticks.wrapping_sub(prev.ticks).wrapping_mul(8).try_into().ok()
208    }
209
210    #[cfg(all(not(target_os="dos"), windows))]
211    #[inline]
212    fn delta_ms<T>(self, prev: MonoTime) -> Option<T> where u64: TryInto<T> {
213        self.ticks.checked_sub(prev.ticks).unwrap().try_into().ok()
214    }
215
216    #[cfg(all(not(target_os="dos"), not(windows)))]
217    #[inline]
218    fn delta_ms<T: Num + num_traits::Bounded + Ord + Copy>(
219        self,
220        prev: MonoTime
221    ) -> Option<T> where i16: TryInto<T>, time_t: TryInto<T> {
222        let mut s = self.s.checked_sub(prev.s).unwrap();
223        assert!(s >= 0);
224        let mut ms = self.ms - prev.ms;
225        if ms < 0 {
226            s = s.checked_sub(1).unwrap();
227            ms += 1000;
228            debug_assert!(ms >= 0);
229        }
230        let ms: T = ms.try_into().ok()?;
231        let s: T = s.try_into().ok()?;
232        let thousand: T = if let Ok(thousand) = 1000i16.try_into() {
233            thousand
234        } else {
235            if s != T::zero() { return None; }
236            T::one()
237        };
238        if T::max_value() / thousand < s { return None; }
239        let s = s * thousand;
240        if T::max_value() - s < ms { return None; }
241        Some(s + ms)
242    }
243}