use std::{
error::Error,
fmt::{self, Debug, Display, Formatter},
ops::ControlFlow
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::cost::Cost;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Budget
{
pub steps: u64,
pub cells: u64
}
impl Budget
{
pub const UNLIMITED: Self = Self {
steps: u64::MAX,
cells: u64::MAX
};
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Usage
{
pub steps: u64,
pub cells: u64,
pub worlds: u64
}
pub(crate) struct Meter<'a>
{
budget: Budget,
steps: u64,
held: u64,
working: u64,
peak: u64,
worlds: u64,
estimate: &'a Cost,
progress: &'a dyn Progress
}
impl<'a> Meter<'a>
{
pub(crate) fn new(
budget: Budget,
held: u64,
estimate: &'a Cost,
progress: &'a dyn Progress
) -> Result<Self, Halt>
{
let mut meter = Self {
budget,
steps: 0,
held: 0,
working: 0,
peak: 0,
worlds: 1,
estimate,
progress
};
meter.charge(0, held)?;
meter.settle(0, held);
Ok(meter)
}
pub(crate) fn charge(&mut self, steps: u64, cells: u64)
-> Result<(), Halt>
{
let remaining = self.budget.steps - self.steps;
if steps > remaining
{
return Err(Halt::Exhausted(Exhausted {
dimension: Dimension::Steps,
requested: steps,
remaining,
consumed: self.steps
}))
}
let alive = self.held.saturating_add(self.working);
let remaining = self.budget.cells.saturating_sub(alive);
if cells > remaining
{
return Err(Halt::Exhausted(Exhausted {
dimension: Dimension::Cells,
requested: cells,
remaining,
consumed: alive
}))
}
self.steps += steps;
self.working = self.working.saturating_add(cells);
self.peak = self.peak.max(alive.saturating_add(cells));
match self.progress.report(self.usage(), self.estimate)
{
ControlFlow::Continue(()) => Ok(()),
ControlFlow::Break(()) => Err(Halt::Cancelled)
}
}
pub(crate) fn free(&mut self, cells: u64)
{
debug_assert!(cells <= self.working, "free only what was charged");
self.working -= cells;
}
pub(crate) fn settle(&mut self, before: u64, after: u64)
{
debug_assert!(before <= self.held, "the locations were held");
debug_assert!(
after <= before + self.working,
"the locations keep no more than was charged: {after} > {before} + {}",
self.working
);
self.held = self.held - before + after;
self.working = 0;
}
#[inline]
pub(crate) fn held(&self) -> u64 { self.held }
#[inline]
pub(crate) fn count_worlds(&mut self, worlds: usize)
{
self.worlds = self.worlds.max(worlds as u64);
}
pub(crate) fn usage(&self) -> Usage
{
Usage {
steps: self.steps,
cells: self.peak,
worlds: self.worlds
}
}
}
impl Debug for Meter<'_>
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result
{
f.debug_struct("Meter")
.field("budget", &self.budget)
.field("steps", &self.steps)
.field("held", &self.held)
.field("working", &self.working)
.field("peak", &self.peak)
.field("worlds", &self.worlds)
.field("estimate", &self.estimate)
.finish_non_exhaustive()
}
}
#[inline]
pub(crate) fn charge_of(count: u128) -> u64
{
u64::try_from(count).unwrap_or(u64::MAX)
}
pub trait Progress
{
fn report(&self, consumed: Usage, estimate: &Cost) -> ControlFlow<()>;
}
impl<F> Progress for F
where
F: Fn(Usage, &Cost) -> ControlFlow<()>
{
#[inline]
fn report(&self, consumed: Usage, estimate: &Cost) -> ControlFlow<()>
{
self(consumed, estimate)
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub struct Unobserved;
impl Progress for Unobserved
{
#[inline]
fn report(&self, _consumed: Usage, _estimate: &Cost) -> ControlFlow<()>
{
ControlFlow::Continue(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum Dimension
{
Steps,
Cells
}
impl Display for Dimension
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result
{
match self
{
Dimension::Steps => write!(f, "steps"),
Dimension::Cells => write!(f, "cells")
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct Exhausted
{
pub(crate) dimension: Dimension,
pub(crate) requested: u64,
pub(crate) remaining: u64,
pub(crate) consumed: u64
}
impl Display for Exhausted
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result
{
write!(
f,
"budget of {} exhausted: {} requested, {} remaining, {} consumed",
self.dimension, self.requested, self.remaining, self.consumed
)
}
}
impl Error for Exhausted {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum Halt
{
Exhausted(Exhausted),
Cancelled
}
impl Display for Halt
{
fn fmt(&self, f: &mut Formatter) -> fmt::Result
{
match self
{
Halt::Exhausted(exhausted) => Display::fmt(exhausted, f),
Halt::Cancelled => write!(f, "cancelled")
}
}
}
impl Error for Halt {}