| use hermit_abi::{self, timespec}; |
| |
| use crate::cmp::Ordering; |
| use crate::hash::{Hash, Hasher}; |
| use crate::time::Duration; |
| |
| const NSEC_PER_SEC: i32 = 1_000_000_000; |
| |
| #[derive(Copy, Clone, Debug)] |
| pub struct Timespec { |
| pub t: timespec, |
| } |
| |
| impl Timespec { |
| pub const MAX: Timespec = Self::new(i64::MAX, 1_000_000_000 - 1); |
| |
| pub const MIN: Timespec = Self::new(i64::MIN, 0); |
| |
| pub const fn zero() -> Timespec { |
| Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } } |
| } |
| |
| pub const fn new(tv_sec: i64, tv_nsec: i32) -> Timespec { |
| assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC); |
| // SAFETY: The assert above checks tv_nsec is within the valid range |
| Timespec { t: timespec { tv_sec, tv_nsec } } |
| } |
| |
| pub fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> { |
| fn sub_ge_to_unsigned(a: i64, b: i64) -> u64 { |
| debug_assert!(a >= b); |
| a.wrapping_sub(b).cast_unsigned() |
| } |
| |
| if self >= other { |
| // Logic here is identical to Unix version of `Timestamp::sub_timespec`, |
| // check comments there why operations do not overflow. |
| Ok(if self.t.tv_nsec >= other.t.tv_nsec { |
| Duration::new( |
| sub_ge_to_unsigned(self.t.tv_sec, other.t.tv_sec), |
| (self.t.tv_nsec - other.t.tv_nsec) as u32, |
| ) |
| } else { |
| Duration::new( |
| sub_ge_to_unsigned(self.t.tv_sec - 1, other.t.tv_sec), |
| (self.t.tv_nsec + NSEC_PER_SEC - other.t.tv_nsec) as u32, |
| ) |
| }) |
| } else { |
| match other.sub_timespec(self) { |
| Ok(d) => Err(d), |
| Err(d) => Ok(d), |
| } |
| } |
| } |
| |
| pub fn checked_add_duration(&self, other: &Duration) -> Option<Timespec> { |
| let mut secs = self.t.tv_sec.checked_add_unsigned(other.as_secs())?; |
| |
| // Nano calculations can't overflow because nanos are <1B which fit |
| // in a u32. |
| let mut nsec = other.subsec_nanos() + u32::try_from(self.t.tv_nsec).unwrap(); |
| if nsec >= NSEC_PER_SEC.try_into().unwrap() { |
| nsec -= u32::try_from(NSEC_PER_SEC).unwrap(); |
| secs = secs.checked_add(1)?; |
| } |
| Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } }) |
| } |
| |
| pub fn checked_sub_duration(&self, other: &Duration) -> Option<Timespec> { |
| let mut secs = self.t.tv_sec.checked_sub_unsigned(other.as_secs())?; |
| |
| // Similar to above, nanos can't overflow. |
| let mut nsec = self.t.tv_nsec as i32 - other.subsec_nanos() as i32; |
| if nsec < 0 { |
| nsec += NSEC_PER_SEC as i32; |
| secs = secs.checked_sub(1)?; |
| } |
| Some(Timespec { t: timespec { tv_sec: secs, tv_nsec: nsec as _ } }) |
| } |
| } |
| |
| impl PartialEq for Timespec { |
| fn eq(&self, other: &Timespec) -> bool { |
| self.t.tv_sec == other.t.tv_sec && self.t.tv_nsec == other.t.tv_nsec |
| } |
| } |
| |
| impl Eq for Timespec {} |
| |
| impl PartialOrd for Timespec { |
| fn partial_cmp(&self, other: &Timespec) -> Option<Ordering> { |
| Some(self.cmp(other)) |
| } |
| } |
| |
| impl Ord for Timespec { |
| fn cmp(&self, other: &Timespec) -> Ordering { |
| let me = (self.t.tv_sec, self.t.tv_nsec); |
| let other = (other.t.tv_sec, other.t.tv_nsec); |
| me.cmp(&other) |
| } |
| } |
| |
| impl Hash for Timespec { |
| fn hash<H: Hasher>(&self, state: &mut H) { |
| self.t.tv_sec.hash(state); |
| self.t.tv_nsec.hash(state); |
| } |
| } |