use crate::duration::Duration;
use crate::helpers::{assert_conversion_fits, Helpers};
use crate::kind::{Kind, Monotonic, Wrapping};
use core::cmp::Ordering;
use core::marker::PhantomData;
use core::ops;
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Copy, Hash, PartialEq, Eq)]
pub struct Instant<T, const NOM: u64, const DENOM: u64, K> {
ticks: T,
#[cfg_attr(feature = "serde", serde(skip))]
_kind: PhantomData<K>,
}
#[cfg(feature = "postcard_max_size")]
impl<T: postcard::experimental::max_size::MaxSize, const NOM: u64, const DENOM: u64, K>
postcard::experimental::max_size::MaxSize for Instant<T, NOM, DENOM, K>
{
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
}
impl<T: core::fmt::Debug, const NOM: u64, const DENOM: u64, K: Kind> core::fmt::Debug
for Instant<T, NOM, DENOM, K>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct(K::DEBUG_NAME)
.field("ticks", &self.ticks)
.finish()
}
}
macro_rules! impl_instant_shared {
($i:ty) => {
impl<const NOM: u64, const DENOM: u64, K: Kind> Instant<$i, NOM, DENOM, K> {
#[doc = concat!("let _i = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[inline]
pub const fn from_ticks(ticks: $i) -> Self {
const { assert!(NOM > 0) };
const { assert!(DENOM > 0) };
Instant {
ticks,
_kind: PhantomData,
}
}
#[doc = concat!("let i = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(234);")]
#[inline]
pub const fn as_ticks(&self) -> $i {
self.ticks
}
}
#[cfg(feature = "defmt")]
impl<const NOM: u64, const DENOM: u64, K: Kind> defmt::Format for Instant<$i, NOM, DENOM, K> {
fn format(&self, f: defmt::Formatter) {
if NOM == 3_600 && DENOM == 1 {
defmt::write!(f, "{} h", self.ticks)
} else if NOM == 60 && DENOM == 1 {
defmt::write!(f, "{} min", self.ticks)
} else if NOM == 1 && DENOM == 1 {
defmt::write!(f, "{} s", self.ticks)
} else if NOM == 1 && DENOM == 1_000 {
defmt::write!(f, "{} ms", self.ticks)
} else if NOM == 1 && DENOM == 1_000_000 {
defmt::write!(f, "{} us", self.ticks)
} else if NOM == 1 && DENOM == 1_000_000_000 {
defmt::write!(f, "{} ns", self.ticks)
} else if NOM == 1 && DENOM == 1_000_000_000_000 {
defmt::write!(f, "{} ps", self.ticks)
} else {
defmt::write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
}
}
}
impl<const NOM: u64, const DENOM: u64, K: Kind> core::fmt::Display
for Instant<$i, NOM, DENOM, K>
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if NOM == 3_600 && DENOM == 1 {
write!(f, "{} h", self.ticks)
} else if NOM == 60 && DENOM == 1 {
write!(f, "{} min", self.ticks)
} else if NOM == 1 && DENOM == 1 {
write!(f, "{} s", self.ticks)
} else if NOM == 1 && DENOM == 1_000 {
write!(f, "{} ms", self.ticks)
} else if NOM == 1 && DENOM == 1_000_000 {
write!(f, "{} us", self.ticks)
} else if NOM == 1 && DENOM == 1_000_000_000 {
write!(f, "{} ns", self.ticks)
} else if NOM == 1 && DENOM == 1_000_000_000_000 {
write!(f, "{} ps", self.ticks)
} else {
write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
}
}
}
};
}
macro_rules! impl_instant_wrapping {
($i:ty) => {
impl<const NOM: u64, const DENOM: u64> Instant<$i, NOM, DENOM, Wrapping> {
#[doc = concat!("let i1 = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let i2 = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
#[doc = concat!("let i1 = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i),"::MAX);")]
#[doc = concat!("let i2 = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let i1 = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(0);")]
#[doc = concat!("let i2 = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1 << (", stringify!($i), "::BITS - 1));")]
#[inline]
pub const fn const_partial_cmp(self, other: Self) -> Option<Ordering> {
const HALF: $i = <$i>::MAX / 2 + 1;
let v = self.ticks.wrapping_sub(other.ticks);
if v == 0 {
Some(Ordering::Equal)
} else if v < HALF {
Some(Ordering::Greater)
} else if v > HALF {
Some(Ordering::Less)
} else {
None
}
}
#[doc = concat!("const I1: WrappingInstant<", stringify!($i), ", 1, 1_000>")]
#[doc = concat!(" = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i), "::MAX);")]
#[doc = concat!("const I2: WrappingInstant<", stringify!($i), ", 1, 1_000>")]
#[doc = concat!(" = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[inline]
pub const fn is_before(self, other: Self) -> bool {
matches!(self.const_partial_cmp(other), Some(Ordering::Less))
}
#[doc = concat!("const I1: WrappingInstant<", stringify!($i), ", 1, 1_000>")]
#[doc = concat!(" = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("const I2: WrappingInstant<", stringify!($i), ", 1, 1_000>")]
#[doc = concat!(" = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i), "::MAX);")]
#[inline]
pub const fn is_after(self, other: Self) -> bool {
matches!(self.const_partial_cmp(other), Some(Ordering::Greater))
}
#[doc = concat!("let i1 = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let i2 = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
#[inline]
pub const fn checked_duration_since(
self,
other: Self,
) -> Option<Duration<$i, NOM, DENOM>> {
match self.const_partial_cmp(other) {
Some(Ordering::Greater) | Some(Ordering::Equal) => {
Some(Duration::<$i, NOM, DENOM>::from_ticks(
self.ticks.wrapping_sub(other.ticks),
))
}
Some(Ordering::Less) | None => None,
}
}
#[doc = concat!("let i = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let i = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
#[doc = concat!("assert_eq!(i.convert_sub_duration(d).unwrap().as_ticks(), ", stringify!($i), "::MAX);")]
#[doc = concat!("let i = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 500>::from_ticks(", stringify!($i),"::MAX / 2 + 1);")]
pub const fn convert_sub_duration<const O_NOM: u64, const O_DENOM: u64>(
self,
other: Duration<$i, O_NOM, O_DENOM>,
) -> Option<Self> {
assert_conversion_fits!(
$i,
Helpers::<NOM, DENOM, O_NOM, O_DENOM>,
"Instant::convert_sub_duration"
);
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
Some(Self::from_ticks(
self.ticks.wrapping_sub(other.as_ticks()),
))
} else {
if let Some(lh) = other
.as_ticks()
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
{
let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
Some(Self::from_ticks(self.ticks.wrapping_sub(ticks)))
} else {
None
}
}
}
#[doc = concat!("let i = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let i = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i), "::MAX);")]
#[doc = concat!("let i = WrappingInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 500>::from_ticks(", stringify!($i),"::MAX / 2 + 1);")]
pub const fn convert_add_duration<const O_NOM: u64, const O_DENOM: u64>(
self,
other: Duration<$i, O_NOM, O_DENOM>,
) -> Option<Self> {
assert_conversion_fits!(
$i,
Helpers::<NOM, DENOM, O_NOM, O_DENOM>,
"Instant::convert_add_duration"
);
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
Some(Self::from_ticks(
self.ticks.wrapping_add(other.as_ticks()),
))
} else {
if let Some(lh) = other
.as_ticks()
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
{
let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
Some(Self::from_ticks(self.ticks.wrapping_add(ticks)))
} else {
None
}
}
}
}
impl<const NOM: u64, const DENOM: u64> PartialOrd for Instant<$i, NOM, DENOM, Wrapping> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.const_partial_cmp(*other)
}
}
impl<const NOM: u64, const DENOM: u64> ops::Sub<Duration<$i, NOM, DENOM>>
for Instant<$i, NOM, DENOM, Wrapping>
{
type Output = Self;
#[inline]
#[track_caller]
fn sub(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
if let Some(v) = self.convert_sub_duration(other) {
v
} else {
panic!("Sub failed! Overflow");
}
}
}
impl<const NOM: u64, const DENOM: u64> ops::Add<Duration<$i, NOM, DENOM>>
for Instant<$i, NOM, DENOM, Wrapping>
{
type Output = Self;
#[inline]
#[track_caller]
fn add(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
if let Some(v) = self.convert_add_duration(other) {
v
} else {
panic!("Add failed! Overflow");
}
}
}
};
}
macro_rules! impl_instant_monotonic {
($i:ty) => {
impl<const NOM: u64, const DENOM: u64> Instant<$i, NOM, DENOM, Monotonic> {
#[doc = concat!("let i1 = MonotonicInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let i2 = MonotonicInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
#[inline]
pub const fn const_cmp(self, other: Self) -> Ordering {
if self.ticks < other.ticks {
Ordering::Less
} else if self.ticks > other.ticks {
Ordering::Greater
} else {
Ordering::Equal
}
}
#[doc = concat!("const I1: MonotonicInstant<", stringify!($i), ", 1, 1_000>")]
#[doc = concat!(" = MonotonicInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("const I2: MonotonicInstant<", stringify!($i), ", 1, 1_000>")]
#[doc = concat!(" = MonotonicInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
#[inline]
pub const fn is_before(self, other: Self) -> bool {
self.ticks < other.ticks
}
#[doc = concat!("const I1: MonotonicInstant<", stringify!($i), ", 1, 1_000>")]
#[doc = concat!(" = MonotonicInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
#[doc = concat!("const I2: MonotonicInstant<", stringify!($i), ", 1, 1_000>")]
#[doc = concat!(" = MonotonicInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[inline]
pub const fn is_after(self, other: Self) -> bool {
self.ticks > other.ticks
}
#[doc = concat!("let i = MonotonicInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(11);")]
#[inline]
pub const fn duration_since_epoch(self) -> Duration<$i, NOM, DENOM> {
Duration::<$i, NOM, DENOM>::from_ticks(self.as_ticks())
}
#[doc = concat!("let i1 = MonotonicInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let i2 = MonotonicInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
#[inline]
pub const fn checked_duration_since(
self,
other: Self,
) -> Option<Duration<$i, NOM, DENOM>> {
match self.ticks.checked_sub(other.ticks) {
Some(ticks) => Some(Duration::<$i, NOM, DENOM>::from_ticks(ticks)),
None => None,
}
}
#[doc = concat!("let i = MonotonicInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let i = MonotonicInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
pub const fn checked_sub_duration<const O_NOM: u64, const O_DENOM: u64>(
self,
other: Duration<$i, O_NOM, O_DENOM>,
) -> Option<Self> {
assert_conversion_fits!(
$i,
Helpers::<NOM, DENOM, O_NOM, O_DENOM>,
"Instant::checked_sub_duration"
);
match Duration::<$i, NOM, DENOM>::from_ticks(self.ticks).checked_sub(other) {
Some(d) => Some(Self::from_ticks(d.as_ticks())),
None => None,
}
}
#[doc = concat!("let i = MonotonicInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let i = MonotonicInstant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i), "::MAX);")]
pub const fn checked_add_duration<const O_NOM: u64, const O_DENOM: u64>(
self,
other: Duration<$i, O_NOM, O_DENOM>,
) -> Option<Self> {
assert_conversion_fits!(
$i,
Helpers::<NOM, DENOM, O_NOM, O_DENOM>,
"Instant::checked_add_duration"
);
match Duration::<$i, NOM, DENOM>::from_ticks(self.ticks).checked_add(other) {
Some(d) => Some(Self::from_ticks(d.as_ticks())),
None => None,
}
}
}
impl<const NOM: u64, const DENOM: u64> Ord for Instant<$i, NOM, DENOM, Monotonic> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.ticks.cmp(&other.ticks)
}
}
impl<const NOM: u64, const DENOM: u64> PartialOrd for Instant<$i, NOM, DENOM, Monotonic> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<const NOM: u64, const DENOM: u64> ops::Sub<Duration<$i, NOM, DENOM>>
for Instant<$i, NOM, DENOM, Monotonic>
{
type Output = Self;
#[inline]
#[track_caller]
fn sub(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
Self::from_ticks(self.ticks - other.as_ticks())
}
}
impl<const NOM: u64, const DENOM: u64> ops::Add<Duration<$i, NOM, DENOM>>
for Instant<$i, NOM, DENOM, Monotonic>
{
type Output = Self;
#[inline]
#[track_caller]
fn add(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
Self::from_ticks(self.ticks + other.as_ticks())
}
}
};
}
macro_rules! impl_instant_ops {
($i:ty, $k:ty) => {
impl<const NOM: u64, const DENOM: u64> ops::Sub<Instant<$i, NOM, DENOM, $k>>
for Instant<$i, NOM, DENOM, $k>
{
type Output = Duration<$i, NOM, DENOM>;
#[inline]
#[track_caller]
fn sub(self, other: Self) -> Self::Output {
if let Some(v) = self.checked_duration_since(other) {
v
} else {
panic!("Sub failed! Other is not before self");
}
}
}
};
}
impl_instant_shared!(u32);
impl_instant_shared!(u64);
impl_instant_wrapping!(u32);
impl_instant_wrapping!(u64);
impl_instant_monotonic!(u32);
impl_instant_monotonic!(u64);
impl_instant_ops!(u32, Wrapping);
impl_instant_ops!(u64, Wrapping);
impl_instant_ops!(u32, Monotonic);
impl_instant_ops!(u64, Monotonic);
impl<T, D, const NOM: u64, const DENOM: u64, K> ops::SubAssign<D> for Instant<T, NOM, DENOM, K>
where
Self: Copy + ops::Sub<D, Output = Self>,
{
#[inline]
#[track_caller]
fn sub_assign(&mut self, other: D) {
*self = *self - other;
}
}
impl<T, D, const NOM: u64, const DENOM: u64, K> ops::AddAssign<D> for Instant<T, NOM, DENOM, K>
where
Self: Copy + ops::Add<D, Output = Self>,
{
#[inline]
#[track_caller]
fn add_assign(&mut self, other: D) {
*self = *self + other;
}
}
impl<const NOM: u64, const DENOM: u64, K: Kind> ops::Sub<Duration<u32, NOM, DENOM>>
for Instant<u64, NOM, DENOM, K>
where
Instant<u64, NOM, DENOM, K>: ops::Sub<Duration<u64, NOM, DENOM>, Output = Self>,
{
type Output = Self;
#[inline]
#[track_caller]
fn sub(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
self - Duration::<u64, NOM, DENOM>::from(other)
}
}
impl<const NOM: u64, const DENOM: u64, K: Kind> ops::Add<Duration<u32, NOM, DENOM>>
for Instant<u64, NOM, DENOM, K>
where
Instant<u64, NOM, DENOM, K>: ops::Add<Duration<u64, NOM, DENOM>, Output = Self>,
{
type Output = Self;
#[inline]
#[track_caller]
fn add(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
self + Duration::<u64, NOM, DENOM>::from(other)
}
}