use core::cell::Cell;
use crate::error::Limit;
use crate::memory::Memory;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Limits {
pub max_depth: u32,
pub max_elements: u64,
pub max_element_length: u64,
pub max_work: u64,
}
impl Default for Limits {
fn default() -> Self {
Self {
max_depth: 1_000,
max_elements: 1_000_000,
max_element_length: 1 << 32,
max_work: 16_000_000,
}
}
}
impl Limits {
#[must_use]
pub fn none() -> Self {
Self {
max_depth: 0,
max_elements: 0,
max_element_length: 0,
max_work: 0,
}
}
}
#[derive(Debug)]
pub(crate) struct Budget {
limits: Limits,
elements: Cell<u64>,
work: Cell<u64>,
memory: Memory,
}
impl Budget {
pub(crate) fn new(limits: Limits, memory: Memory) -> Self {
Self {
limits,
elements: Cell::new(0),
work: Cell::new(0),
memory,
}
}
pub(crate) fn limits(&self) -> Limits {
self.limits
}
pub(crate) fn memory(&self) -> &Memory {
&self.memory
}
pub(crate) fn element(&self) -> Result<(), Limit> {
let spent = self.elements.get();
debug_assert!(spent <= self.limits.max_elements);
if spent >= self.limits.max_elements {
return Err(Limit::Elements);
}
self.elements.set(spent + 1);
Ok(())
}
#[cfg(test)]
pub(crate) fn work(&self, amount: u64) -> Result<(), Limit> {
self.work_in(amount, &self.memory)
}
pub(crate) fn work_in(&self, amount: u64, execution: &Memory) -> Result<(), Limit> {
let spent = self.work.get();
debug_assert!(spent <= self.limits.max_work);
if amount > self.limits.max_work - spent {
return Err(Limit::Work);
}
execution.charge_work(amount).map_err(|_| Limit::Work)?;
self.work.set(spent + amount);
Ok(())
}
pub(crate) fn depth(&self, depth: u32) -> Result<(), Limit> {
if depth > self.limits.max_depth {
return Err(Limit::Depth);
}
Ok(())
}
pub(crate) fn length(&self, length: u64) -> Result<(), Limit> {
if length > self.limits.max_element_length {
return Err(Limit::ElementLength);
}
Ok(())
}
}
#[cfg(test)]
mod tests;