graven/
verify.rs

1//! Whole-message verification limits.
2
3use crate::Error;
4
5/// Guards a recursive walk against cycles (backward pointers) and against
6/// amplification via shared subtrees.
7#[derive(Debug, Clone)]
8pub struct VerifyCtx {
9    depth: u32,
10    max_depth: u32,
11    budget: usize,
12}
13
14impl VerifyCtx {
15    pub const DEFAULT_MAX_DEPTH: u32 = 64;
16
17    /// Defaults: depth 64, budget `max(buf_len * 8, 1 MiB)` bytes.
18    pub fn new(buf_len: usize) -> Self {
19        Self::with_limits(Self::DEFAULT_MAX_DEPTH, buf_len.saturating_mul(8).max(1 << 20))
20    }
21
22    pub fn with_limits(max_depth: u32, budget: usize) -> Self {
23        VerifyCtx { depth: 0, max_depth, budget }
24    }
25
26    pub fn max_depth(&self) -> u32 {
27        self.max_depth
28    }
29
30    /// Remaining budget in bytes.
31    pub fn budget(&self) -> usize {
32        self.budget
33    }
34
35    #[inline]
36    pub fn enter(&mut self) -> Result<(), Error> {
37        if self.depth >= self.max_depth {
38            return Err(Error::DepthExceeded);
39        }
40        self.depth += 1;
41        Ok(())
42    }
43
44    #[inline]
45    pub fn leave(&mut self) {
46        self.depth = self.depth.saturating_sub(1);
47    }
48
49    #[inline]
50    pub fn charge(&mut self, bytes: usize) -> Result<(), Error> {
51        self.budget = self.budget.checked_sub(bytes).ok_or(Error::BudgetExceeded)?;
52        Ok(())
53    }
54}