mach_sys/
clock_types.rs

1//! This module roughly corresponds to `mach/clock_types.h`.
2
3use crate::ffi::{c_int, c_uint, c_ulonglong};
4use core::ops::{Add, Sub, AddAssign, SubAssign};
5use core::cmp::Ordering;
6use core::time::Duration;
7
8use crate::vm_types::integer_t;
9
10pub type alarm_type_t   = c_int;
11pub type sleep_type_t   = c_int;
12pub type clock_id_t     = c_int;
13pub type clock_flavor_t = c_int;
14pub type clock_attr_t   = *mut c_int;
15pub type clock_res_t    = c_int;
16
17pub const SYSTEM_CLOCK:   c_uint = 0;
18pub const CALENDAR_CLOCK: c_uint = 1;
19pub const REALTIME_CLOCK: c_uint = 0;
20
21pub const ALRMTYPE:       c_uint = 0xff;
22pub const TIME_ABSOLUTE:  c_uint = 0x00;
23pub const TIME_RELATIVE:  c_uint = 0x01;
24
25pub const CLOCK_GET_TIME_RES: c_uint = 1;
26pub const CLOCK_ALARM_CURRES: c_uint = 3;
27pub const CLOCK_ALARM_MINRES: c_uint = 4;
28pub const CLOCK_ALARM_MAXRES: c_uint = 5;
29
30
31#[repr(C)]
32#[derive(Copy, Clone, Debug, Default, Hash, PartialOrd, PartialEq, Eq, Ord)]
33pub struct time_value_t {
34    pub seconds: integer_t,
35    pub microseconds: integer_t,
36}
37pub const TIME_MICROS_MAX: integer_t = 1_000_000;
38
39#[repr(C)]
40#[derive(Copy, Clone, Debug, Default, Hash)]
41pub struct mach_timespec_t {
42    pub tv_sec:  c_uint,
43    pub tv_nsec: clock_res_t,
44}
45pub const USEC_PER_SEC:  c_ulonglong = 1_000_000;
46pub const NSEC_PER_SEC:  c_ulonglong = 1_000_000_000;
47pub const NSEC_PER_USEC: c_ulonglong = 1_000;
48pub const NSEC_PER_MSEC: c_ulonglong = 1_000_000;
49pub type mach_timespec = mach_timespec_t;
50
51impl PartialOrd for mach_timespec {
52    fn partial_cmp(&self, other: &mach_timespec) -> Option<Ordering> {
53        Some(mach_timespec::cmp(self, other))
54    }
55}
56impl Ord for mach_timespec {
57    fn cmp(&self, other: &mach_timespec) -> Ordering {
58        mach_timespec::cmp(self, other)
59    }
60}
61
62impl From<mach_timespec> for Duration {
63    fn from(val: mach_timespec) -> Duration {
64        val.to_duration()
65    }
66}
67impl From<&mach_timespec> for Duration {
68    fn from(val: &mach_timespec) -> Duration {
69        val.to_duration()
70    }
71}
72impl From<Duration> for mach_timespec {
73    fn from(val: Duration) -> mach_timespec {
74        mach_timespec::from_duration(val)
75    }
76}
77impl From<&Duration> for mach_timespec {
78    fn from(val: &Duration) -> mach_timespec {
79        mach_timespec::from_duration(*val)
80    }
81}
82
83/*
84impl PartialEq for mach_timespec {
85    fn eq(&self, other: &Self) -> bool {
86        assert!( self.is_valid());
87        assert!(other.is_valid());
88
89        self.tv_sec == other.tv_sec && self.tv_nsec == other.tv_nsec
90    }
91}*/
92
93impl<T> PartialEq<T> for mach_timespec
94where T: Into<Duration> + Clone
95{
96    fn eq(&self, other: &T) -> bool {
97        self.to_duration() == other.clone().into()
98    }
99}
100impl Eq for mach_timespec {}
101
102impl mach_timespec {
103    pub const fn new() -> Self {
104        Self {
105            tv_sec:  0,
106            tv_nsec: 0,
107        }
108    }
109
110    pub const fn from_secs(sec: c_uint) -> Self {
111        Self {
112            tv_sec:  sec,
113            tv_nsec: 0
114        }
115    }
116    pub const fn from_nanos(nsec: clock_res_t) -> Self {
117        if nsec < 0 {
118            panic!("mach_timespec cannot convert from nanoseconds: `.tv_nsec` is a negative integer!");
119        }
120        Self::from_duration( Duration::from_nanos(nsec as u64) )
121    }
122
123    /* start non-const methods */
124    pub fn from_secs_f64(fsec: f64) -> Self {
125        Self::from_duration( Duration::from_secs_f64(fsec) )
126    }
127    pub fn as_secs_f64(&self) -> f64 {
128        self.to_duration().as_secs_f64()
129    }
130
131    pub fn from_secs_f32(fsec: f32) -> Self {
132        Self::from_duration( Duration::from_secs_f32(fsec) )
133    }
134    pub fn as_secs_f32(&self) -> f32 {
135        self.to_duration().as_secs_f32()
136    }
137    /* end non-const methods */
138
139    pub const fn to_duration(&self) -> Duration {
140        if ! self.is_valid() {
141            panic!("mach_timespec is invalid!");
142        }
143
144        let sec  = self.tv_sec  as u64;
145        let nsec = self.tv_nsec as u32;
146
147        Duration::new(sec, nsec)
148    }
149    pub const fn from_duration(dur: Duration) -> Self {
150        let tv_sec = {
151            let sec = dur.as_secs();
152
153            // handle 2038 year issue (aka Y2038 problem)
154            if sec > (c_uint::MAX as u64) {
155                panic!("cannot convert from Duration to mach_timespec: .tv_sec overflow c_uint!");
156            }
157
158            sec as c_uint
159        };
160
161        // this is fine.
162        // because 1,000,000,000 never overflow c_int (even it's 32-bit).
163        // and the return value is never be negative number: https://doc.rust-lang.org/1.76.0/src/core/time.rs.html#36
164        let tv_nsec = dur.subsec_nanos() as clock_res_t;
165
166        let this = Self {
167            tv_sec,
168            tv_nsec,
169        };
170        assert!(this.is_valid()); // this should never fails (otherwise found a bug)
171        this
172    }
173
174    pub const fn is_valid(&self) -> bool {
175        if self.tv_nsec < 0 {
176            return false;
177        }
178
179        if (self.tv_nsec as c_ulonglong) >= NSEC_PER_SEC {
180            return false;
181        }
182
183        true
184    }
185
186    pub const fn cmp(&self, other: &Self) -> Ordering {
187        if self.tv_sec > other.tv_sec {
188            return Ordering::Greater;
189        }
190        if self.tv_sec < other.tv_sec {
191            return Ordering::Less;
192        }
193
194        // self.tv_sec == other.tv_sec
195
196        if self.tv_nsec > other.tv_nsec {
197            return Ordering::Greater;
198        }
199        if self.tv_nsec < other.tv_nsec {
200            return Ordering::Less;
201        }
202
203        // both of seconds and nano-seconds is equal.
204
205        Ordering::Equal
206    }
207
208    pub const fn nsec_diff(&self, other: &Self)
209        -> c_ulonglong
210    {
211        if self.tv_sec > other.tv_sec {
212            return NSEC_PER_SEC;
213        }
214
215        if self.tv_sec < other.tv_sec {
216            return !NSEC_PER_SEC;
217        }
218
219        (self.tv_nsec - other.tv_nsec) as c_ulonglong
220    }
221
222    pub const fn add(&self, other: &Self) -> Self {
223        let mut result = Self::new();
224
225        result.tv_sec  = self.tv_sec  + other.tv_sec;
226        result.tv_nsec = self.tv_nsec + other.tv_nsec;
227
228        let t1ns = self.tv_nsec as c_ulonglong;
229        if t1ns >= NSEC_PER_SEC {
230            result.tv_nsec =
231                (t1ns - NSEC_PER_SEC) as clock_res_t;
232
233            result.tv_sec += 1;
234        }
235
236        result
237    }
238
239    pub const fn sub(&self, other: &Self) -> Self {
240        let mut result = Self::new();
241
242        result.tv_sec  = self.tv_sec  - other.tv_sec;
243        result.tv_nsec = self.tv_nsec - other.tv_nsec;
244
245        if result.tv_nsec < 0 {
246            result.tv_nsec +=
247                NSEC_PER_SEC as clock_res_t;
248
249            result.tv_sec  -= 1;
250        }
251
252        result
253    }
254}
255
256macro_rules! _timespec_ops_impl {
257    ($op:tt, $func:tt) => {
258        _timespec_ops_impl!($op, $func, $func);
259    };
260
261    ($op:tt, $trait_func:tt, $orig_func:tt) => {
262        impl $op<mach_timespec> for mach_timespec {
263            type Output = mach_timespec;
264            fn $trait_func(self, other: mach_timespec)
265                -> mach_timespec
266            {
267                mach_timespec::$orig_func(&self, &other)
268            }
269        }
270
271        // we can not use Self (due to it contains borrow symbol)
272        impl $op<&mach_timespec> for &mach_timespec {
273            type Output = mach_timespec;
274            fn $trait_func(self, other: &mach_timespec)
275                -> mach_timespec
276            {
277                mach_timespec::$orig_func(self, other)
278            }
279        }
280        
281        impl $op<&mach_timespec> for mach_timespec {
282            type Output = mach_timespec;
283            fn $trait_func(self, other: &mach_timespec)
284                -> mach_timespec
285            {
286                mach_timespec::$orig_func(&self, other)
287            }
288        }
289        impl $op<mach_timespec> for &mach_timespec {
290            type Output = mach_timespec;
291            fn $trait_func(self, other: mach_timespec)
292                -> mach_timespec
293            {
294                mach_timespec::$orig_func(self, &other)
295            }
296        }
297    };
298}
299
300_timespec_ops_impl!(Add, add);
301_timespec_ops_impl!(Sub, sub);
302
303macro_rules! _timespec_ops_assign_impl {
304    ($op:tt, $func:tt, $orig_func:tt) => {
305        impl $op for mach_timespec {
306            fn $func(&mut self, other: mach_timespec) {
307                let ret = mach_timespec::$orig_func(&self, &other);
308                *self = ret;
309            }
310        }
311    }
312}
313
314_timespec_ops_assign_impl!(AddAssign, add_assign, add);
315_timespec_ops_assign_impl!(SubAssign, sub_assign, sub);
316
317/* legacy functions */
318
319#[allow(non_snake_case)]
320pub const fn BAD_MACH_TIMESPEC(t: mach_timespec) ->bool{
321    ! t.is_valid()
322}
323
324#[allow(non_snake_case)]
325pub const fn CMP_MACH_TIMESPEC(
326    t1: &mach_timespec,
327    t2: &mach_timespec
328) -> c_ulonglong {
329    t1.nsec_diff(t2)
330}
331
332pub const fn add_mach_timespec(
333    t1: &mach_timespec,
334    t2: &mach_timespec
335) -> mach_timespec {
336    mach_timespec::add(t1, t2)
337}
338pub const fn sub_mach_timespec(
339    t1: &mach_timespec,
340    t2: &mach_timespec
341) -> mach_timespec {
342    mach_timespec::sub(t1, t2)
343}
344
345#[allow(non_snake_case)]
346#[deprecated]
347pub fn ADD_MACH_TIMESPEC(
348    t1: &mut mach_timespec,
349    t2: &mach_timespec
350) {
351    *t1 = mach_timespec::add(t1, t2);
352}
353#[allow(non_snake_case)]
354#[deprecated]
355pub fn SUB_MACH_TIMESPEC(
356    t1: &mut mach_timespec,
357    t2: &mach_timespec
358) {
359    *t1 = mach_timespec::sub(t1, t2);
360}
361
362#[allow(non_snake_case)]
363pub const fn BAD_ALRMTYPE(t: c_uint) -> bool {
364    ( t & (!TIME_RELATIVE) ) != 0
365}
366
367#[cfg(test)]
368mod test {
369    use super::*;
370    use std::io::Write;
371
372    use crate::pl;
373
374    #[test]
375    fn ops_add() {
376        let mut rets = std::vec::Vec::new();
377        let c = 10240;
378        for _ in 0..c {
379            let n = fastrand::i32(clock_res_t::MIN as i32..clock_res_t::MAX as i32) as clock_res_t;
380            rets.push(_ops_add(n));
381        }
382
383        for out in
384            rets.into_iter()
385            .skip(c - 5)
386            .collect::<Vec<_>>()
387        {
388            pl!("{}", out);
389        }
390    }
391
392    fn _ops_add(n: clock_res_t) -> String {
393        let ret = std::panic::catch_unwind(|| {
394            mach_timespec::from_nanos(n)
395        });
396
397        let mut out = String::new();
398        if n >= 0 {
399            let spec = ret.expect("mach_timespec::from_nanos() parse failed");
400            assert!(spec.is_valid());
401            out.push_str(&format!("spec({spec:#?}) is valid"));
402            let dur = Duration::from_nanos(n as u64);
403
404            assert_eq!(spec, dur);
405            out.push_str(&format!("spec is equal to {:#?}", dur));
406        } else {
407            assert!(ret.is_err());
408            out.push_str(&format!("{n:?} is invalid"));
409        }
410        out
411    }
412}
413