//! Time handling structures and functions use core::{ ops::{Add, Sub}, time::Duration, }; use abi_lib::SyscallRegister; /// Number of nanoseconds in each second pub const NANOSECONDS_IN_SECOND: u64 = 1_000_000_000; /// Number of microseconds in each second pub const MICROSECONDS_IN_SECOND: u64 = 1_000_000; /// Number of milliseconds in each second pub const MILLISECONDS_IN_SECOND: u64 = 1_000; /// Represents a point of time as measured by some system clock #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] #[repr(C)] pub struct SystemTime { seconds: u64, nanoseconds: u64, } /// Represents a certain clock type #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum ClockType { /// Monotonically increasing clock since some arbitrary point in the past Monotonic, /// "Real-world" clock, subject to jumps RealTime, } impl SyscallRegister for ClockType { fn into_syscall_register(self) -> usize { match self { Self::Monotonic => 1, Self::RealTime => 2, } } fn from_syscall_register(value: usize) -> Self { match value { 1 => Self::Monotonic, 2 => Self::RealTime, _ => unreachable!(), } } } impl SystemTime { /// Zero time, also represents UNIX Epoch for real-world clocks pub const ZERO: Self = Self { seconds: 0, nanoseconds: 0, }; /// Constructs a new [SystemTime] from seconds and nanoseconds, without performing /// any adjustments. /// /// # Safety /// /// The caller must guarantee the `nanoseconds` value does not violate the invariant that /// `nanoseconds` is less than [NANOSECONDS_IN_SECOND]. pub unsafe fn new_unchecked(seconds: u64, nanoseconds: u64) -> Self { Self { seconds, nanoseconds, } } /// Constructs a new [SystemTime] from seconds and nanoseconds. If `nanoseconds` contains extra /// full seconds, those are converted to seconds (i.e. the time is "normalized"). /// /// # Panics /// /// This function will panic if the conversion result overflows [u64] in the `seconds` field. pub fn new(seconds: u64, nanoseconds: u64) -> Self { Self::new_opt(seconds, nanoseconds).unwrap() } /// Constructs a new [SystemTime] from seconds and nanoseconds. This function returns `None` if /// conversion fails. See [SystemTime::new]. pub fn new_opt(mut seconds: u64, nanoseconds: u64) -> Option { seconds = seconds.checked_add(nanoseconds / NANOSECONDS_IN_SECOND)?; Some(Self { seconds, nanoseconds: nanoseconds % NANOSECONDS_IN_SECOND, }) } /// Constructs a [SystemTime] from seconds part only pub fn from_seconds(seconds: u64) -> Self { Self { seconds, nanoseconds: 0, } } /// Returns the number of full milliseconds represented by this structure pub fn as_millis(&self) -> u128 { (self.seconds as u128 * MILLISECONDS_IN_SECOND as u128) + (self.nanoseconds as u128 / 1000000) } /// Returns the number of sub-second nanoseconds #[inline(always)] pub fn subsec_nanos(&self) -> u64 { self.nanoseconds } /// Returns the number of full seconds #[inline(always)] pub fn seconds(&self) -> u64 { self.seconds } /// Adds a [Duration] to this [SystemTime], returns `None` if the addition overflows pub fn checked_add_duration(&self, other: &Duration) -> Option { let nanoseconds = self.nanoseconds.checked_add(other.subsec_nanos() as u64)?; let seconds = nanoseconds / NANOSECONDS_IN_SECOND; let nanoseconds = nanoseconds % NANOSECONDS_IN_SECOND; let seconds = self .seconds .checked_add(other.as_secs())? .checked_add(seconds)?; Some(Self { seconds, nanoseconds, }) } /// Subtracts a [Duration] from this [SystemTime], returns `None` if the subtraction underflows pub fn checked_sub_duration(&self, other: &Duration) -> Option { let mut seconds = self.seconds.checked_sub(other.as_secs())?; let nanoseconds = if self.nanoseconds < other.subsec_nanos() as u64 { seconds = seconds.checked_sub(1)?; NANOSECONDS_IN_SECOND + self.nanoseconds - other.subsec_nanos() as u64 } else { self.nanoseconds - other.subsec_nanos() as u64 }; Some(Self { seconds, nanoseconds, }) } /// Subtracts two [SystemTime]s, returns `Ok(self - other)` if `self >= other`, /// `Err(other - self)` otherwise. pub fn sub_time(&self, other: &SystemTime) -> Result { if self >= other { Ok(self.checked_sub_time(other).unwrap()) } else { match other.sub_time(self) { Ok(d) => Err(d), Err(d) => Ok(d), } } } /// Subtracts two [SystemTime]s, returns `None` if the subtraction underflows pub fn checked_sub_time(&self, other: &SystemTime) -> Option { let mut seconds = self.seconds.checked_sub(other.seconds)?; let nanoseconds = if self.nanoseconds < other.nanoseconds { seconds = seconds.checked_sub(1)?; NANOSECONDS_IN_SECOND + self.nanoseconds - other.nanoseconds } else { self.nanoseconds - other.nanoseconds }; Some(Duration::new(seconds, nanoseconds as u32)) } } impl Add for SystemTime { type Output = SystemTime; #[inline] fn add(self, rhs: Duration) -> Self::Output { self.checked_add_duration(&rhs).unwrap() } } impl Sub for SystemTime { type Output = SystemTime; #[inline] fn sub(self, rhs: Duration) -> Self::Output { self.checked_sub_duration(&rhs).unwrap() } } #[cfg(test)] mod tests { use core::time::Duration; use super::{SystemTime, NANOSECONDS_IN_SECOND}; #[test] fn time_sub_time() { let t0 = SystemTime::new(1, NANOSECONDS_IN_SECOND - 1); let t1 = SystemTime::new(2, 0); assert_eq!(t0.sub_time(&t1).unwrap_err(), Duration::new(0, 1)); assert_eq!(t1.sub_time(&t0).unwrap(), Duration::new(0, 1)); } #[test] fn time_checked_sub_time() { let t0 = SystemTime::new(1, NANOSECONDS_IN_SECOND - 1); let t1 = SystemTime::new(2, 0); assert!(t0.checked_sub_time(&t1).is_none()); assert_eq!(t1.checked_sub_time(&t0).unwrap(), Duration::new(0, 1)); } #[test] fn time_sub_duration() { let t0 = SystemTime::new(2, 1); assert_eq!( t0.checked_sub_duration(&Duration::new(1, 1)).unwrap(), SystemTime::new(1, 0) ); assert_eq!( t0.checked_sub_duration(&Duration::new(1, 2)).unwrap(), SystemTime::new(0, NANOSECONDS_IN_SECOND - 1) ); assert_eq!( t0.checked_sub_duration(&Duration::new(1, NANOSECONDS_IN_SECOND as u32 - 1)) .unwrap(), SystemTime::new(0, 2) ); assert_eq!( t0.checked_sub_duration(&Duration::new(2, 1)).unwrap(), SystemTime::ZERO ); assert!(t0.checked_sub_duration(&Duration::new(2, 2)).is_none()); } #[test] fn time_add_duration() { let t0 = SystemTime::new(2, 1); let t1 = SystemTime::new(u64::MAX, 1); assert_eq!( t0.checked_add_duration(&Duration::new(1, 0)).unwrap(), SystemTime::new(3, 1) ); assert_eq!( t0.checked_add_duration(&Duration::new(1, NANOSECONDS_IN_SECOND as u32 - 1)) .unwrap(), SystemTime::new(4, 0) ); assert_eq!( t1.checked_add_duration(&Duration::new(0, NANOSECONDS_IN_SECOND as u32 - 2)) .unwrap(), SystemTime::new(u64::MAX, NANOSECONDS_IN_SECOND - 1) ); assert!(t1 .checked_add_duration(&Duration::new(0, NANOSECONDS_IN_SECOND as u32 - 1)) .is_none()); assert!(t1.checked_add_duration(&Duration::new(1, 0)).is_none()); } }