Skip to main content

cadmpeg_core/decode/
work_scratch.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Temporary evaluator storage governed by its work slice's decode session.
3
4use super::budget::{DecodeBudget, ScopedReservation};
5use super::error::ResourceLimit;
6
7/// A temporary reservation whose lifetime covers evaluator-owned scratch.
8#[derive(Debug)]
9pub struct WorkScratch<'a> {
10    reservation: Option<ScopedReservation<'a>>,
11}
12
13impl<'a> WorkScratch<'a> {
14    pub(super) fn new(
15        session: Option<&'a DecodeBudget>,
16        bytes: u64,
17        operation: &'static str,
18    ) -> Result<Self, ResourceLimit> {
19        let reservation = session
20            .map(|session| session.reserve_scoped_limit(bytes, operation))
21            .transpose()?;
22        Ok(Self { reservation })
23    }
24
25    /// Admit additional scratch bytes before growing evaluator storage.
26    ///
27    /// # Errors
28    /// Returns the original resource refusal from an attached decode session.
29    pub fn grow(&mut self, bytes: u64) -> Result<(), ResourceLimit> {
30        if let Some(reservation) = &mut self.reservation {
31            reservation.grow_limit(bytes)?;
32        }
33        Ok(())
34    }
35}
36
37#[cfg(test)]
38mod tests {
39    use super::super::arena::DecodeArena;
40    use super::super::context::DecodeContext;
41    use super::super::error::ResourceDimension;
42    use super::super::policy::DecodePolicy;
43
44    #[test]
45    fn evaluator_scratch_tracks_live_growth_and_preserves_refusal() {
46        let mut policy = DecodePolicy::service();
47        policy.limits.max_materialized_bytes = 8;
48        let arena = DecodeArena::new();
49        let (ctx, _) =
50            DecodeContext::from_root_bytes(&[], &arena, &policy).expect("admitted test operation");
51        let budget = ctx.work_budget(10);
52        {
53            let mut scratch = budget
54                .reserve_scratch(4, "test evaluator scratch")
55                .expect("admitted test operation");
56            scratch.grow(4).expect("admitted test operation");
57        }
58        let mut scratch = budget
59            .reserve_scratch(8, "test evaluator scratch")
60            .expect("admitted test operation");
61        let error = scratch.grow(1).expect_err("test operation must refuse");
62        assert_eq!(error.dimension, ResourceDimension::MaterializedBytes);
63        assert_eq!(error.used, 8);
64        assert_eq!(error.additional, 1);
65        drop(scratch);
66        assert!(
67            matches!(ctx.finish_session(), Err(crate::CodecError::ResourceLimit(limit)) if limit == error)
68        );
69    }
70}