Skip to main content

structured_asn1/
memory.rs

1//! Shared storage accounting for independently owned results.
2//!
3//! The caller creates the trusted account before processing input. Containers
4//! retain that account independently of any parser and release their charge
5//! only when the backing allocation is deallocated. Input buffers supplied by
6//! the caller are outside these allocations; borrowed views do not copy them.
7
8use alloc::sync::Arc;
9use core::fmt;
10use core::ops::{Deref, DerefMut};
11use core::sync::atomic::{AtomicUsize, Ordering};
12
13use allocator_api2::collections::TryReserveErrorKind;
14use allocator_api2::vec::Vec;
15
16mod allocator;
17mod map;
18mod sort;
19mod string;
20pub use map::{HashWork, OwnedMap};
21pub use sort::SortError;
22pub use string::OwnedString;
23
24/// A failed storage operation, distinct from malformed input.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum MemoryError {
27    /// The shared capacity allowance cannot cover this allocation's peak.
28    LimitExceeded,
29    /// The underlying allocator could not supply storage.
30    OutOfMemory,
31    /// The requested capacity cannot be represented by an allocation layout.
32    CapacityOverflow,
33    /// The shared cumulative work allowance cannot cover this operation.
34    WorkExceeded,
35}
36
37impl fmt::Display for MemoryError {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        f.write_str(match self {
40            Self::LimitExceeded => "memory budget spent",
41            Self::OutOfMemory => "allocator could not supply storage",
42            Self::CapacityOverflow => "storage capacity overflow",
43            Self::WorkExceeded => "work budget spent",
44        })
45    }
46}
47
48#[cfg(feature = "std")]
49impl std::error::Error for MemoryError {}
50
51/// Caller-owned accounting state, constructed without allocating memory.
52///
53/// The caller supplies its shared ownership separately, during trusted setup.
54/// This keeps account creation out of document-driven allocation paths.
55#[derive(Debug)]
56pub struct MemoryAccount {
57    limit: usize,
58    used: AtomicUsize,
59    work_limit: usize,
60    work: AtomicUsize,
61}
62
63impl MemoryAccount {
64    /// An immutable capacity allowance, in bytes of allocator-visible storage.
65    #[must_use]
66    pub const fn new(limit: usize, work_limit: usize) -> Self {
67        Self {
68            limit,
69            used: AtomicUsize::new(0),
70            work_limit,
71            work: AtomicUsize::new(0),
72        }
73    }
74
75    fn reserve(&self, bytes: usize) -> core::result::Result<Reservation<'_>, MemoryError> {
76        let mut used = self.used.load(Ordering::Relaxed);
77        loop {
78            debug_assert!(used <= self.limit);
79            if bytes > self.limit - used {
80                return Err(MemoryError::LimitExceeded);
81            }
82            match self.used.compare_exchange_weak(
83                used,
84                used + bytes,
85                Ordering::Relaxed,
86                Ordering::Relaxed,
87            ) {
88                Ok(_) => {
89                    return Ok(Reservation {
90                        account: self,
91                        bytes,
92                    });
93                }
94                Err(current) => used = current,
95            }
96        }
97    }
98
99    fn release(&self, bytes: usize) {
100        let previous = self.used.fetch_sub(bytes, Ordering::Relaxed);
101        debug_assert!(previous >= bytes);
102    }
103}
104
105struct Reservation<'a> {
106    account: &'a MemoryAccount,
107    bytes: usize,
108}
109
110impl Reservation<'_> {
111    fn commit(mut self) {
112        self.bytes = 0;
113    }
114}
115
116impl Drop for Reservation<'_> {
117    fn drop(&mut self) {
118        self.account.release(self.bytes);
119    }
120}
121
122/// A shared storage domain retained by owned results.
123///
124/// Cloning this handle shares existing state and allocates no new block.
125#[derive(Debug, Clone)]
126pub struct Memory {
127    account: Arc<MemoryAccount>,
128}
129
130impl Memory {
131    /// Uses caller-supplied shared state; this constructor allocates nothing.
132    #[must_use]
133    pub fn new(account: Arc<MemoryAccount>) -> Self {
134        Self { account }
135    }
136
137    /// The immutable allowance in bytes.
138    #[must_use]
139    pub fn limit(&self) -> usize {
140        self.account.limit
141    }
142
143    /// Currently reserved bytes, including pending allocations and growth peaks.
144    #[must_use]
145    pub fn used(&self) -> usize {
146        self.account.used.load(Ordering::Relaxed)
147    }
148
149    /// Whether two handles share exactly the same account, without allocation.
150    #[must_use]
151    pub fn same_account(&self, other: &Self) -> bool {
152        Arc::ptr_eq(&self.account, &other.account)
153    }
154
155    /// Cumulative work already admitted, including operations that later failed.
156    #[must_use]
157    pub fn work_used(&self) -> usize {
158        self.account.work.load(Ordering::Relaxed)
159    }
160
161    /// Charges work before performing it; storage release never refunds work.
162    ///
163    /// An amount unrepresentable on this target is refused before any work,
164    /// preserving the existing u64 scanning API on 32-bit targets.
165    pub fn charge_work(&self, amount: u64) -> core::result::Result<(), MemoryError> {
166        let amount = usize::try_from(amount).map_err(|_| MemoryError::WorkExceeded)?;
167        let mut spent = self.account.work.load(Ordering::Relaxed);
168        loop {
169            debug_assert!(spent <= self.account.work_limit);
170            if amount > self.account.work_limit - spent {
171                return Err(MemoryError::WorkExceeded);
172            }
173            match self.account.work.compare_exchange_weak(
174                spent,
175                spent + amount,
176                Ordering::Relaxed,
177                Ordering::Relaxed,
178            ) {
179                Ok(_) => return Ok(()),
180                Err(current) => spent = current,
181            }
182        }
183    }
184
185    /// Reserves storage for an adapter-owned allocation before allocating it.
186    ///
187    /// Keep the returned guard until after deallocation, including rollback on
188    /// failure. Reserve the complete actual capacity; a growth must retain the
189    /// old guard while admitting the replacement. This bridge does not allocate.
190    pub fn reserve(&self, bytes: usize) -> core::result::Result<MemoryReservation, MemoryError> {
191        let reservation = self.account.reserve(bytes)?;
192        reservation.commit();
193        Ok(MemoryReservation {
194            memory: self.clone(),
195            bytes,
196        })
197    }
198
199    /// An empty owned collection sharing this domain, without allocating storage.
200    #[must_use]
201    pub fn vector<T>(&self) -> OwnedVec<T> {
202        OwnedVec {
203            values: Vec::new_in(allocator::AccountAllocator::new(self.clone())),
204        }
205    }
206
207    /// An empty accounted hash map, without allocating storage.
208    #[must_use]
209    pub fn map<K: HashWork, V>(&self) -> OwnedMap<K, V> {
210        OwnedMap::new(self.clone())
211    }
212
213    /// Empty accounted UTF-8 text, without allocating storage.
214    #[must_use]
215    pub fn string(&self) -> OwnedString {
216        OwnedString::new(self)
217    }
218}
219
220/// A reservation retained alongside an adapter-owned allocation.
221///
222/// Its drop releases the reserved charge. The adapter must deallocate its block
223/// first; dropping this guard earlier would misreport the allocation's lifetime.
224#[derive(Debug)]
225#[must_use = "retain the reservation until its allocation has been deallocated"]
226pub struct MemoryReservation {
227    memory: Memory,
228    bytes: usize,
229}
230
231impl Drop for MemoryReservation {
232    fn drop(&mut self) {
233        self.memory.account.release(self.bytes);
234    }
235}
236
237/// An owned collection whose capacity remains charged until deallocation.
238///
239/// It exposes slices rather than the underlying growable container, so callers
240/// cannot bypass fallible growth. Moving it retains accounting; copying values
241/// requires a fresh reservation. Clearing values does not release capacity.
242pub struct OwnedVec<T> {
243    values: Vec<T, allocator::AccountAllocator>,
244}
245
246impl<T: fmt::Debug> fmt::Debug for OwnedVec<T> {
247    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248        self.values.fmt(f)
249    }
250}
251
252impl<T: PartialEq> PartialEq for OwnedVec<T> {
253    fn eq(&self, other: &Self) -> bool {
254        **self == **other
255    }
256}
257
258impl<T: Eq> Eq for OwnedVec<T> {}
259
260impl<T: PartialEq, const N: usize> PartialEq<[T; N]> for OwnedVec<T> {
261    fn eq(&self, other: &[T; N]) -> bool {
262        &**self == other
263    }
264}
265
266impl<T> OwnedVec<T> {
267    /// The account retained by this collection, for related adapter operations.
268    #[must_use]
269    pub fn memory(&self) -> &Memory {
270        self.values.allocator().memory()
271    }
272    /// The allocated capacity in elements (zero-sized types allocate no bytes).
273    #[must_use]
274    pub fn capacity(&self) -> usize {
275        self.values.capacity()
276    }
277
278    /// Reserves capacity before any allocation, accounting for growth's peak.
279    pub fn try_reserve_exact(
280        &mut self,
281        additional: usize,
282    ) -> core::result::Result<(), MemoryError> {
283        self.values.allocator().clear_failure();
284        self.values
285            .try_reserve_exact(additional)
286            .map_err(|error| match error.kind() {
287                TryReserveErrorKind::CapacityOverflow => MemoryError::CapacityOverflow,
288                TryReserveErrorKind::AllocError { .. } => self.values.allocator().failure(),
289            })
290    }
291
292    /// Reserves amortized growth, charging the actual capacity and growth peak.
293    pub fn try_reserve(&mut self, additional: usize) -> core::result::Result<(), MemoryError> {
294        self.values.allocator().clear_failure();
295        self.values
296            .try_reserve(additional)
297            .map_err(|error| match error.kind() {
298                TryReserveErrorKind::CapacityOverflow => MemoryError::CapacityOverflow,
299                TryReserveErrorKind::AllocError { .. } => self.values.allocator().failure(),
300            })
301    }
302
303    /// Appends one value, acquiring any required storage before moving it.
304    pub fn try_push(&mut self, value: T) -> core::result::Result<(), MemoryError> {
305        self.values.allocator().memory().charge_work(1)?;
306        if self.values.len() == self.values.capacity() {
307            self.try_reserve(1)?;
308        }
309        // Vec guarantees push cannot allocate while capacity is available.
310        self.values.push(value);
311        Ok(())
312    }
313
314    /// Drops the last value, retaining the allocated capacity.
315    pub fn pop(&mut self) -> Option<T> {
316        self.values.pop()
317    }
318
319    /// Drops every value, retaining the allocated capacity and its charge.
320    pub fn clear(&mut self) {
321        self.values.clear();
322    }
323
324    /// Sorts in place without allocating or recursion.
325    ///
326    /// Comparisons and moves are admitted before execution. The comparator
327    /// must admit its own data-dependent work before examining values. On
328    /// refusal, values remain a permutation of the input, partially ordered.
329    pub fn try_sort_unstable_by<E>(
330        &mut self,
331        compare: impl FnMut(&T, &T) -> core::result::Result<core::cmp::Ordering, E>,
332    ) -> core::result::Result<(), SortError<E>> {
333        let memory = self.memory().clone();
334        sort::sort(&mut self.values, &memory, compare)
335    }
336}
337
338impl<T: Copy> OwnedVec<T> {
339    /// Copies a slice after reserving storage and cumulative work in this domain.
340    pub fn try_extend_from_slice(&mut self, values: &[T]) -> core::result::Result<(), MemoryError> {
341        let work = values.len().max(core::mem::size_of_val(values));
342        self.memory().charge_work(work as u64)?;
343        if self.values.capacity() == 0 {
344            self.try_reserve_exact(values.len())?;
345        } else {
346            self.try_reserve(values.len())?;
347        }
348        self.values.extend_from_slice(values);
349        Ok(())
350    }
351    /// Copies values into independently charged storage in the same domain.
352    pub fn try_copy(&self) -> core::result::Result<Self, MemoryError> {
353        let mut copy = self.values.allocator().memory().vector();
354        let work = self.values.len().max(core::mem::size_of_val(&**self));
355        self.values.allocator().memory().charge_work(work as u64)?;
356        copy.try_reserve_exact(self.values.len())?;
357        // Reservation above guarantees the Copy specialization cannot allocate.
358        copy.values.extend_from_slice(&self.values);
359        Ok(copy)
360    }
361}
362
363impl<T> Deref for OwnedVec<T> {
364    type Target = [T];
365    fn deref(&self) -> &[T] {
366        &self.values
367    }
368}
369
370impl<T> DerefMut for OwnedVec<T> {
371    fn deref_mut(&mut self) -> &mut [T] {
372        &mut self.values
373    }
374}
375
376impl<'a, T> IntoIterator for &'a OwnedVec<T> {
377    type Item = &'a T;
378    type IntoIter = core::slice::Iter<'a, T>;
379    fn into_iter(self) -> Self::IntoIter {
380        self.iter()
381    }
382}
383
384impl<'a, T> IntoIterator for &'a mut OwnedVec<T> {
385    type Item = &'a mut T;
386    type IntoIter = core::slice::IterMut<'a, T>;
387    fn into_iter(self) -> Self::IntoIter {
388        self.iter_mut()
389    }
390}
391
392/// An owning iterator that retains capacity accounting until storage is freed.
393pub struct OwnedIter<T> {
394    values: allocator_api2::vec::IntoIter<T, allocator::AccountAllocator>,
395}
396
397impl<T> Iterator for OwnedIter<T> {
398    type Item = T;
399    fn next(&mut self) -> Option<T> {
400        self.values.next()
401    }
402    fn size_hint(&self) -> (usize, Option<usize>) {
403        self.values.size_hint()
404    }
405}
406
407impl<T> DoubleEndedIterator for OwnedIter<T> {
408    fn next_back(&mut self) -> Option<T> {
409        self.values.next_back()
410    }
411}
412
413impl<T> ExactSizeIterator for OwnedIter<T> {}
414impl<T> core::iter::FusedIterator for OwnedIter<T> {}
415
416impl<T> IntoIterator for OwnedVec<T> {
417    type Item = T;
418    type IntoIter = OwnedIter<T>;
419    fn into_iter(self) -> Self::IntoIter {
420        OwnedIter {
421            values: self.values.into_iter(),
422        }
423    }
424}
425
426#[cfg(test)]
427mod tests;