use core::cmp::max;
use core::fmt::{Debug, Formatter};
use core::num::NonZeroU16;
use core::ops::{Add, Div, Mul, Sub};
use core::time::Duration;
use crate::utils::ChunkSlab;
pub trait TimerClock {
type Duration: TimerCount;
type Instant;
fn start(&self) -> Self::Instant;
fn stop(&self, start: Self::Instant) -> Self::Duration;
}
pub trait TimerCount: Copy + Ord + Add<Output=Self> + Sub<Output=Self> + Default {
fn div_by(self, by: NonZeroU16) -> Self;
fn mul_by(self, by: NonZeroU16) -> Self;
}
macro_rules! impl_count {
($($name:ident),*) => {
$(impl TimerCount for $name{
fn div_by(self, by: NonZeroU16) -> Self { self.div(by.get() as Self) }
fn mul_by(self, by: NonZeroU16) -> Self { self.mul(by.get() as Self) }
})*
}
}
impl_count!(u16,i16,u32,i32,u64,i64,u128,i128,usize,isize);
impl TimerCount for Duration {
fn div_by(self, by: NonZeroU16) -> Self { self.div(by.get() as u32) }
fn mul_by(self, by: NonZeroU16) -> Self { self.mul(by.get() as u32) }
}
pub struct TimingGroup<C: TimerCount> {
info: ChunkSlab<usize, TimeEntry<C>>,
max: C,
}
#[derive(Debug, Copy, Clone)]
struct TimeEntry<D> {
sum: D,
proportion: NonZeroU16,
}
impl<C: TimerCount> TimingGroup<C> {
pub fn new() -> Self {
Self {
info: ChunkSlab::new(),
max: C::default(),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
info: ChunkSlab::with_capacity(capacity),
max: C::default(),
}
}
pub fn insert(&mut self, slot_count: u16) -> usize {
let proportion = NonZeroU16::new(slot_count).expect("Time slot count is zero.");
self.info.insert(TimeEntry {
proportion,
sum: C::default(),
})
}
pub fn remove(&mut self, key: usize) {
self.info.remove(key).expect("Error: unknown key passed to TimingGroup::remove");
}
pub fn contains(&self, key: usize) -> bool {
self.info.get(key).is_some()
}
pub fn count(&self) -> usize { self.info.len() }
pub fn get_slot_count(&self, key: usize) -> Option<u16> { self.info.get(key).map(|v| v.proportion.get()) }
pub fn clear(&mut self) {
self.info.clear();
self.max = C::default();
}
pub fn can_execute(&self, key: usize) -> bool {
let this = *self.info.get(key).expect("Error: unknown key passed to TimingGroup::can_execute");
let this_dur = Self::get_proportional(&this);
if this_dur == self.max {
if self.info.iter().any(|(_, v)| Self::get_proportional(v) != this_dur) {
return false;
}
}
let min_bound = self.max.mul_by(NonZeroU16::new(9).unwrap()).div_by(NonZeroU16::new(10).unwrap());
let min_time = self.info.iter().map(|(_, v)| Self::get_proportional(v)).min().unwrap();
if min_time <= min_bound { return this_dur <= min_bound; }
true
}
pub fn update_duration(&mut self, key: usize, dur: C) {
let this = self.info.get_mut(key).expect("Error: unknown key passed to TimingGroup::update_duration");
this.sum = this.sum + dur;
self.max = max(self.max, Self::get_proportional(this));
let min_time = self.info.iter().map(|(_, v)| Self::get_proportional(v)).min().unwrap();
if min_time != C::default() && false {
self.max = self.max - min_time;
for (_, entry) in self.info.iter_mut() { entry.sum = entry.sum - min_time.mul_by(entry.proportion);
}
}
}
#[inline]
fn get_proportional(entry: &TimeEntry<C>) -> C {
entry.sum.div_by(entry.proportion)
}
}
impl<C: TimerCount + Debug> Debug for TimingGroup<C> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
struct DebugEntry<'a, T: Debug>(&'a TimeEntry<T>);
impl<'a, T: Debug> Debug for DebugEntry<'a, T> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("")
.field("slots", &self.0.proportion.get())
.field("total", &self.0.sum).finish()
}
}
let mut m = f.debug_map();
for (key, entry) in self.info.iter() {
m.entry(&key, &DebugEntry(entry));
}
m.finish()
}
}
#[cfg(feature = "std")]
#[derive(Default, Clone, Eq, PartialEq, Hash, Debug)]
pub struct StdTimerClock;
#[cfg(feature = "std")]
impl TimerClock for StdTimerClock {
type Duration = Duration;
type Instant = std::time::Instant;
#[inline]
fn start(&self) -> Self::Instant { Self::Instant::now() }
#[inline]
fn stop(&self, start: Self::Instant) -> Self::Duration { Self::Instant::now() - start }
}