1#![allow(dead_code)]
5
6use core::num::TryFromIntError;
7use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
8use core::time::Duration;
9
10use crate::backend::c;
11#[allow(unused)]
12use crate::ffi;
13#[cfg(not(fix_y2038))]
14use core::ptr::null;
15
16#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
18#[repr(C)]
19pub struct Timespec {
20    pub tv_sec: Secs,
22
23    pub tv_nsec: Nsecs,
32}
33
34pub type Secs = i64;
36
37#[cfg(any(
39    fix_y2038,
40    linux_raw,
41    all(libc, target_arch = "x86_64", target_pointer_width = "32")
42))]
43pub type Nsecs = i64;
44
45#[cfg(all(
47    not(fix_y2038),
48    libc,
49    not(all(target_arch = "x86_64", target_pointer_width = "32"))
50))]
51pub type Nsecs = ffi::c_long;
52
53impl Timespec {
54    pub const fn checked_add(self, rhs: Self) -> Option<Self> {
107        if let Some(mut tv_sec) = self.tv_sec.checked_add(rhs.tv_sec) {
108            let mut tv_nsec = self.tv_nsec + rhs.tv_nsec;
109            if tv_nsec >= 1_000_000_000 {
110                tv_nsec -= 1_000_000_000;
111                if let Some(carried_sec) = tv_sec.checked_add(1) {
112                    tv_sec = carried_sec;
113                } else {
114                    return None;
115                }
116            }
117            Some(Self { tv_sec, tv_nsec })
118        } else {
119            None
120        }
121    }
122
123    pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
176        if let Some(mut tv_sec) = self.tv_sec.checked_sub(rhs.tv_sec) {
177            let mut tv_nsec = self.tv_nsec - rhs.tv_nsec;
178            if tv_nsec < 0 {
179                tv_nsec += 1_000_000_000;
180                if let Some(borrowed_sec) = tv_sec.checked_sub(1) {
181                    tv_sec = borrowed_sec;
182                } else {
183                    return None;
184                }
185            }
186            Some(Self { tv_sec, tv_nsec })
187        } else {
188            None
189        }
190    }
191
192    pub(crate) fn as_c_int_millis(&self) -> Option<c::c_int> {
194        let secs = self.tv_sec;
195        if secs < 0 {
196            return None;
197        }
198        secs.checked_mul(1000)
199            .and_then(|millis| {
200                millis.checked_add((i64::from(self.tv_nsec) + 999_999) / 1_000_000)
203            })
204            .and_then(|millis| c::c_int::try_from(millis).ok())
205    }
206}
207
208impl TryFrom<Timespec> for Duration {
209    type Error = TryFromIntError;
210
211    fn try_from(ts: Timespec) -> Result<Self, Self::Error> {
212        Ok(Self::new(ts.tv_sec.try_into()?, ts.tv_nsec as _))
213    }
214}
215
216impl TryFrom<Duration> for Timespec {
217    type Error = TryFromIntError;
218
219    fn try_from(dur: Duration) -> Result<Self, Self::Error> {
220        Ok(Self {
221            tv_sec: dur.as_secs().try_into()?,
222            tv_nsec: dur.subsec_nanos() as _,
223        })
224    }
225}
226
227impl Add for Timespec {
228    type Output = Self;
229
230    fn add(self, rhs: Self) -> Self {
231        self.checked_add(rhs)
232            .expect("overflow when adding timespecs")
233    }
234}
235
236impl AddAssign for Timespec {
237    fn add_assign(&mut self, rhs: Self) {
238        *self = *self + rhs;
239    }
240}
241
242impl Sub for Timespec {
243    type Output = Self;
244
245    fn sub(self, rhs: Self) -> Self {
246        self.checked_sub(rhs)
247            .expect("overflow when subtracting timespecs")
248    }
249}
250
251impl SubAssign for Timespec {
252    fn sub_assign(&mut self, rhs: Self) {
253        *self = *self - rhs;
254    }
255}
256
257impl Neg for Timespec {
258    type Output = Self;
259
260    fn neg(self) -> Self {
261        Self::default() - self
262    }
263}
264
265#[cfg(fix_y2038)]
269#[repr(C)]
270#[derive(Debug, Clone)]
271pub(crate) struct LibcTimespec {
272    pub(crate) tv_sec: Secs,
273
274    #[cfg(target_endian = "big")]
275    padding: core::mem::MaybeUninit<u32>,
276
277    pub(crate) tv_nsec: i32,
278
279    #[cfg(target_endian = "little")]
280    padding: core::mem::MaybeUninit<u32>,
281}
282
283#[cfg(fix_y2038)]
284impl From<LibcTimespec> for Timespec {
285    #[inline]
286    fn from(t: LibcTimespec) -> Self {
287        Self {
288            tv_sec: t.tv_sec,
289            tv_nsec: t.tv_nsec as _,
290        }
291    }
292}
293
294#[cfg(fix_y2038)]
295impl From<Timespec> for LibcTimespec {
296    #[inline]
297    fn from(t: Timespec) -> Self {
298        Self {
299            tv_sec: t.tv_sec,
300            tv_nsec: t.tv_nsec as _,
301            padding: core::mem::MaybeUninit::uninit(),
302        }
303    }
304}
305
306#[cfg(not(fix_y2038))]
307pub(crate) fn as_libc_timespec_ptr(timespec: &Timespec) -> *const c::timespec {
308    #[cfg(test)]
309    {
310        assert_eq_size!(Timespec, c::timespec);
311    }
312    crate::utils::as_ptr(timespec).cast::<c::timespec>()
313}
314
315#[cfg(not(fix_y2038))]
316pub(crate) fn as_libc_timespec_mut_ptr(
317    timespec: &mut core::mem::MaybeUninit<Timespec>,
318) -> *mut c::timespec {
319    #[cfg(test)]
320    {
321        assert_eq_size!(Timespec, c::timespec);
322    }
323    timespec.as_mut_ptr().cast::<c::timespec>()
324}
325
326#[cfg(not(fix_y2038))]
327pub(crate) fn option_as_libc_timespec_ptr(timespec: Option<&Timespec>) -> *const c::timespec {
328    match timespec {
329        None => null(),
330        Some(timespec) => as_libc_timespec_ptr(timespec),
331    }
332}
333
334#[cfg(apple)]
340#[inline]
341pub(crate) fn fix_negative_nsecs(
342    mut secs: c::time_t,
343    mut nsecs: c::c_long,
344) -> (c::time_t, c::c_long) {
345    #[cold]
346    fn adjust(secs: &mut c::time_t, nsecs: c::c_long) -> c::c_long {
347        assert!(nsecs >= -1_000_000_000);
348        assert!(*secs < 0);
349        assert!(*secs > c::time_t::MIN);
350        *secs -= 1;
351        nsecs + 1_000_000_000
352    }
353
354    if nsecs < 0 {
355        nsecs = adjust(&mut secs, nsecs);
356    }
357    (secs, nsecs)
358}
359
360#[cfg(test)]
361mod tests {
362    use super::*;
363
364    #[cfg(apple)]
365    #[test]
366    fn test_negative_timestamps() {
367        let mut secs = -59;
368        let mut nsecs = -900_000_000;
369        (secs, nsecs) = fix_negative_nsecs(secs, nsecs);
370        assert_eq!(secs, -60);
371        assert_eq!(nsecs, 100_000_000);
372        (secs, nsecs) = fix_negative_nsecs(secs, nsecs);
373        assert_eq!(secs, -60);
374        assert_eq!(nsecs, 100_000_000);
375    }
376
377    #[test]
378    fn test_sizes() {
379        assert_eq_size!(Secs, u64);
380        const_assert!(core::mem::size_of::<Timespec>() >= core::mem::size_of::<(u64, u32)>());
381        const_assert!(core::mem::size_of::<Nsecs>() >= 4);
382
383        let mut t = Timespec {
384            tv_sec: 0,
385            tv_nsec: 0,
386        };
387
388        t.tv_nsec = 999_999_999_u32 as _;
390        assert_eq!(t.tv_nsec as u64, 999_999_999_u64);
391
392        t.tv_sec = 0x1_0000_0000_u64 as _;
394        assert_eq!(t.tv_sec as u64, 0x1_0000_0000_u64);
395    }
396
397    #[cfg(fix_y2038)]
399    #[test]
400    #[allow(deprecated)]
401    fn test_fix_y2038() {
402        assert_eq_size!(libc::time_t, u32);
403    }
404
405    #[cfg(not(fix_y2038))]
407    #[test]
408    fn timespec_layouts() {
409        use crate::backend::c;
410        check_renamed_struct!(Timespec, timespec, tv_sec, tv_nsec);
411    }
412
413    #[cfg(linux_raw_dep)]
415    #[test]
416    fn test_against_kernel_timespec() {
417        assert_eq_size!(Timespec, linux_raw_sys::general::__kernel_timespec);
418        assert_eq_align!(Timespec, linux_raw_sys::general::__kernel_timespec);
419        assert_eq!(
420            memoffset::span_of!(Timespec, tv_sec),
421            memoffset::span_of!(linux_raw_sys::general::__kernel_timespec, tv_sec)
422        );
423        assert_eq!(
424            memoffset::span_of!(Timespec, tv_nsec),
425            memoffset::span_of!(linux_raw_sys::general::__kernel_timespec, tv_nsec)
426        );
427    }
428}