use crate::bytes::{ReturnOutputByteCount, RuntimeStateByteCount};
use crate::error::{LimitError, RunError};
use crate::limits::{ExecutionLimits, StepCount};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct RuntimeBudgetState {
limits: ExecutionLimits,
completed_steps: StepCount,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct StepPermit {
next_step: StepCount,
}
impl RuntimeBudgetState {
pub(crate) const fn new(limits: ExecutionLimits) -> Self {
Self {
limits,
completed_steps: StepCount::ZERO,
}
}
pub(crate) const fn completed_steps(self) -> StepCount {
self.completed_steps
}
pub(crate) fn ensure_rewrite_state_len(
self,
attempted_len: RuntimeStateByteCount,
) -> Result<(), RunError> {
if !self.limits.state_byte_limit().accepts(attempted_len) {
return Err(LimitError::state(self.limits.state_byte_limit(), attempted_len).into());
}
Ok(())
}
pub(crate) fn ensure_return_len(
self,
attempted_len: ReturnOutputByteCount,
) -> Result<(), RunError> {
if !self.limits.return_byte_limit().accepts(attempted_len) {
return Err(
LimitError::return_output(self.limits.return_byte_limit(), attempted_len).into(),
);
}
Ok(())
}
fn ensure_next_step_allowed(self, state_len: RuntimeStateByteCount) -> Result<(), LimitError> {
if !self
.limits
.step_limit()
.allows_next_after(self.completed_steps)
{
return Err(LimitError::step(
self.limits.step_limit(),
self.completed_steps,
state_len,
));
}
Ok(())
}
pub(crate) fn reserve_next_step(
self,
state_len: RuntimeStateByteCount,
) -> Result<StepPermit, LimitError> {
self.ensure_next_step_allowed(state_len)?;
let Some(next_step) = self.completed_steps.checked_next() else {
return Err(LimitError::step(
self.limits.step_limit(),
self.completed_steps,
state_len,
));
};
Ok(StepPermit { next_step })
}
pub(crate) fn commit(&mut self, permit: StepPermit) -> StepCount {
self.completed_steps = permit.next_step;
permit.next_step
}
}