forked from libre-chip/fayalite
217 lines
7.1 KiB
Rust
217 lines
7.1 KiB
Rust
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
// See Notices.txt for copyright information
|
|
use std::{
|
|
fmt,
|
|
ops::{Add, AddAssign},
|
|
time::Duration,
|
|
};
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
|
pub struct SimInstant {
|
|
time_since_start: SimDuration,
|
|
}
|
|
|
|
impl Add<SimDuration> for SimInstant {
|
|
type Output = SimInstant;
|
|
|
|
fn add(mut self, rhs: SimDuration) -> Self::Output {
|
|
self += rhs;
|
|
self
|
|
}
|
|
}
|
|
|
|
impl AddAssign<SimDuration> for SimInstant {
|
|
fn add_assign(&mut self, rhs: SimDuration) {
|
|
self.time_since_start += rhs;
|
|
}
|
|
}
|
|
|
|
impl Add<SimInstant> for SimDuration {
|
|
type Output = SimInstant;
|
|
|
|
fn add(self, rhs: SimInstant) -> Self::Output {
|
|
rhs.add(self)
|
|
}
|
|
}
|
|
|
|
impl SimInstant {
|
|
pub const START: SimInstant = SimInstant {
|
|
time_since_start: SimDuration::ZERO,
|
|
};
|
|
}
|
|
|
|
impl fmt::Debug for SimInstant {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
self.time_since_start.fmt(f)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
|
pub struct SimDuration {
|
|
attos: u128,
|
|
}
|
|
|
|
impl AddAssign for SimDuration {
|
|
fn add_assign(&mut self, rhs: SimDuration) {
|
|
*self = *self + rhs;
|
|
}
|
|
}
|
|
|
|
impl Add for SimDuration {
|
|
type Output = SimDuration;
|
|
|
|
fn add(self, rhs: SimDuration) -> Self::Output {
|
|
SimDuration {
|
|
attos: self
|
|
.attos
|
|
.checked_add(rhs.attos)
|
|
.expect("overflow adding durations"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
|
|
pub struct SimDurationParts {
|
|
pub attos: u16,
|
|
pub femtos: u16,
|
|
pub picos: u16,
|
|
pub nanos: u16,
|
|
pub micros: u16,
|
|
pub millis: u16,
|
|
pub secs: u128,
|
|
}
|
|
|
|
macro_rules! impl_duration_units {
|
|
(
|
|
$(
|
|
#[unit_const = $UNIT:ident, from_units = $from_units:ident, units = $units:ident, suffix = $suffix:literal]
|
|
const $log10_units_per_sec:ident: u32 = $log10_units_per_sec_value:expr;
|
|
)*
|
|
) => {
|
|
impl SimDuration {
|
|
$(
|
|
const $log10_units_per_sec: u32 = $log10_units_per_sec_value;
|
|
pub const fn $from_units($units: u128) -> Self {
|
|
Self::from_units_helper::<{ Self::$log10_units_per_sec }>($units)
|
|
}
|
|
)*
|
|
pub const fn into_parts(mut self) -> SimDurationParts {
|
|
$(
|
|
let $units = self.attos / const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) };
|
|
self.attos %= const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) };
|
|
)*
|
|
SimDurationParts {
|
|
$($units: $units as _,)*
|
|
}
|
|
}
|
|
pub const fn from_parts_checked(parts: SimDurationParts) -> Option<Self> {
|
|
let attos = 0u128;
|
|
$(
|
|
let Some(product) = const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) }.checked_mul(parts.$units as u128) else {
|
|
return None;
|
|
};
|
|
let Some(attos) = attos.checked_add(product) else {
|
|
return None;
|
|
};
|
|
)*
|
|
Some(Self {
|
|
attos,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for SimDuration {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let ilog10_attos = match self.attos.checked_ilog10() {
|
|
Some(v) => v,
|
|
None => Self::LOG10_ATTOS_PER_SEC,
|
|
};
|
|
let (suffix, int, fraction, fraction_digits) =
|
|
match Self::LOG10_ATTOS_PER_SEC.saturating_sub(ilog10_attos) {
|
|
$(
|
|
..=Self::$log10_units_per_sec => {
|
|
let divisor = const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) };
|
|
(
|
|
$suffix,
|
|
self.attos / divisor,
|
|
self.attos % divisor,
|
|
(Self::LOG10_ATTOS_PER_SEC - Self::$log10_units_per_sec) as usize,
|
|
)
|
|
},
|
|
)*
|
|
_ => unreachable!(),
|
|
};
|
|
write!(f, "{int}")?;
|
|
if fraction != 0 {
|
|
write!(f, ".{fraction:0fraction_digits$}")?;
|
|
}
|
|
write!(f, " {suffix}")
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
#[test]
|
|
fn test_duration_debug() {
|
|
$(
|
|
assert_eq!(
|
|
format!("{:?}", SimDuration::$from_units(123)),
|
|
concat!("123 ", $suffix)
|
|
);
|
|
assert_eq!(
|
|
format!("{:?}", SimDuration::$from_units(1)),
|
|
concat!("1 ", $suffix),
|
|
);
|
|
let mut v = SimDuration::$from_units(1);
|
|
if v.attos < 1 << 53 {
|
|
v.attos += 1;
|
|
assert_eq!(
|
|
format!("{v:?}"),
|
|
format!("{} {}", v.attos as f64 / 10.0f64.powf((SimDuration::LOG10_ATTOS_PER_SEC - SimDuration::$log10_units_per_sec) as f64), $suffix),
|
|
"1 {} + 1 as == {} as", $suffix, v.attos,
|
|
);
|
|
}
|
|
)*
|
|
}
|
|
};
|
|
}
|
|
|
|
impl_duration_units! {
|
|
#[unit_const = SECOND, from_units = from_secs, units = secs, suffix = "s"]
|
|
const LOG10_SECS_PER_SEC: u32 = 0;
|
|
#[unit_const = MILLISECOND, from_units = from_millis, units = millis, suffix = "ms"]
|
|
const LOG10_MILLIS_PER_SEC: u32 = 3;
|
|
#[unit_const = MICROSECOND, from_units = from_micros, units = micros, suffix = "μs"]
|
|
const LOG10_MICROS_PER_SEC: u32 = 6;
|
|
#[unit_const = NANOSECOND, from_units = from_nanos, units = nanos, suffix = "ns"]
|
|
const LOG10_NANOS_PER_SEC: u32 = 9;
|
|
#[unit_const = PICOSECOND, from_units = from_picos, units = picos, suffix = "ps"]
|
|
const LOG10_PICOS_PER_SEC: u32 = 12;
|
|
#[unit_const = FEMTOSECOND, from_units = from_femtos, units = femtos, suffix = "fs"]
|
|
const LOG10_FEMTOS_PER_SEC: u32 = 15;
|
|
#[unit_const = ATTOSECOND, from_units = from_attos, units = attos, suffix = "as"]
|
|
const LOG10_ATTOS_PER_SEC: u32 = 18;
|
|
}
|
|
|
|
impl SimDuration {
|
|
const fn from_units_helper<const UNITS_PER_SEC: u32>(units: u128) -> Self {
|
|
let Some(attos) =
|
|
units.checked_mul(const { 10u128.pow(Self::LOG10_ATTOS_PER_SEC - UNITS_PER_SEC) })
|
|
else {
|
|
panic!("duration too big");
|
|
};
|
|
Self { attos }
|
|
}
|
|
pub const ZERO: SimDuration = SimDuration::from_secs(0);
|
|
pub const fn from_parts(parts: SimDurationParts) -> Self {
|
|
match Self::from_parts_checked(parts) {
|
|
Some(v) => v,
|
|
None => panic!("duration too big"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Duration> for SimDuration {
|
|
fn from(duration: Duration) -> Self {
|
|
Self::from_nanos(duration.as_nanos())
|
|
}
|
|
}
|