structured_asn1/memory/work.rs
1//! Borrowed parser execution for transitive scratch work.
2
3use super::{Memory, MemoryError, OwnedVec, allocator};
4use crate::limits::Budget;
5
6mod private {
7 pub trait Sealed {}
8}
9
10/// An execution domain used by accounted collections and schema processing.
11///
12/// [`Memory`] owns an independent aggregate domain. [`Execution`] additionally
13/// borrows a parser's cumulative ceiling. The sealed implementations ensure
14/// that allocator and collection admission cannot bypass either boundary.
15pub trait Work: Clone + private::Sealed {
16 /// Storage ownership retained by the allocator independently of borrowed work.
17 type Retained: core::fmt::Debug;
18
19 /// Keeps an allocation's account alive even if its collection is forgotten.
20 /// Owned [`Memory`] already holds that account; borrowed [`Execution`]
21 /// supplies one owning handle per collection without allocating a new account.
22 fn retain_storage(&self) -> Self::Retained;
23
24 /// Moves the existing account owner when finishing independently owned storage.
25 /// The sealed implementations preserve the account without cloning it.
26 fn into_storage_owner(self, retained: Self::Retained) -> Memory;
27
28 /// The account retaining allocated capacity.
29 fn memory(&self) -> &Memory;
30
31 /// Admits work before execution, against every applicable ceiling.
32 fn charge_work(&self, amount: u64) -> Result<(), MemoryError>;
33
34 /// Creates empty accounted scratch retaining this execution boundary.
35 ///
36 /// No allocation occurs until a fallible growth operation. A collection
37 /// borrowing [`Execution`] cannot outlive that parser or execution account.
38 ///
39 /// # Examples
40 ///
41 /// ```
42 /// use std::sync::Arc;
43 /// use structured_asn1::{Parser, memory::{Memory, MemoryAccount, Work}};
44 /// let parser = Parser::ber(Memory::new(Arc::new(MemoryAccount::new(1024, 1024))));
45 /// let reader = parser.read(&[], 0).unwrap();
46 /// let mut scratch = reader.execution().vector();
47 /// scratch.try_push(42_u8)?;
48 /// assert_eq!(&*scratch, &[42]);
49 /// # Ok::<(), structured_asn1::memory::MemoryError>(())
50 /// ```
51 #[must_use]
52 fn vector<T>(&self) -> OwnedVec<T, Self> {
53 OwnedVec {
54 values: allocator_api2::vec::Vec::new_in(allocator::AccountAllocator::new(
55 self.clone(),
56 )),
57 }
58 }
59}
60
61impl private::Sealed for Memory {}
62
63impl Work for Memory {
64 type Retained = ();
65
66 fn retain_storage(&self) {}
67
68 fn into_storage_owner(self, (): ()) -> Memory {
69 self
70 }
71
72 fn memory(&self) -> &Memory {
73 self
74 }
75
76 fn charge_work(&self, amount: u64) -> Result<(), MemoryError> {
77 Memory::charge_work(self, amount)
78 }
79}
80
81/// A borrowed aggregate account and original parser-session work ceiling.
82///
83/// Obtain this from [`crate::Reader::execution`]. Copies allocate nothing and
84/// acquire no shared ownership. Scratch storage and work use the reader's
85/// current account; rebinding the account never resets the parser ceiling.
86///
87/// A parser-bound allocation cannot survive its borrowed owner:
88///
89/// ```compile_fail
90/// use std::sync::Arc;
91/// use structured_asn1::{Parser, memory::{Memory, MemoryAccount, Work}};
92/// let mut scratch;
93/// {
94/// let parser = Parser::ber(Memory::new(Arc::new(MemoryAccount::new(1024, 1024))));
95/// let reader = parser.read(&[], 0).unwrap();
96/// scratch = reader.execution().vector();
97/// scratch.try_push(42_u8).unwrap();
98/// }
99/// drop(scratch);
100/// ```
101#[derive(Debug, Clone, Copy)]
102pub struct Execution<'a> {
103 memory: &'a Memory,
104 budget: &'a Budget,
105}
106
107impl<'a> Execution<'a> {
108 pub(crate) fn new(memory: &'a Memory, budget: &'a Budget) -> Self {
109 Self { memory, budget }
110 }
111}
112
113impl private::Sealed for Execution<'_> {}
114
115impl Work for Execution<'_> {
116 type Retained = Memory;
117
118 fn retain_storage(&self) -> Memory {
119 self.memory.clone()
120 }
121
122 fn into_storage_owner(self, retained: Memory) -> Memory {
123 retained
124 }
125
126 fn memory(&self) -> &Memory {
127 self.memory
128 }
129
130 fn charge_work(&self, amount: u64) -> Result<(), MemoryError> {
131 self.budget
132 .work_in(amount, self.memory)
133 .map_err(|_| MemoryError::WorkExceeded)
134 }
135}