1use core::mem;
2use core::num::niche_types::Nanoseconds;
3
4use crate::io;
5use crate::time::Duration;
6
7const NSEC_PER_SEC: u64 = 1_000_000_000;
8
9#[allow(dead_code)] pub const TIMESPEC_MAX: libc::timespec = {
11 let mut ts = unsafe { mem::zeroed::<libc::timespec>() };
12 ts.tv_sec = <libc::time_t>::MAX;
13 ts.tv_nsec = 1_000_000_000 - 1;
14 ts
15};
16
17#[cfg(target_os = "nto")]
20pub(in crate::sys) const TIMESPEC_MAX_CAPPED: libc::timespec = libc::timespec {
21 tv_sec: (u64::MAX / NSEC_PER_SEC) as i64,
22 tv_nsec: (u64::MAX % NSEC_PER_SEC) as i64,
23};
24
25#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
26pub(crate) struct Timespec {
27 pub tv_sec: i64,
28 pub tv_nsec: Nanoseconds,
29}
30
31impl Timespec {
32 pub const MAX: Timespec = unsafe { Self::new_unchecked(i64::MAX, 1_000_000_000 - 1) };
33
34 pub const MIN: Timespec = unsafe { Self::new_unchecked(i64::MIN, 0) };
38
39 const unsafe fn new_unchecked(tv_sec: i64, tv_nsec: i64) -> Timespec {
40 Timespec { tv_sec, tv_nsec: unsafe { Nanoseconds::new_unchecked(tv_nsec as u32) } }
41 }
42
43 pub const fn zero() -> Timespec {
44 unsafe { Self::new_unchecked(0, 0) }
45 }
46
47 pub const fn new(tv_sec: i64, tv_nsec: i64) -> Result<Timespec, io::Error> {
48 #[cfg(target_vendor = "apple")]
61 let (tv_sec, tv_nsec) =
62 if (tv_sec <= 0 && tv_sec > i64::MIN) && (tv_nsec < 0 && tv_nsec > -1_000_000_000) {
63 (tv_sec - 1, tv_nsec + 1_000_000_000)
64 } else {
65 (tv_sec, tv_nsec)
66 };
67 if tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64 {
68 Ok(unsafe { Self::new_unchecked(tv_sec, tv_nsec) })
69 } else {
70 Err(io::const_error!(io::ErrorKind::InvalidData, "invalid timestamp"))
71 }
72 }
73
74 #[allow(dead_code)]
75 pub fn now(clock: libc::clockid_t) -> Timespec {
76 use crate::mem::MaybeUninit;
77 use crate::sys::cvt;
78
79 #[cfg(all(
81 target_os = "linux",
82 target_env = "gnu",
83 target_pointer_width = "32",
84 not(target_arch = "riscv32")
85 ))]
86 {
87 use crate::sys::weak::weak;
88
89 weak!(
92 fn __clock_gettime64(
93 clockid: libc::clockid_t,
94 tp: *mut __timespec64,
95 ) -> libc::c_int;
96 );
97
98 if let Some(clock_gettime64) = __clock_gettime64.get() {
99 let mut t = MaybeUninit::uninit();
100 cvt(unsafe { clock_gettime64(clock, t.as_mut_ptr()) }).unwrap();
101 let t = unsafe { t.assume_init() };
102 return Timespec::new(t.tv_sec as i64, t.tv_nsec as i64).unwrap();
103 }
104 }
105
106 let mut t = MaybeUninit::uninit();
107 cvt(unsafe { libc::clock_gettime(clock, t.as_mut_ptr()) }).unwrap();
108 let t = unsafe { t.assume_init() };
109 Timespec::new(t.tv_sec as i64, t.tv_nsec as i64).unwrap()
110 }
111
112 pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
113 fn sub_ge_to_unsigned(a: i64, b: i64) -> u64 {
115 debug_assert!(a >= b);
116 a.wrapping_sub(b).cast_unsigned()
117 }
118
119 if self >= other {
120 let (secs, nsec) = if self.tv_nsec.as_inner() >= other.tv_nsec.as_inner() {
121 (
122 sub_ge_to_unsigned(self.tv_sec, other.tv_sec),
123 self.tv_nsec.as_inner() - other.tv_nsec.as_inner(),
124 )
125 } else {
126 debug_assert!(self.tv_nsec < other.tv_nsec);
128 debug_assert!(self.tv_sec > other.tv_sec);
129 debug_assert!(self.tv_sec > i64::MIN);
130 (
131 sub_ge_to_unsigned(self.tv_sec - 1, other.tv_sec),
132 self.tv_nsec.as_inner() + (NSEC_PER_SEC as u32) - other.tv_nsec.as_inner(),
133 )
134 };
135
136 Ok(Duration::new(secs, nsec))
137 } else {
138 match other.sub_timespec(self) {
139 Ok(d) => Err(d),
140 Err(d) => Ok(d),
141 }
142 }
143 }
144
145 pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> {
146 let mut secs = self.tv_sec.checked_add_unsigned(other.as_secs())?;
147
148 let mut nsec = other.subsec_nanos() + self.tv_nsec.as_inner();
151 if nsec >= NSEC_PER_SEC as u32 {
152 nsec -= NSEC_PER_SEC as u32;
153 secs = secs.checked_add(1)?;
154 }
155 Some(unsafe { Timespec::new_unchecked(secs, nsec.into()) })
156 }
157
158 pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> {
159 let mut secs = self.tv_sec.checked_sub_unsigned(other.as_secs())?;
160
161 let mut nsec = self.tv_nsec.as_inner() as i32 - other.subsec_nanos() as i32;
163 if nsec < 0 {
164 nsec += NSEC_PER_SEC as i32;
165 secs = secs.checked_sub(1)?;
166 }
167 Some(unsafe { Timespec::new_unchecked(secs, nsec.into()) })
168 }
169
170 #[allow(dead_code)]
171 pub fn to_timespec(&self) -> Option<libc::timespec> {
172 Some({
173 let mut ts = libc::timespec::default();
174 ts.tv_sec = self.tv_sec.try_into().ok()?;
175 ts.tv_nsec = self.tv_nsec.as_inner().try_into().ok()?;
176 ts
177 })
178 }
179
180 #[cfg(target_os = "nto")]
183 pub(in crate::sys) fn to_timespec_capped(&self) -> Option<libc::timespec> {
184 if (self.tv_nsec.as_inner() as u64)
186 .checked_add((self.tv_sec as u64).checked_mul(NSEC_PER_SEC)?)
187 .is_none()
188 {
189 return None;
190 }
191 self.to_timespec()
192 }
193
194 #[cfg(all(
195 target_os = "linux",
196 target_env = "gnu",
197 target_pointer_width = "32",
198 not(target_arch = "riscv32")
199 ))]
200 pub fn to_timespec64(&self) -> __timespec64 {
201 __timespec64::new(self.tv_sec, self.tv_nsec.as_inner() as _)
202 }
203}
204
205#[cfg(all(
206 target_os = "linux",
207 target_env = "gnu",
208 target_pointer_width = "32",
209 not(target_arch = "riscv32")
210))]
211#[repr(C)]
212pub(crate) struct __timespec64 {
213 pub(crate) tv_sec: i64,
214 #[cfg(target_endian = "big")]
215 _padding: i32,
216 pub(crate) tv_nsec: i32,
217 #[cfg(target_endian = "little")]
218 _padding: i32,
219}
220
221#[cfg(all(
222 target_os = "linux",
223 target_env = "gnu",
224 target_pointer_width = "32",
225 not(target_arch = "riscv32")
226))]
227impl __timespec64 {
228 pub(crate) fn new(tv_sec: i64, tv_nsec: i32) -> Self {
229 Self { tv_sec, tv_nsec, _padding: 0 }
230 }
231}