yggdrasil/lib/abi/src/time.rs

267 lines
8.2 KiB
Rust

//! 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<Self> {
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<Self> {
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<Self> {
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<Duration, Duration> {
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<Duration> {
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<Duration> for SystemTime {
type Output = SystemTime;
#[inline]
fn add(self, rhs: Duration) -> Self::Output {
self.checked_add_duration(&rhs).unwrap()
}
}
impl Sub<Duration> 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());
}
}