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 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}