use core::{mem, ops::Add};
#[macro_export]
macro_rules! def_event_slots {
(@__inner $esi: ty, $n: expr$(,)*) => {
pub(super) const LEN: usize = $n;
};
(@__inner $esi: ty, $n: expr$(,)+ $ident: ident, $($other: tt)*) => {
pub const $ident: $esi = <$esi>::new($n);
def_event_slots!(@__inner $esi, $n + 1, $($other)*);
};
(
@__inner $esi: ty,
$n: expr$(,)+
$start_ident: ident..$end_ident: ident $len: expr,
$($other: tt)*
) => {
pub const $start_ident: $esi = <$esi>::new($n);
pub const $end_ident: $esi = <$esi>::new($n + $len - 1);
def_event_slots!(@__inner $esi, $n + $len, $($other)*);
};
($esi: ty, $($contents: tt)*) => {
def_event_slots!(@__inner $esi, 1, $($contents)*,);
}
}
pub type RawTimestamp = u64;
pub type SignedTimestamp = i64;
#[derive(Clone, Copy)]
struct EventSlot<T, E, ESI>
where
T: Copy + Ord + Add + From<RawTimestamp> + Into<RawTimestamp>,
E: Copy + Eq + Default,
ESI: Copy + Eq + From<usize> + Into<usize>,
{
time: T,
event: E,
prev_i: ESI,
next_i: ESI,
}
#[derive(Clone)]
pub struct Schedule<T, E, ESI, const EVENT_SLOTS: usize>
where
T: Copy + Ord + Add + From<RawTimestamp> + Into<RawTimestamp>,
E: Copy + Eq + Default,
ESI: Copy + Eq + From<usize> + Into<usize>,
{
slots: [EventSlot<T, E, ESI>; EVENT_SLOTS],
next_event_time: T,
}
impl<T, E, ESI, const EVENT_SLOTS: usize> Schedule<T, E, ESI, EVENT_SLOTS>
where
T: Copy + Ord + Add + From<RawTimestamp> + Into<RawTimestamp>,
E: Copy + Eq + Default,
ESI: Copy + Eq + From<usize> + Into<usize>,
{
pub fn new() -> Self {
let mut slots = [EventSlot {
time: T::from(0),
event: E::default(),
prev_i: ESI::from(0),
next_i: ESI::from(0),
}; EVENT_SLOTS];
slots[0].time = T::from(RawTimestamp::MAX);
Schedule {
slots,
next_event_time: T::from(RawTimestamp::MAX),
}
}
#[inline]
pub fn next_event(&self) -> Option<E> {
let next_i = self.slots[0].next_i.into();
if next_i == 0 {
None
} else {
Some(self.slots[next_i].event)
}
}
#[inline]
pub fn next_event_time(&self) -> T {
self.next_event_time
}
pub fn pop_pending_event(&mut self, cur_time: T) -> Option<(E, T)> {
if cur_time < self.next_event_time {
return None;
}
let slot = &mut self.slots[self.slots[0].next_i.into()];
slot.time = T::from(0);
let event = slot.event;
let next_i = slot.next_i;
self.slots[0].next_i = next_i;
let next_slot = &mut self.slots[next_i.into()];
next_slot.prev_i = ESI::from(0);
Some((
event,
mem::replace(&mut self.next_event_time, next_slot.time),
))
}
#[inline]
pub fn set_event(&mut self, slot_index: ESI, event: E) {
self.slots[slot_index.into()].event = event;
}
#[allow(clippy::shadow_unrelated)]
pub fn schedule(&mut self, slot_index: ESI, time: T) {
let slot = &mut self.slots[slot_index.into()];
debug_assert!(slot.time == T::from(0));
slot.time = time;
if time <= self.next_event_time {
let next_i = self.slots[0].next_i;
let slot = &mut self.slots[slot_index.into()];
slot.prev_i = ESI::from(0);
slot.next_i = next_i;
self.slots[next_i.into()].prev_i = slot_index;
self.slots[0].next_i = slot_index;
self.next_event_time = time;
} else {
let mut next_i = self.slots[self.slots[0].next_i.into()].next_i;
loop {
let next_slot = &mut self.slots[next_i.into()];
if time < next_slot.time {
let prev_i = next_slot.prev_i;
next_slot.prev_i = slot_index;
let slot = &mut self.slots[slot_index.into()];
slot.prev_i = prev_i;
slot.next_i = next_i;
self.slots[prev_i.into()].next_i = slot_index;
break;
}
next_i = next_slot.next_i;
}
}
}
pub fn cancel(&mut self, slot_index: ESI) {
let slot = &mut self.slots[slot_index.into()];
debug_assert!(slot.time != T::from(0));
slot.time = T::from(0);
let prev_i = slot.prev_i;
let next_i = slot.next_i;
self.slots[prev_i.into()].next_i = next_i;
let next_slot = &mut self.slots[next_i.into()];
next_slot.prev_i = prev_i;
if prev_i.into() == 0 {
let new_next_event_time = next_slot.time;
self.next_event_time = new_next_event_time;
}
}
}
impl<T, E, ESI, const EVENT_SLOTS: usize> Default for Schedule<T, E, ESI, EVENT_SLOTS>
where
T: Copy + Ord + Add + From<RawTimestamp> + Into<RawTimestamp>,
E: Copy + Eq + Default,
ESI: Copy + Eq + From<usize> + Into<usize>,
{
fn default() -> Self {
Self::new()
}
}