267 lines
8.2 KiB
Rust
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());
|
|
}
|
|
}
|