use std::cell::Cell;
use prost::DecodeError;
use crate::frame::DEFAULT_MAX_DECODE_BYTES;
thread_local! {
static REMAINING: Cell<Option<usize>> = const { Cell::new(None) };
}
pub fn with_decode_budget<R>(bytes: usize, f: impl FnOnce() -> R) -> R {
let _guard = RestoreBudget(REMAINING.replace(Some(bytes.min(DEFAULT_MAX_DECODE_BYTES))));
f()
}
#[cfg(feature = "test-util")]
#[must_use]
pub fn decode_budget_remaining() -> Option<usize> {
REMAINING.get()
}
struct RestoreBudget(Option<usize>);
impl Drop for RestoreBudget {
fn drop(&mut self) {
REMAINING.set(self.0);
}
}
pub(crate) fn charge(bytes: usize) -> Result<(), DecodeError> {
REMAINING.with(|remaining| {
let current = remaining
.get()
.ok_or_else(|| error("decode allocation outside a frame; use decode_frame or FrameReader"))?;
let next = current.checked_sub(bytes).ok_or_else(exhausted)?;
remaining.set(Some(next));
Ok(())
})
}
pub(crate) fn boxed<T>(value: T) -> Result<Box<T>, DecodeError> {
charge(size_of::<T>())?;
Ok(Box::new(value))
}
#[expect(deprecated)]
pub(crate) fn error(message: &'static str) -> DecodeError {
DecodeError::new(message)
}
pub(crate) fn exhausted() -> DecodeError {
error("frame exceeds decode memory budget")
}