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 #[inline]
210 pub(crate) fn to_sec_usec(&self) -> Option<(Secs, u32)> {
211 let mut sec = self.tv_sec;
212 let mut usec = (self.tv_nsec + 999) / 1000;
213 if usec >= 1_000_000 {
214 sec = sec.checked_add(1)?;
215 usec -= 1_000_000;
216 }
217 Some((sec, usec as u32))
218 }
219
220 #[cfg(all(any(libc, target_os = "wasi"), not(windows)))]
223 pub(crate) fn to_timeval(&self) -> crate::io::Result<c::timeval> {
224 let (sec, usec) = self.to_sec_usec().ok_or(crate::io::Errno::INVAL)?;
225 Ok(c::timeval {
226 tv_sec: sec.try_into().map_err(|_| crate::io::Errno::INVAL)?,
227 tv_usec: usec as _,
228 })
229 }
230
231 #[cfg(windows)]
234 pub(crate) fn to_timeval(&self) -> crate::io::Result<c::TIMEVAL> {
235 let (sec, usec) = self.to_sec_usec().ok_or(crate::io::Errno::OPNOTSUPP)?;
236 Ok(c::TIMEVAL {
237 tv_sec: sec.try_into().map_err(|_| crate::io::Errno::OPNOTSUPP)?,
238 tv_usec: usec as _,
239 })
240 }
241}
242
243impl TryFrom<Timespec> for Duration {
244 type Error = TryFromIntError;
245
246 fn try_from(ts: Timespec) -> Result<Self, Self::Error> {
247 Ok(Self::new(ts.tv_sec.try_into()?, ts.tv_nsec as _))
248 }
249}
250
251impl TryFrom<Duration> for Timespec {
252 type Error = TryFromIntError;
253
254 fn try_from(dur: Duration) -> Result<Self, Self::Error> {
255 Ok(Self {
256 tv_sec: dur.as_secs().try_into()?,
257 tv_nsec: dur.subsec_nanos() as _,
258 })
259 }
260}
261
262impl Add for Timespec {
263 type Output = Self;
264
265 fn add(self, rhs: Self) -> Self {
266 self.checked_add(rhs)
267 .expect("overflow when adding timespecs")
268 }
269}
270
271impl AddAssign for Timespec {
272 fn add_assign(&mut self, rhs: Self) {
273 *self = *self + rhs;
274 }
275}
276
277impl Sub for Timespec {
278 type Output = Self;
279
280 fn sub(self, rhs: Self) -> Self {
281 self.checked_sub(rhs)
282 .expect("overflow when subtracting timespecs")
283 }
284}
285
286impl SubAssign for Timespec {
287 fn sub_assign(&mut self, rhs: Self) {
288 *self = *self - rhs;
289 }
290}
291
292impl Neg for Timespec {
293 type Output = Self;
294
295 fn neg(self) -> Self {
296 Self::default() - self
297 }
298}
299
300#[cfg(fix_y2038)]
304#[repr(C)]
305#[derive(Debug, Clone)]
306pub(crate) struct LibcTimespec {
307 pub(crate) tv_sec: Secs,
308
309 #[cfg(target_endian = "big")]
310 padding: core::mem::MaybeUninit<u32>,
311
312 pub(crate) tv_nsec: i32,
313
314 #[cfg(target_endian = "little")]
315 padding: core::mem::MaybeUninit<u32>,
316}
317
318#[cfg(fix_y2038)]
319impl From<LibcTimespec> for Timespec {
320 #[inline]
321 fn from(t: LibcTimespec) -> Self {
322 Self {
323 tv_sec: t.tv_sec,
324 tv_nsec: t.tv_nsec as _,
325 }
326 }
327}
328
329#[cfg(fix_y2038)]
330impl From<Timespec> for LibcTimespec {
331 #[inline]
332 fn from(t: Timespec) -> Self {
333 Self {
334 tv_sec: t.tv_sec,
335 tv_nsec: t.tv_nsec as _,
336 padding: core::mem::MaybeUninit::uninit(),
337 }
338 }
339}
340
341#[cfg(not(fix_y2038))]
342pub(crate) fn as_libc_timespec_ptr(timespec: &Timespec) -> *const c::timespec {
343 #[cfg(test)]
344 {
345 static_assertions::assert_eq_size!(Timespec, c::timespec);
346 }
347 crate::utils::as_ptr(timespec).cast::<c::timespec>()
348}
349
350#[cfg(not(fix_y2038))]
351pub(crate) fn as_libc_timespec_mut_ptr(
352 timespec: &mut core::mem::MaybeUninit<Timespec>,
353) -> *mut c::timespec {
354 #[cfg(test)]
355 {
356 static_assertions::assert_eq_size!(Timespec, c::timespec);
357 }
358 timespec.as_mut_ptr().cast::<c::timespec>()
359}
360
361#[cfg(not(fix_y2038))]
362pub(crate) fn option_as_libc_timespec_ptr(timespec: Option<&Timespec>) -> *const c::timespec {
363 match timespec {
364 None => null(),
365 Some(timespec) => as_libc_timespec_ptr(timespec),
366 }
367}
368
369#[cfg(apple)]
375#[inline]
376pub(crate) fn fix_negative_nsecs(
377 mut secs: c::time_t,
378 mut nsecs: c::c_long,
379) -> (c::time_t, c::c_long) {
380 #[cold]
381 fn adjust(secs: &mut c::time_t, nsecs: c::c_long) -> c::c_long {
382 assert!(nsecs >= -1_000_000_000);
383 assert!(*secs < 0);
384 assert!(*secs > c::time_t::MIN);
385 *secs -= 1;
386 nsecs + 1_000_000_000
387 }
388
389 if nsecs < 0 {
390 nsecs = adjust(&mut secs, nsecs);
391 }
392 (secs, nsecs)
393}
394
395#[cfg(test)]
396mod tests {
397 use super::*;
398
399 #[cfg(apple)]
400 #[test]
401 fn test_negative_timestamps() {
402 let mut secs = -59;
403 let mut nsecs = -900_000_000;
404 (secs, nsecs) = fix_negative_nsecs(secs, nsecs);
405 assert_eq!(secs, -60);
406 assert_eq!(nsecs, 100_000_000);
407 (secs, nsecs) = fix_negative_nsecs(secs, nsecs);
408 assert_eq!(secs, -60);
409 assert_eq!(nsecs, 100_000_000);
410 }
411
412 #[test]
413 fn test_sizes() {
414 static_assertions::assert_eq_size!(Secs, u64);
415 static_assertions::const_assert!(
416 core::mem::size_of::<Timespec>() >= core::mem::size_of::<(u64, u32)>()
417 );
418 static_assertions::const_assert!(core::mem::size_of::<Nsecs>() >= 4);
419
420 let mut t = Timespec {
421 tv_sec: 0,
422 tv_nsec: 0,
423 };
424
425 t.tv_nsec = 999_999_999_u32 as _;
427 assert_eq!(t.tv_nsec as u64, 999_999_999_u64);
428
429 t.tv_sec = 0x1_0000_0000_u64 as _;
431 assert_eq!(t.tv_sec as u64, 0x1_0000_0000_u64);
432 }
433
434 #[cfg(any(libc, target_os = "wasi", windows))]
435 #[test]
436 fn test_to_timeval() {
437 let ts = Timespec {
438 tv_sec: 4,
439 tv_nsec: 999_999_500,
440 };
441 let tv = ts.to_timeval().unwrap();
442 assert_eq!(tv.tv_sec, 5);
443 assert_eq!(tv.tv_usec, 0);
444
445 let ts2 = Timespec {
446 tv_sec: 2,
447 tv_nsec: 500_000_000,
448 };
449 let tv2 = ts2.to_timeval().unwrap();
450 assert_eq!(tv2.tv_sec, 2);
451 assert_eq!(tv2.tv_usec, 500_000);
452 }
453
454 #[cfg(fix_y2038)]
456 #[test]
457 #[allow(deprecated)]
458 fn test_fix_y2038() {
459 static_assertions::assert_eq_size!(libc::time_t, u32);
460 }
461
462 #[cfg(not(fix_y2038))]
464 #[test]
465 fn timespec_layouts() {
466 use crate::backend::c;
467 check_renamed_struct!(Timespec, timespec, tv_sec, tv_nsec);
468 }
469
470 #[cfg(linux_raw_dep)]
472 #[test]
473 fn test_against_kernel_timespec() {
474 static_assertions::assert_eq_size!(Timespec, linux_raw_sys::general::__kernel_timespec);
475 static_assertions::assert_eq_align!(Timespec, linux_raw_sys::general::__kernel_timespec);
476 assert_eq!(
477 memoffset::span_of!(Timespec, tv_sec),
478 memoffset::span_of!(linux_raw_sys::general::__kernel_timespec, tv_sec)
479 );
480 assert_eq!(
481 memoffset::span_of!(Timespec, tv_nsec),
482 memoffset::span_of!(linux_raw_sys::general::__kernel_timespec, tv_nsec)
483 );
484 }
485}