Skip to main content

structured_asn1/memory/
work.rs

1//! Borrowed parser execution for transitive scratch work.
2
3use super::{HashWork, Memory, MemoryError, OwnedMap, OwnedVec, allocator};
4use crate::limits::BudgetView;
5use crate::{Encoding, Parser, Reader, ScopedReader};
6
7mod private {
8    pub trait Sealed {}
9}
10
11/// An execution domain used by accounted collections and schema processing.
12///
13/// [`Memory`] owns an independent aggregate domain. [`Execution`] additionally
14/// borrows a parser's cumulative ceiling. The sealed implementations ensure
15/// that allocator and collection admission cannot bypass either boundary.
16pub trait Work: Clone + private::Sealed {
17    /// Storage ownership retained by the allocator independently of borrowed work.
18    type Retained: core::fmt::Debug;
19
20    /// Keeps an allocation's account alive even if its collection is forgotten.
21    /// Owned [`Memory`] already holds that account; borrowed [`Execution`]
22    /// supplies one owning handle per collection without allocating a new account.
23    fn retain_storage(&self) -> Self::Retained;
24
25    /// Moves the existing account owner when finishing independently owned storage.
26    /// The sealed implementations preserve the account without cloning it.
27    fn into_storage_owner(self, retained: Self::Retained) -> Memory;
28
29    /// Attaches current work while moving an existing same-account owner.
30    /// The caller verifies account identity before transferring storage.
31    fn with_storage_owner(&self, memory: Memory) -> (Self, Self::Retained);
32
33    /// The account retaining allocated capacity.
34    fn memory(&self) -> &Memory;
35
36    /// Admits work before execution, against every applicable ceiling.
37    fn charge_work(&self, amount: u64) -> Result<(), MemoryError>;
38
39    /// Opens original input while borrowing this operation only for decoding.
40    /// Parsed elements and scalar views retain the longer original input
41    /// lifetime; no result needs to keep a completed operation alive merely
42    /// because it borrows input. Source and current ceilings still apply to
43    /// every read, scratch allocation and copy before that work.
44    /// A cursor itself cannot escape a local operation:
45    ///
46    /// ```compile_fail
47    /// use structured_asn1::{Encoding, Parser, ScopedReader, memory::Work};
48    /// fn escape<'source>(source: &'source Parser, data: &'source [u8])
49    ///     -> ScopedReader<'source, 'source>
50    /// {
51    ///     let operation = Parser::der(source.memory().clone());
52    ///     let query = operation.read(&[], 900).unwrap();
53    ///     let execution = query.execution();
54    ///     execution.read_scoped_source(source, data, 400, Encoding::Der, 0).unwrap()
55    /// }
56    /// ```
57    fn read_scoped_source<'source: 'operation, 'operation>(
58        &'operation self,
59        parser: &'source Parser,
60        data: &'source [u8],
61        base: u64,
62        encoding: Encoding,
63        depth: u32,
64    ) -> crate::Result<ScopedReader<'source, 'operation>>;
65
66    /// Nested form of [`Work::read_scoped_source`], retaining actual outer+1
67    /// depth. Only decoding borrows this operation; returned input views do not.
68    fn read_scoped_nested<'source: 'operation, 'operation>(
69        &'operation self,
70        parser: &'source Parser,
71        data: &'source [u8],
72        base: u64,
73        encoding: Encoding,
74        outer: u32,
75    ) -> crate::Result<ScopedReader<'source, 'operation>> {
76        let depth = outer.checked_add(1).ok_or_else(|| {
77            crate::Error::new(
78                crate::ErrorCode::LimitExceeded(crate::Limit::Depth),
79                base,
80                crate::Path::default(),
81            )
82        })?;
83        self.read_scoped_source(parser, data, base, encoding, depth)
84    }
85
86    /// Opens original nested bytes without discarding this execution boundary.
87    /// Memory retains its existing source-plus-aggregate admission; Execution
88    /// additionally preserves the current root operation before any decode.
89    /// Source parser/data/base/encoding and actual `outer + 1` depth remain
90    /// original. A transition replaces a previous foreign source ceiling,
91    /// never the operation owner. A fresh document uses depth zero through
92    /// [`Work::read_source`], rather than inventing a nested carrier level.
93    ///
94    /// `Self: 'source` retains the real budget lifetimes, not the borrow of a
95    /// temporary Execution wrapper. The returned reader owns its account handle;
96    /// no parser/diagnostic clone, new domain or budget reset is introduced.
97    /// It still cannot outlive either actual parser budget:
98    ///
99    /// ```compile_fail
100    /// use std::sync::Arc;
101    /// use structured_asn1::{Parser, Encoding, memory::{Memory,MemoryAccount,Work}};
102    /// let memory = Memory::new(Arc::new(MemoryAccount::new(4096,4096)));
103    /// let source = Parser::ber(memory.clone());
104    /// let mut lazy;
105    /// {
106    ///     let operation = Parser::ber(memory);
107    ///     let query = operation.read(&[],900).unwrap();
108    ///     lazy = query.execution().read_nested(&source,&[2,1,7],400,Encoding::Ber,0).unwrap();
109    /// }
110    /// lazy.next_element().unwrap();
111    /// ```
112    ///
113    /// ```compile_fail
114    /// use std::sync::Arc;
115    /// use structured_asn1::{Parser, Encoding, memory::{Memory,MemoryAccount,Work}};
116    /// let memory = Memory::new(Arc::new(MemoryAccount::new(4096,4096)));
117    /// let operation = Parser::ber(memory.clone());
118    /// let query = operation.read(&[],900).unwrap();
119    /// let mut lazy;
120    /// {
121    ///     let source = Parser::ber(memory);
122    ///     lazy = query.execution().read_nested(&source,&[2,1,7],400,Encoding::Ber,0).unwrap();
123    /// }
124    /// lazy.next_element().unwrap();
125    /// ```
126    fn read_nested<'source>(
127        &self,
128        parser: &'source Parser,
129        data: &'source [u8],
130        base: u64,
131        encoding: Encoding,
132        outer: u32,
133    ) -> crate::Result<Reader<'source>>
134    where
135        Self: 'source;
136
137    /// Opens an original source window at its actual ASN.1 depth.
138    ///
139    /// This has the same source/account and lifetime guarantees as
140    /// [`Work::read_nested`], without adding a carrier level. A fresh document
141    /// begins at zero; an already nested window must supply its original depth.
142    /// Current operation ceilings remain in force even when a retained query
143    /// uses a new source parser bounded by the retained input size.
144    fn read_source<'source>(
145        &self,
146        parser: &'source Parser,
147        data: &'source [u8],
148        base: u64,
149        encoding: Encoding,
150        depth: u32,
151    ) -> crate::Result<Reader<'source>>
152    where
153        Self: 'source;
154
155    /// Creates empty accounted scratch retaining this execution boundary.
156    ///
157    /// No allocation occurs until a fallible growth operation. A collection
158    /// borrowing [`Execution`] cannot outlive that parser or execution account.
159    ///
160    /// # Examples
161    ///
162    /// ```
163    /// use std::sync::Arc;
164    /// use structured_asn1::{Parser, memory::{Memory, MemoryAccount, Work}};
165    /// let parser = Parser::ber(Memory::new(Arc::new(MemoryAccount::new(1024, 1024))));
166    /// let reader = parser.read(&[], 0).unwrap();
167    /// let mut scratch = reader.execution().vector();
168    /// scratch.try_push(42_u8)?;
169    /// assert_eq!(&*scratch, &[42]);
170    /// # Ok::<(), structured_asn1::memory::MemoryError>(())
171    /// ```
172    #[must_use]
173    fn vector<T>(&self) -> OwnedVec<T, Self> {
174        OwnedVec {
175            values: allocator_api2::vec::Vec::new_in(allocator::AccountAllocator::new(
176                self.clone(),
177            )),
178        }
179    }
180
181    /// Creates a bounded scratch map in this storage and execution domain.
182    /// Hash/probes, table growth and value movement retain the same ceilings.
183    #[must_use]
184    fn map<K: HashWork, V>(&self) -> OwnedMap<K, V, Self> {
185        OwnedMap::new(self.clone())
186    }
187}
188
189impl private::Sealed for Memory {}
190
191impl Work for Memory {
192    type Retained = ();
193
194    fn retain_storage(&self) {}
195
196    fn into_storage_owner(self, (): ()) -> Memory {
197        self
198    }
199
200    fn with_storage_owner(&self, memory: Memory) -> (Self, ()) {
201        (memory, ())
202    }
203
204    fn memory(&self) -> &Memory {
205        self
206    }
207
208    fn charge_work(&self, amount: u64) -> Result<(), MemoryError> {
209        Memory::charge_work(self, amount)
210    }
211
212    fn read_scoped_source<'source: 'operation, 'operation>(
213        &'operation self,
214        parser: &'source Parser,
215        data: &'source [u8],
216        base: u64,
217        encoding: Encoding,
218        depth: u32,
219    ) -> crate::Result<ScopedReader<'source, 'operation>> {
220        parser.read_scoped_source_in(data, base, encoding, depth, None, self)
221    }
222
223    fn read_nested<'source>(
224        &self,
225        parser: &'source Parser,
226        data: &'source [u8],
227        base: u64,
228        encoding: Encoding,
229        outer: u32,
230    ) -> crate::Result<Reader<'source>>
231    where
232        Self: 'source,
233    {
234        parser.read_nested_in(data, base, encoding, outer, self)
235    }
236
237    fn read_source<'source>(
238        &self,
239        parser: &'source Parser,
240        data: &'source [u8],
241        base: u64,
242        encoding: Encoding,
243        depth: u32,
244    ) -> crate::Result<Reader<'source>>
245    where
246        Self: 'source,
247    {
248        parser.read_source_in(data, base, encoding, depth, self)
249    }
250}
251
252/// A borrowed aggregate account and original parser-session work ceiling.
253///
254/// Obtain this from [`crate::Reader::execution`]. Copies allocate nothing and
255/// acquire no shared ownership. Scratch storage and work use the reader's
256/// current account; rebinding the account never resets the parser ceiling.
257///
258/// A parser-bound allocation cannot survive its borrowed owner:
259///
260/// ```compile_fail
261/// use std::sync::Arc;
262/// use structured_asn1::{Parser, memory::{Memory, MemoryAccount, Work}};
263/// let mut scratch;
264/// {
265///     let parser = Parser::ber(Memory::new(Arc::new(MemoryAccount::new(1024, 1024))));
266///     let reader = parser.read(&[], 0).unwrap();
267///     scratch = reader.execution().vector();
268///     scratch.try_push(42_u8).unwrap();
269/// }
270/// drop(scratch);
271/// ```
272#[derive(Debug, Clone, Copy)]
273pub struct Execution<'a> {
274    memory: &'a Memory,
275    budget: BudgetView<'a>,
276}
277
278impl<'a> Execution<'a> {
279    pub(crate) fn new(memory: &'a Memory, budget: BudgetView<'a>) -> Self {
280        Self { memory, budget }
281    }
282}
283
284impl private::Sealed for Execution<'_> {}
285
286impl Work for Execution<'_> {
287    type Retained = Memory;
288
289    fn retain_storage(&self) -> Memory {
290        self.memory.clone()
291    }
292
293    fn into_storage_owner(self, retained: Memory) -> Memory {
294        retained
295    }
296
297    fn with_storage_owner(&self, memory: Memory) -> (Self, Memory) {
298        (*self, memory)
299    }
300
301    fn memory(&self) -> &Memory {
302        self.memory
303    }
304
305    fn charge_work(&self, amount: u64) -> Result<(), MemoryError> {
306        self.budget
307            .work_in(amount, self.memory)
308            .map_err(|_| MemoryError::WorkExceeded)
309    }
310
311    fn read_scoped_source<'source: 'operation, 'operation>(
312        &'operation self,
313        parser: &'source Parser,
314        data: &'source [u8],
315        base: u64,
316        encoding: Encoding,
317        depth: u32,
318    ) -> crate::Result<ScopedReader<'source, 'operation>> {
319        parser.read_scoped_source_in(
320            data,
321            base,
322            encoding,
323            depth,
324            Some(self.budget.operation()),
325            self.memory,
326        )
327    }
328
329    fn read_nested<'source>(
330        &self,
331        parser: &'source Parser,
332        data: &'source [u8],
333        base: u64,
334        encoding: Encoding,
335        outer: u32,
336    ) -> crate::Result<Reader<'source>>
337    where
338        Self: 'source,
339    {
340        parser.read_nested_operation_in(
341            data,
342            base,
343            encoding,
344            outer,
345            self.budget.operation(),
346            self.memory,
347        )
348    }
349
350    fn read_source<'source>(
351        &self,
352        parser: &'source Parser,
353        data: &'source [u8],
354        base: u64,
355        encoding: Encoding,
356        depth: u32,
357    ) -> crate::Result<Reader<'source>>
358    where
359        Self: 'source,
360    {
361        parser.read_query_budget_in(
362            data,
363            base,
364            encoding,
365            depth,
366            self.budget.operation(),
367            self.memory,
368        )
369    }
370}