| 1 | //! `Timespec` and related types, which are used by multiple public API |
| 2 | //! modules. |
| 3 | |
| 4 | #[cfg (not(fix_y2038))] |
| 5 | use crate::backend::c; |
| 6 | |
| 7 | /// `struct timespec` |
| 8 | #[cfg (not(fix_y2038))] |
| 9 | pub type Timespec = c::timespec; |
| 10 | |
| 11 | /// `struct timespec` |
| 12 | #[cfg (fix_y2038)] |
| 13 | #[derive (Debug, Clone, Copy)] |
| 14 | #[repr (C)] |
| 15 | pub struct Timespec { |
| 16 | /// Seconds. |
| 17 | pub tv_sec: Secs, |
| 18 | |
| 19 | /// Nanoseconds. Must be less than 1_000_000_000. |
| 20 | pub tv_nsec: Nsecs, |
| 21 | } |
| 22 | |
| 23 | /// A type for the `tv_sec` field of [`Timespec`]. |
| 24 | #[cfg (not(fix_y2038))] |
| 25 | #[allow (deprecated)] |
| 26 | pub type Secs = c::time_t; |
| 27 | |
| 28 | /// A type for the `tv_sec` field of [`Timespec`]. |
| 29 | #[cfg (fix_y2038)] |
| 30 | pub type Secs = i64; |
| 31 | |
| 32 | /// A type for the `tv_sec` field of [`Timespec`]. |
| 33 | #[cfg (any( |
| 34 | fix_y2038, |
| 35 | linux_raw, |
| 36 | all(libc, target_arch = "x86_64" , target_pointer_width = "32" ) |
| 37 | ))] |
| 38 | pub type Nsecs = i64; |
| 39 | |
| 40 | /// A type for the `tv_nsec` field of [`Timespec`]. |
| 41 | #[cfg (all( |
| 42 | not(fix_y2038), |
| 43 | libc, |
| 44 | not(all(target_arch = "x86_64" , target_pointer_width = "32" )) |
| 45 | ))] |
| 46 | pub type Nsecs = c::c_long; |
| 47 | |
| 48 | /// On 32-bit glibc platforms, `timespec` has anonymous padding fields, which |
| 49 | /// Rust doesn't support yet (see `unnamed_fields`), so we define our own |
| 50 | /// struct with explicit padding, with bidirectional `From` impls. |
| 51 | #[cfg (fix_y2038)] |
| 52 | #[repr (C)] |
| 53 | #[derive (Debug, Clone)] |
| 54 | pub(crate) struct LibcTimespec { |
| 55 | pub(crate) tv_sec: Secs, |
| 56 | |
| 57 | #[cfg (target_endian = "big" )] |
| 58 | padding: core::mem::MaybeUninit<u32>, |
| 59 | |
| 60 | pub(crate) tv_nsec: i32, |
| 61 | |
| 62 | #[cfg (target_endian = "little" )] |
| 63 | padding: core::mem::MaybeUninit<u32>, |
| 64 | } |
| 65 | |
| 66 | #[cfg (fix_y2038)] |
| 67 | impl From<LibcTimespec> for Timespec { |
| 68 | #[inline ] |
| 69 | fn from(t: LibcTimespec) -> Self { |
| 70 | Self { |
| 71 | tv_sec: t.tv_sec, |
| 72 | tv_nsec: t.tv_nsec as _, |
| 73 | } |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | #[cfg (fix_y2038)] |
| 78 | impl From<Timespec> for LibcTimespec { |
| 79 | #[inline ] |
| 80 | fn from(t: Timespec) -> Self { |
| 81 | Self { |
| 82 | tv_sec: t.tv_sec, |
| 83 | tv_nsec: t.tv_nsec as _, |
| 84 | padding: core::mem::MaybeUninit::uninit(), |
| 85 | } |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | #[test ] |
| 90 | fn test_sizes() { |
| 91 | assert_eq_size!(Secs, u64); |
| 92 | const_assert!(core::mem::size_of::<Timespec>() >= core::mem::size_of::<(u64, u32)>()); |
| 93 | const_assert!(core::mem::size_of::<Nsecs>() >= 4); |
| 94 | |
| 95 | let mut t = Timespec { |
| 96 | tv_sec: 0, |
| 97 | tv_nsec: 0, |
| 98 | }; |
| 99 | |
| 100 | // `tv_nsec` needs to be able to hold nanoseconds up to a second. |
| 101 | t.tv_nsec = 999_999_999_u32 as _; |
| 102 | assert_eq!(t.tv_nsec as u64, 999_999_999_u64); |
| 103 | |
| 104 | // `tv_sec` needs to be able to hold more than 32-bits of seconds. |
| 105 | t.tv_sec = 0x1_0000_0000_u64 as _; |
| 106 | assert_eq!(t.tv_sec as u64, 0x1_0000_0000_u64); |
| 107 | } |
| 108 | |
| 109 | // Test that our workarounds are needed. |
| 110 | #[cfg (fix_y2038)] |
| 111 | #[test ] |
| 112 | #[allow (deprecated)] |
| 113 | fn test_fix_y2038() { |
| 114 | assert_eq_size!(libc::time_t, u32); |
| 115 | } |
| 116 | |