summaryrefslogtreecommitdiff
path: root/vendor/rustix/src/timespec.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/rustix/src/timespec.rs')
-rw-r--r--vendor/rustix/src/timespec.rs428
1 files changed, 428 insertions, 0 deletions
diff --git a/vendor/rustix/src/timespec.rs b/vendor/rustix/src/timespec.rs
new file mode 100644
index 00000000..f5404ba4
--- /dev/null
+++ b/vendor/rustix/src/timespec.rs
@@ -0,0 +1,428 @@
+//! `Timespec` and related types, which are used by multiple public API
+//! modules.
+
+#![allow(dead_code)]
+
+use core::num::TryFromIntError;
+use core::ops::{Add, AddAssign, Neg, Sub, SubAssign};
+use core::time::Duration;
+
+use crate::backend::c;
+#[allow(unused)]
+use crate::ffi;
+#[cfg(not(fix_y2038))]
+use core::ptr::null;
+
+/// `struct timespec`—A quantity of time in seconds plus nanoseconds.
+#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
+#[repr(C)]
+pub struct Timespec {
+ /// Seconds.
+ pub tv_sec: Secs,
+
+ /// Nanoseconds. Must be less than 1_000_000_000.
+ ///
+ /// When passed to [`rustix::fs::utimensat`], this field may instead be
+ /// assigned the values [`UTIME_NOW`] or [`UTIME_OMIT`].
+ ///
+ /// [`UTIME_NOW`]: crate::fs::UTIME_NOW
+ /// [`UTIME_OMIT`]: crate::fs::UTIME_OMIT
+ /// [`rustix::fs::utimensat`]: crate::fs::utimensat
+ pub tv_nsec: Nsecs,
+}
+
+/// A type for the `tv_sec` field of [`Timespec`].
+pub type Secs = i64;
+
+/// A type for the `tv_nsec` field of [`Timespec`].
+#[cfg(any(
+ fix_y2038,
+ linux_raw,
+ all(libc, target_arch = "x86_64", target_pointer_width = "32")
+))]
+pub type Nsecs = i64;
+
+/// A type for the `tv_nsec` field of [`Timespec`].
+#[cfg(all(
+ not(fix_y2038),
+ libc,
+ not(all(target_arch = "x86_64", target_pointer_width = "32"))
+))]
+pub type Nsecs = ffi::c_long;
+
+impl Timespec {
+ /// Checked `Timespec` addition. Returns `None` if overflow occurred.
+ ///
+ /// # Panics
+ ///
+ /// If `0 <= .tv_nsec < 1_000_000_000` doesn't hold, this function may
+ /// panic or return unexpected results.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use rustix::event::Timespec;
+ ///
+ /// assert_eq!(
+ /// Timespec {
+ /// tv_sec: 1,
+ /// tv_nsec: 2
+ /// }
+ /// .checked_add(Timespec {
+ /// tv_sec: 30,
+ /// tv_nsec: 40
+ /// }),
+ /// Some(Timespec {
+ /// tv_sec: 31,
+ /// tv_nsec: 42
+ /// })
+ /// );
+ /// assert_eq!(
+ /// Timespec {
+ /// tv_sec: 0,
+ /// tv_nsec: 999_999_999
+ /// }
+ /// .checked_add(Timespec {
+ /// tv_sec: 0,
+ /// tv_nsec: 2
+ /// }),
+ /// Some(Timespec {
+ /// tv_sec: 1,
+ /// tv_nsec: 1
+ /// })
+ /// );
+ /// assert_eq!(
+ /// Timespec {
+ /// tv_sec: i64::MAX,
+ /// tv_nsec: 999_999_999
+ /// }
+ /// .checked_add(Timespec {
+ /// tv_sec: 0,
+ /// tv_nsec: 1
+ /// }),
+ /// None
+ /// );
+ /// ```
+ pub const fn checked_add(self, rhs: Self) -> Option<Self> {
+ if let Some(mut tv_sec) = self.tv_sec.checked_add(rhs.tv_sec) {
+ let mut tv_nsec = self.tv_nsec + rhs.tv_nsec;
+ if tv_nsec >= 1_000_000_000 {
+ tv_nsec -= 1_000_000_000;
+ if let Some(carried_sec) = tv_sec.checked_add(1) {
+ tv_sec = carried_sec;
+ } else {
+ return None;
+ }
+ }
+ Some(Self { tv_sec, tv_nsec })
+ } else {
+ None
+ }
+ }
+
+ /// Checked `Timespec` subtraction. Returns `None` if overflow occurred.
+ ///
+ /// # Panics
+ ///
+ /// If `0 <= .tv_nsec < 1_000_000_000` doesn't hold, this function may
+ /// panic or return unexpected results.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use rustix::event::Timespec;
+ ///
+ /// assert_eq!(
+ /// Timespec {
+ /// tv_sec: 31,
+ /// tv_nsec: 42
+ /// }
+ /// .checked_sub(Timespec {
+ /// tv_sec: 30,
+ /// tv_nsec: 40
+ /// }),
+ /// Some(Timespec {
+ /// tv_sec: 1,
+ /// tv_nsec: 2
+ /// })
+ /// );
+ /// assert_eq!(
+ /// Timespec {
+ /// tv_sec: 1,
+ /// tv_nsec: 1
+ /// }
+ /// .checked_sub(Timespec {
+ /// tv_sec: 0,
+ /// tv_nsec: 2
+ /// }),
+ /// Some(Timespec {
+ /// tv_sec: 0,
+ /// tv_nsec: 999_999_999
+ /// })
+ /// );
+ /// assert_eq!(
+ /// Timespec {
+ /// tv_sec: i64::MIN,
+ /// tv_nsec: 0
+ /// }
+ /// .checked_sub(Timespec {
+ /// tv_sec: 0,
+ /// tv_nsec: 1
+ /// }),
+ /// None
+ /// );
+ /// ```
+ pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
+ if let Some(mut tv_sec) = self.tv_sec.checked_sub(rhs.tv_sec) {
+ let mut tv_nsec = self.tv_nsec - rhs.tv_nsec;
+ if tv_nsec < 0 {
+ tv_nsec += 1_000_000_000;
+ if let Some(borrowed_sec) = tv_sec.checked_sub(1) {
+ tv_sec = borrowed_sec;
+ } else {
+ return None;
+ }
+ }
+ Some(Self { tv_sec, tv_nsec })
+ } else {
+ None
+ }
+ }
+
+ /// Convert from `Timespec` to `c::c_int` milliseconds, rounded up.
+ pub(crate) fn as_c_int_millis(&self) -> Option<c::c_int> {
+ let secs = self.tv_sec;
+ if secs < 0 {
+ return None;
+ }
+ secs.checked_mul(1000)
+ .and_then(|millis| {
+ // Add the nanoseconds, converted to milliseconds, rounding up.
+ // With Rust 1.73.0 this can use `div_ceil`.
+ millis.checked_add((i64::from(self.tv_nsec) + 999_999) / 1_000_000)
+ })
+ .and_then(|millis| c::c_int::try_from(millis).ok())
+ }
+}
+
+impl TryFrom<Timespec> for Duration {
+ type Error = TryFromIntError;
+
+ fn try_from(ts: Timespec) -> Result<Self, Self::Error> {
+ Ok(Self::new(ts.tv_sec.try_into()?, ts.tv_nsec as _))
+ }
+}
+
+impl TryFrom<Duration> for Timespec {
+ type Error = TryFromIntError;
+
+ fn try_from(dur: Duration) -> Result<Self, Self::Error> {
+ Ok(Self {
+ tv_sec: dur.as_secs().try_into()?,
+ tv_nsec: dur.subsec_nanos() as _,
+ })
+ }
+}
+
+impl Add for Timespec {
+ type Output = Self;
+
+ fn add(self, rhs: Self) -> Self {
+ self.checked_add(rhs)
+ .expect("overflow when adding timespecs")
+ }
+}
+
+impl AddAssign for Timespec {
+ fn add_assign(&mut self, rhs: Self) {
+ *self = *self + rhs;
+ }
+}
+
+impl Sub for Timespec {
+ type Output = Self;
+
+ fn sub(self, rhs: Self) -> Self {
+ self.checked_sub(rhs)
+ .expect("overflow when subtracting timespecs")
+ }
+}
+
+impl SubAssign for Timespec {
+ fn sub_assign(&mut self, rhs: Self) {
+ *self = *self - rhs;
+ }
+}
+
+impl Neg for Timespec {
+ type Output = Self;
+
+ fn neg(self) -> Self {
+ Self::default() - self
+ }
+}
+
+/// On 32-bit glibc platforms, `timespec` has anonymous padding fields, which
+/// Rust doesn't support yet (see `unnamed_fields`), so we define our own
+/// struct with explicit padding, with bidirectional `From` impls.
+#[cfg(fix_y2038)]
+#[repr(C)]
+#[derive(Debug, Clone)]
+pub(crate) struct LibcTimespec {
+ pub(crate) tv_sec: Secs,
+
+ #[cfg(target_endian = "big")]
+ padding: core::mem::MaybeUninit<u32>,
+
+ pub(crate) tv_nsec: i32,
+
+ #[cfg(target_endian = "little")]
+ padding: core::mem::MaybeUninit<u32>,
+}
+
+#[cfg(fix_y2038)]
+impl From<LibcTimespec> for Timespec {
+ #[inline]
+ fn from(t: LibcTimespec) -> Self {
+ Self {
+ tv_sec: t.tv_sec,
+ tv_nsec: t.tv_nsec as _,
+ }
+ }
+}
+
+#[cfg(fix_y2038)]
+impl From<Timespec> for LibcTimespec {
+ #[inline]
+ fn from(t: Timespec) -> Self {
+ Self {
+ tv_sec: t.tv_sec,
+ tv_nsec: t.tv_nsec as _,
+ padding: core::mem::MaybeUninit::uninit(),
+ }
+ }
+}
+
+#[cfg(not(fix_y2038))]
+pub(crate) fn as_libc_timespec_ptr(timespec: &Timespec) -> *const c::timespec {
+ #[cfg(test)]
+ {
+ assert_eq_size!(Timespec, c::timespec);
+ }
+ crate::utils::as_ptr(timespec).cast::<c::timespec>()
+}
+
+#[cfg(not(fix_y2038))]
+pub(crate) fn as_libc_timespec_mut_ptr(
+ timespec: &mut core::mem::MaybeUninit<Timespec>,
+) -> *mut c::timespec {
+ #[cfg(test)]
+ {
+ assert_eq_size!(Timespec, c::timespec);
+ }
+ timespec.as_mut_ptr().cast::<c::timespec>()
+}
+
+#[cfg(not(fix_y2038))]
+pub(crate) fn option_as_libc_timespec_ptr(timespec: Option<&Timespec>) -> *const c::timespec {
+ match timespec {
+ None => null(),
+ Some(timespec) => as_libc_timespec_ptr(timespec),
+ }
+}
+
+/// As described [here], Apple platforms may return a negative nanoseconds
+/// value in some cases; adjust it so that nanoseconds is always in
+/// `0..1_000_000_000`.
+///
+/// [here]: https://github.com/rust-lang/rust/issues/108277#issuecomment-1787057158
+#[cfg(apple)]
+#[inline]
+pub(crate) fn fix_negative_nsecs(
+ mut secs: c::time_t,
+ mut nsecs: c::c_long,
+) -> (c::time_t, c::c_long) {
+ #[cold]
+ fn adjust(secs: &mut c::time_t, nsecs: c::c_long) -> c::c_long {
+ assert!(nsecs >= -1_000_000_000);
+ assert!(*secs < 0);
+ assert!(*secs > c::time_t::MIN);
+ *secs -= 1;
+ nsecs + 1_000_000_000
+ }
+
+ if nsecs < 0 {
+ nsecs = adjust(&mut secs, nsecs);
+ }
+ (secs, nsecs)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[cfg(apple)]
+ #[test]
+ fn test_negative_timestamps() {
+ let mut secs = -59;
+ let mut nsecs = -900_000_000;
+ (secs, nsecs) = fix_negative_nsecs(secs, nsecs);
+ assert_eq!(secs, -60);
+ assert_eq!(nsecs, 100_000_000);
+ (secs, nsecs) = fix_negative_nsecs(secs, nsecs);
+ assert_eq!(secs, -60);
+ assert_eq!(nsecs, 100_000_000);
+ }
+
+ #[test]
+ fn test_sizes() {
+ assert_eq_size!(Secs, u64);
+ const_assert!(core::mem::size_of::<Timespec>() >= core::mem::size_of::<(u64, u32)>());
+ const_assert!(core::mem::size_of::<Nsecs>() >= 4);
+
+ let mut t = Timespec {
+ tv_sec: 0,
+ tv_nsec: 0,
+ };
+
+ // `tv_nsec` needs to be able to hold nanoseconds up to a second.
+ t.tv_nsec = 999_999_999_u32 as _;
+ assert_eq!(t.tv_nsec as u64, 999_999_999_u64);
+
+ // `tv_sec` needs to be able to hold more than 32-bits of seconds.
+ t.tv_sec = 0x1_0000_0000_u64 as _;
+ assert_eq!(t.tv_sec as u64, 0x1_0000_0000_u64);
+ }
+
+ // Test that our workarounds are needed.
+ #[cfg(fix_y2038)]
+ #[test]
+ #[allow(deprecated)]
+ fn test_fix_y2038() {
+ assert_eq_size!(libc::time_t, u32);
+ }
+
+ // Test that our workarounds are not needed.
+ #[cfg(not(fix_y2038))]
+ #[test]
+ fn timespec_layouts() {
+ use crate::backend::c;
+ check_renamed_struct!(Timespec, timespec, tv_sec, tv_nsec);
+ }
+
+ // Test that `Timespec` matches Linux's `__kernel_timespec`.
+ #[cfg(linux_kernel)]
+ #[test]
+ fn test_against_kernel_timespec() {
+ assert_eq_size!(Timespec, linux_raw_sys::general::__kernel_timespec);
+ assert_eq_align!(Timespec, linux_raw_sys::general::__kernel_timespec);
+ assert_eq!(
+ memoffset::span_of!(Timespec, tv_sec),
+ memoffset::span_of!(linux_raw_sys::general::__kernel_timespec, tv_sec)
+ );
+ assert_eq!(
+ memoffset::span_of!(Timespec, tv_nsec),
+ memoffset::span_of!(linux_raw_sys::general::__kernel_timespec, tv_nsec)
+ );
+ }
+}