use std::cell::Cell;
use std::mem::size_of;
use super::value::Value;
thread_local! {
static BYTES_LEFT: Cell<u64> = const { Cell::new(u64::MAX) };
}
#[inline]
pub(crate) fn charge(bytes: usize) {
BYTES_LEFT.with(|c| c.set(c.get().saturating_sub(bytes as u64)));
}
pub(crate) const ENV_COST: usize =
size_of::<super::env::Environment<'_>>() + 2 * size_of::<usize>() + size_of::<usize>();
pub(crate) const BINDING_COST: usize = size_of::<(String, Value<'_>)>() + size_of::<usize>();
#[inline]
pub(crate) fn charge_list(len: usize) {
charge(len * size_of::<Value<'_>>());
}
#[inline]
pub(crate) fn bytes_exhausted() -> bool {
BYTES_LEFT.with(|c| c.get() == 0)
}
pub(crate) struct ByteBudget {
previous: u64,
}
impl ByteBudget {
pub(crate) fn install(bytes: u64) -> Self {
let previous = BYTES_LEFT.with(|c| c.replace(bytes));
ByteBudget { previous }
}
}
impl Drop for ByteBudget {
fn drop(&mut self) {
BYTES_LEFT.with(|c| c.set(self.previous));
}
}
#[derive(Debug, Clone, Copy)]
pub struct Budget {
steps: u64,
bytes: u64,
}
impl Budget {
pub const DEFAULT: Budget = Budget {
steps: 10_000_000,
bytes: 256 * 1024 * 1024,
};
pub fn per_call() -> Self {
Self::DEFAULT
}
pub fn steps(mut self, steps: u64) -> Self {
self.steps = steps;
self
}
pub fn memory_bytes(mut self, bytes: u64) -> Self {
self.bytes = bytes;
self
}
pub fn memory_mb(self, mb: u64) -> Self {
self.memory_bytes(mb.saturating_mul(1024 * 1024))
}
}
impl Default for Budget {
fn default() -> Self {
Self::DEFAULT
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Usage {
pub steps: u64,
pub bytes: u64,
}
pub struct Metered {
_bytes: ByteBudget,
budget: Budget,
}
impl Budget {
pub fn begin(self, interp: &mut super::Interpreter<'_>) -> Metered {
interp.set_step_budget(self.steps);
Metered {
_bytes: ByteBudget::install(self.bytes),
budget: self,
}
}
}
impl Metered {
pub fn usage(&self, interp: &super::Interpreter<'_>) -> Usage {
Usage {
steps: self.budget.steps - interp.steps_left(),
bytes: self.budget.bytes - BYTES_LEFT.with(Cell::get),
}
}
}