use std::time::Duration; use std::ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Div, DivAssign}; /// Type to use for storing femtoseconds /// /// In webassembly, using u128 results in exceedingly slow runtimes, so we use u64 instead /// which is enough for 5 hours of simulation time. #[cfg(not(target_arch = "wasm32"))] type Femtos = u128; #[cfg(target_arch = "wasm32")] type Femtos = u64; /// Represents a duration of time in femtoseconds /// /// The `ClockDuration` type is used to represent lengths of time and is /// intentionally similar to `std::time::Duration`, but which records /// time as femtoseconds to keep accurancy when dealing with partial /// nanosecond clock divisons. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct ClockDuration { femtos: Femtos, } impl ClockDuration { pub const ZERO: Self = Self::from_femtos(0); pub const MAX: Self = Self::from_femtos(Femtos::MAX); pub const FEMTOS_PER_SEC: Femtos = 1_000_000_000_000_000; pub const FEMTOS_PER_MILLISEC: Femtos = 1_000_000_000_000; pub const FEMTOS_PER_MICROSEC: Femtos = 1_000_000_000; pub const FEMTOS_PER_NANOSEC: Femtos = 1_000_000; pub const FEMTOS_PER_PICOSEC: Femtos = 1_000; #[inline] pub const fn from_secs(secs: u64) -> Self { Self { femtos: secs as Femtos * Self::FEMTOS_PER_SEC, } } #[inline] pub const fn from_millis(millisecs: u64) -> Self { Self { femtos: millisecs as Femtos * Self::FEMTOS_PER_MILLISEC, } } #[inline] pub const fn from_micros(microsecs: u64) -> Self { Self { femtos: microsecs as Femtos * Self::FEMTOS_PER_MICROSEC, } } #[inline] pub const fn from_nanos(nanosecs: u64) -> Self { Self { femtos: nanosecs as Femtos * Self::FEMTOS_PER_NANOSEC, } } #[inline] pub const fn from_picos(picosecs: u128) -> Self { Self { femtos: picosecs as Femtos * Self::FEMTOS_PER_PICOSEC, } } #[inline] pub const fn from_femtos(femtos: Femtos) -> Self { Self { femtos, } } #[inline] pub const fn as_secs(self) -> u64 { (self.femtos / Self::FEMTOS_PER_SEC) as u64 } #[inline] pub const fn as_millis(self) -> u64 { (self.femtos / Self::FEMTOS_PER_MILLISEC) as u64 } #[inline] pub const fn as_micros(self) -> u64 { (self.femtos / Self::FEMTOS_PER_MICROSEC) as u64 } #[inline] pub const fn as_nanos(self) -> u64 { (self.femtos / Self::FEMTOS_PER_NANOSEC) as u64 } #[inline] #[allow(clippy::unnecessary_cast)] pub const fn as_picos(self) -> u128 { (self.femtos / Self::FEMTOS_PER_PICOSEC) as u128 } #[inline] pub const fn as_femtos(self) -> Femtos { self.femtos } #[inline] pub const fn checked_add(self, rhs: Self) -> Option { match self.femtos.checked_add(rhs.femtos) { Some(femtos) => Some(Self::from_femtos(femtos)), None => None, } } #[inline] pub const fn checked_sub(self, rhs: Self) -> Option { match self.femtos.checked_sub(rhs.femtos) { Some(femtos) => Some(Self::from_femtos(femtos)), None => None, } } } impl Add for ClockDuration { type Output = Self; fn add(self, rhs: Self) -> Self::Output { self.checked_add(rhs).expect("clock duration overflow during addition") } } impl AddAssign for ClockDuration { fn add_assign(&mut self, rhs: Self) { *self = *self + rhs; } } impl Sub for ClockDuration { type Output = Self; fn sub(self, rhs: Self) -> Self::Output { self.checked_sub(rhs).expect("clock duration overflow during subtraction") } } impl SubAssign for ClockDuration { fn sub_assign(&mut self, rhs: Self) { *self = *self - rhs; } } impl Mul for ClockDuration { type Output = Self; fn mul(self, rhs: u64) -> Self::Output { Self::from_femtos(self.femtos * rhs as Femtos) } } impl MulAssign for ClockDuration { fn mul_assign(&mut self, rhs: u64) { *self = Self::from_femtos(self.femtos * rhs as Femtos); } } impl Div for ClockDuration { type Output = Self; fn div(self, rhs: u64) -> Self::Output { Self::from_femtos(self.femtos / rhs as Femtos) } } impl DivAssign for ClockDuration { fn div_assign(&mut self, rhs: u64) { *self = Self::from_femtos(self.femtos / rhs as Femtos); } } impl Div for ClockDuration { type Output = u64; fn div(self, rhs: ClockDuration) -> Self::Output { (self.femtos / rhs.femtos) as u64 } } impl From for Duration { fn from(value: ClockDuration) -> Self { Duration::from_nanos(value.as_nanos()) } } impl From for ClockDuration { fn from(value: Duration) -> Self { ClockDuration::from_nanos(value.as_nanos() as u64) } } /// Represents time from the start of the simulation /// /// `ClockTime` is for representing the current running clock. It uses a /// duration to represent the time from simulation start, and is monotonic. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct ClockTime(ClockDuration); impl ClockTime { pub const START: Self = Self(ClockDuration::ZERO); pub const FOREVER: Self = Self(ClockDuration::MAX); #[inline] pub const fn as_duration(self) -> ClockDuration { self.0 } #[inline] pub fn duration_since(self, other: Self) -> ClockDuration { self.0 - other.0 } #[inline] pub const fn checked_add(self, duration: ClockDuration) -> Option { match self.0.checked_add(duration) { Some(duration) => Some(Self(duration)), None => None, } } #[inline] pub const fn checked_sub(self, duration: ClockDuration) -> Option { match self.0.checked_sub(duration) { Some(duration) => Some(Self(duration)), None => None, } } } impl Add for ClockTime { type Output = Self; fn add(self, rhs: ClockDuration) -> Self::Output { Self(self.0.add(rhs)) } } impl AddAssign for ClockTime { fn add_assign(&mut self, rhs: ClockDuration) { *self = Self(self.0.add(rhs)); } } /// Represents a frequency in Hz /// /// Clocks are usually given as a frequency, but durations are needed when dealing with clocks /// and clock durations. This type makes it easier to create a clock of a given frequency and /// convert it to a `ClockDuration` #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Frequency { hertz: u32, } impl Frequency { #[inline] pub const fn from_hz(hertz: u32) -> Self { Self { hertz, } } #[inline] pub const fn from_khz(khz: u32) -> Self { Self { hertz: khz * 1_000, } } #[inline] pub const fn from_mhz(mhz: u32) -> Self { Self { hertz: mhz * 1_000_000, } } #[inline] pub const fn as_hz(self) -> u32 { self.hertz } #[inline] pub const fn as_khz(self) -> u32 { self.hertz / 1_000 } #[inline] pub const fn as_mhz(self) -> u32 { self.hertz / 1_000_000 } #[inline] pub const fn period_duration(self) -> ClockDuration { ClockDuration::from_femtos(ClockDuration::FEMTOS_PER_SEC / self.hertz as Femtos) } } impl Mul for Frequency { type Output = Self; fn mul(self, rhs: u32) -> Self::Output { Self::from_hz(self.hertz * rhs) } } impl MulAssign for Frequency { fn mul_assign(&mut self, rhs: u32) { *self = Self::from_hz(self.hertz * rhs); } } impl Div for Frequency { type Output = Self; fn div(self, rhs: u32) -> Self::Output { Self::from_hz(self.hertz / rhs) } } impl DivAssign for Frequency { fn div_assign(&mut self, rhs: u32) { *self = Self::from_hz(self.hertz / rhs); } }