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 shared_string;
19mod sort;
20mod string;
21mod text;
22mod work;
23pub use map::{HashWork, MapExecution, OwnedMap, RetainError};
24pub use shared_string::SharedString;
25pub use sort::SortError;
26pub use string::OwnedString;
27pub use text::{OwnedText, TextError, TextErrorKind};
28pub use work::{Execution, Work};
29
30/// A failed storage operation, distinct from malformed input.
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum MemoryError {
33    /// The shared capacity allowance cannot cover this allocation's peak.
34    LimitExceeded,
35    /// The underlying allocator could not supply storage.
36    OutOfMemory,
37    /// The requested capacity cannot be represented by an allocation layout.
38    CapacityOverflow,
39    /// An aggregate or borrowed parser-session work allowance is exhausted.
40    WorkExceeded,
41}
42
43impl fmt::Display for MemoryError {
44    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45        f.write_str(match self {
46            Self::LimitExceeded => "memory budget spent",
47            Self::OutOfMemory => "allocator could not supply storage",
48            Self::CapacityOverflow => "storage capacity overflow",
49            Self::WorkExceeded => "work budget spent",
50        })
51    }
52}
53
54#[cfg(feature = "std")]
55impl std::error::Error for MemoryError {}
56
57/// Caller-owned accounting state, constructed without allocating memory.
58///
59/// The caller supplies its shared ownership separately, during trusted setup.
60/// This keeps account creation out of document-driven allocation paths.
61#[derive(Debug)]
62pub struct MemoryAccount {
63    limit: usize,
64    used: AtomicUsize,
65    work_limit: usize,
66    work: AtomicUsize,
67}
68
69impl MemoryAccount {
70    /// An immutable capacity allowance, in bytes of allocator-visible storage.
71    #[must_use]
72    pub const fn new(limit: usize, work_limit: usize) -> Self {
73        Self {
74            limit,
75            used: AtomicUsize::new(0),
76            work_limit,
77            work: AtomicUsize::new(0),
78        }
79    }
80
81    fn reserve(&self, bytes: usize) -> core::result::Result<Reservation<'_>, MemoryError> {
82        let mut used = self.used.load(Ordering::Relaxed);
83        loop {
84            debug_assert!(used <= self.limit);
85            if bytes > self.limit - used {
86                return Err(MemoryError::LimitExceeded);
87            }
88            match self.used.compare_exchange_weak(
89                used,
90                used + bytes,
91                Ordering::Relaxed,
92                Ordering::Relaxed,
93            ) {
94                Ok(_) => {
95                    return Ok(Reservation {
96                        account: self,
97                        bytes,
98                    });
99                }
100                Err(current) => used = current,
101            }
102        }
103    }
104
105    fn release(&self, bytes: usize) {
106        let previous = self.used.fetch_sub(bytes, Ordering::Relaxed);
107        debug_assert!(previous >= bytes);
108    }
109}
110
111struct Reservation<'a> {
112    account: &'a MemoryAccount,
113    bytes: usize,
114}
115
116impl Reservation<'_> {
117    fn commit(mut self) {
118        self.bytes = 0;
119    }
120}
121
122impl Drop for Reservation<'_> {
123    fn drop(&mut self) {
124        self.account.release(self.bytes);
125    }
126}
127
128/// A shared storage domain retained by owned results.
129///
130/// Cloning this handle shares existing state and allocates no new block.
131#[derive(Debug, Clone)]
132pub struct Memory {
133    account: Arc<MemoryAccount>,
134}
135
136impl Memory {
137    /// Uses caller-supplied shared state; this constructor allocates nothing.
138    #[must_use]
139    pub fn new(account: Arc<MemoryAccount>) -> Self {
140        Self { account }
141    }
142
143    /// The immutable allowance in bytes.
144    #[must_use]
145    pub fn limit(&self) -> usize {
146        self.account.limit
147    }
148
149    /// Currently reserved bytes, including pending allocations and growth peaks.
150    #[must_use]
151    pub fn used(&self) -> usize {
152        self.account.used.load(Ordering::Relaxed)
153    }
154
155    /// Whether two handles share exactly the same account, without allocation.
156    #[must_use]
157    pub fn same_account(&self, other: &Self) -> bool {
158        Arc::ptr_eq(&self.account, &other.account)
159    }
160
161    /// Cumulative work already admitted, including operations that later failed.
162    #[must_use]
163    pub fn work_used(&self) -> usize {
164        self.account.work.load(Ordering::Relaxed)
165    }
166
167    /// Currently available aggregate work units, without reserving them.
168    /// Concurrent consumption can reduce this observation; every operation
169    /// must still use [`Self::charge_work`] before execution.
170    #[must_use]
171    pub fn work_remaining(&self) -> usize {
172        let spent = self.account.work.load(Ordering::Relaxed);
173        debug_assert!(spent <= self.account.work_limit);
174        self.account.work_limit - spent
175    }
176
177    /// Charges work before performing it; storage release never refunds work.
178    ///
179    /// An amount unrepresentable on this target is refused before any work,
180    /// preserving the existing u64 scanning API on 32-bit targets.
181    pub fn charge_work(&self, amount: u64) -> core::result::Result<(), MemoryError> {
182        let amount = usize::try_from(amount).map_err(|_| MemoryError::WorkExceeded)?;
183        let mut spent = self.account.work.load(Ordering::Relaxed);
184        loop {
185            debug_assert!(spent <= self.account.work_limit);
186            if amount > self.account.work_limit - spent {
187                return Err(MemoryError::WorkExceeded);
188            }
189            match self.account.work.compare_exchange_weak(
190                spent,
191                spent + amount,
192                Ordering::Relaxed,
193                Ordering::Relaxed,
194            ) {
195                Ok(_) => return Ok(()),
196                Err(current) => spent = current,
197            }
198        }
199    }
200
201    /// Reserves storage for an adapter-owned allocation before allocating it.
202    ///
203    /// Keep the returned guard until after deallocation, including rollback on
204    /// failure. Reserve the complete actual capacity; a growth must retain the
205    /// old guard while admitting the replacement. This bridge does not allocate.
206    pub fn reserve(&self, bytes: usize) -> core::result::Result<MemoryReservation, MemoryError> {
207        let reservation = self.account.reserve(bytes)?;
208        reservation.commit();
209        Ok(MemoryReservation {
210            memory: self.clone(),
211            bytes,
212        })
213    }
214
215    /// An empty owned collection sharing this domain, without allocating storage.
216    #[must_use]
217    pub fn vector<T>(&self) -> OwnedVec<T> {
218        Work::vector(self)
219    }
220
221    /// An empty accounted hash map, without allocating storage.
222    #[must_use]
223    pub fn map<K: HashWork, V>(&self) -> OwnedMap<K, V> {
224        OwnedMap::new(self.clone())
225    }
226
227    /// Empty accounted UTF-8 text, without allocating storage.
228    #[must_use]
229    pub fn string(&self) -> OwnedString {
230        OwnedString::new(self)
231    }
232}
233
234/// A reservation retained alongside an adapter-owned allocation.
235///
236/// Its drop releases the reserved charge. The adapter must deallocate its block
237/// first; dropping this guard earlier would misreport the allocation's lifetime.
238#[derive(Debug)]
239#[must_use = "retain the reservation until its allocation has been deallocated"]
240pub struct MemoryReservation {
241    memory: Memory,
242    bytes: usize,
243}
244
245impl Drop for MemoryReservation {
246    fn drop(&mut self) {
247        self.memory.account.release(self.bytes);
248    }
249}
250
251/// An owned collection whose capacity remains charged until deallocation.
252///
253/// It exposes slices rather than the underlying growable container, so callers
254/// cannot bypass fallible growth. Moving it retains accounting; copying values
255/// requires a fresh reservation. Clearing values does not release capacity.
256/// The default [`Memory`] execution owner is independent of parser lifetime;
257/// a collection using [`Execution`] borrows its parser's work ceiling too.
258pub struct OwnedVec<T, W: Work = Memory> {
259    values: Vec<T, allocator::AccountAllocator<W>>,
260}
261
262/// Ownership-preserving vector execution transfer, including both inputs on
263/// mismatch. Layout, elements, capacity and retained charges are unchanged.
264#[must_use]
265pub enum VecExecution<T, Old: Work, New: Work> {
266    /// Same allocation, now mutated under the requested current execution.
267    Changed {
268        /// Transferred vector.
269        values: OwnedVec<T, New>,
270        /// Previous work, without replaying charges.
271        previous: Old,
272    },
273    /// Different accounts cannot exchange allocation charges.
274    Foreign {
275        /// Unchanged original vector.
276        values: OwnedVec<T, Old>,
277        /// Unused requested work.
278        requested: New,
279    },
280}
281
282impl<T> OwnedVec<T> {
283    /// Temporarily attaches the current operation's work ceilings.
284    ///
285    /// The execution must use this allocation's existing account. On mismatch
286    /// the original collection is returned unchanged. On success the pointer,
287    /// values, capacity, charge and owning account handle move without copying,
288    /// allocation or new work. Previously admitted work is neither reset nor
289    /// charged again. Call [`OwnedVec::into_owned`] to retain the allocation
290    /// independently after the callback, including after a refused mutation.
291    pub fn with_execution<W: Work>(self, execution: &W) -> Result<OwnedVec<T, W>, Self> {
292        if !self.memory().same_account(execution.memory()) {
293            return Err(self);
294        }
295        Ok(OwnedVec {
296            values: allocator::scope(self.values, execution),
297        })
298    }
299}
300
301impl<T, W: Work> OwnedVec<T, W> {
302    pub(crate) fn work(&self) -> &W {
303        self.values.allocator().work()
304    }
305
306    /// Rebinds storage without allocation, element copying or repeated work.
307    /// Borrowed executions transfer their keeper without a refcount increase;
308    /// retaining previous independent Memory additionally owns that account.
309    pub fn rebind<New: Work>(self, execution: New) -> VecExecution<T, W, New> {
310        if !self.memory().same_account(execution.memory()) {
311            return VecExecution::Foreign {
312                values: self,
313                requested: execution,
314            };
315        }
316        let (values, previous) = allocator::rebind(self.values, &execution);
317        VecExecution::Changed {
318            values: OwnedVec { values },
319            previous,
320        }
321    }
322    /// Finishes bounded construction as an independently owned result.
323    ///
324    /// Transfers the existing allocation and its original account without
325    /// copying elements, reallocating, changing capacity or reserving again.
326    /// Subsequent operations use that aggregate account; the parser-local
327    /// ceiling governed construction and is no longer attached. Borrows in
328    /// `T` retain their own lifetimes.
329    ///
330    /// # Examples
331    /// ```
332    /// use std::sync::Arc;
333    /// use structured_asn1::{Parser, memory::{Memory, MemoryAccount, Work}};
334    /// let memory = Memory::new(Arc::new(MemoryAccount::new(1024, 1024)));
335    /// let result;
336    /// {
337    ///     let parser = Parser::ber(memory.clone());
338    ///     let reader = parser.read(&[], 0)?;
339    ///     let mut scratch = reader.execution().vector();
340    ///     scratch.try_push(7_u8).unwrap();
341    ///     result = scratch.into_owned();
342    /// }
343    /// assert_eq!(&*result, &[7]);
344    /// assert!(memory.used() > 0);
345    /// drop(result);
346    /// assert_eq!(memory.used(), 0);
347    /// # Ok::<(), structured_asn1::Error>(())
348    /// ```
349    #[must_use]
350    pub fn into_owned(self) -> OwnedVec<T> {
351        OwnedVec {
352            values: allocator::retain(self.values),
353        }
354    }
355}
356
357impl<T: fmt::Debug, W: Work> fmt::Debug for OwnedVec<T, W> {
358    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
359        self.values.fmt(f)
360    }
361}
362
363impl<T: PartialEq, W: Work> PartialEq for OwnedVec<T, W> {
364    fn eq(&self, other: &Self) -> bool {
365        **self == **other
366    }
367}
368
369impl<T: Eq, W: Work> Eq for OwnedVec<T, W> {}
370
371impl<T: PartialEq, W: Work, const N: usize> PartialEq<[T; N]> for OwnedVec<T, W> {
372    fn eq(&self, other: &[T; N]) -> bool {
373        &**self == other
374    }
375}
376
377impl<T, W: Work> OwnedVec<T, W> {
378    /// The account retained by this collection, for related adapter operations.
379    #[must_use]
380    pub fn memory(&self) -> &Memory {
381        self.values.allocator().memory()
382    }
383    /// The allocated capacity in elements (zero-sized types allocate no bytes).
384    #[must_use]
385    pub fn capacity(&self) -> usize {
386        self.values.capacity()
387    }
388
389    /// Reserves capacity before any allocation, accounting for growth's peak.
390    pub fn try_reserve_exact(
391        &mut self,
392        additional: usize,
393    ) -> core::result::Result<(), MemoryError> {
394        self.values.allocator().clear_failure();
395        self.values
396            .try_reserve_exact(additional)
397            .map_err(|error| match error.kind() {
398                TryReserveErrorKind::CapacityOverflow => MemoryError::CapacityOverflow,
399                TryReserveErrorKind::AllocError { .. } => self.values.allocator().failure(),
400            })
401    }
402
403    /// Reserves amortized growth, charging the actual capacity and growth peak.
404    pub fn try_reserve(&mut self, additional: usize) -> core::result::Result<(), MemoryError> {
405        self.values.allocator().clear_failure();
406        self.values
407            .try_reserve(additional)
408            .map_err(|error| match error.kind() {
409                TryReserveErrorKind::CapacityOverflow => MemoryError::CapacityOverflow,
410                TryReserveErrorKind::AllocError { .. } => self.values.allocator().failure(),
411            })
412    }
413
414    /// Appends one value, acquiring any required storage before moving it.
415    pub fn try_push(&mut self, value: T) -> core::result::Result<(), MemoryError> {
416        self.values.allocator().work().charge_work(1)?;
417        if self.values.len() == self.values.capacity() {
418            self.try_reserve(1)?;
419        }
420        // Vec guarantees push cannot allocate while capacity is available.
421        self.values.push(value);
422        Ok(())
423    }
424
425    /// Drops the last value, retaining the allocated capacity.
426    pub fn pop(&mut self) -> Option<T> {
427        self.values.pop()
428    }
429
430    /// Drops every value, retaining the allocated capacity and its charge.
431    pub fn clear(&mut self) {
432        self.values.clear();
433    }
434
435    /// Moves every element out in source order while retaining this allocation.
436    ///
437    /// Admits the larger of the element count and initialized byte size under
438    /// the original execution before changing length. Refusal leaves all values,
439    /// length, pointer, capacity and storage charge unchanged. No allocation,
440    /// clone or shifting of a remaining tail occurs. Iterator drop destroys any
441    /// unconsumed elements; the empty vector retains its original capacity.
442    /// Arbitrary work performed by element destructors remains their caller's
443    /// responsibility, separate from this prepaid collection traversal/movement.
444    ///
445    /// The iterator exclusively borrows the vector. Forgetting it can leak
446    /// unconsumed elements, as with the underlying vector's drain; the backing
447    /// allocation stays owned and charged until the vector is deallocated.
448    pub fn try_drain_all(
449        &mut self,
450    ) -> Result<
451        impl DoubleEndedIterator<Item = T> + ExactSizeIterator + core::iter::FusedIterator + '_,
452        MemoryError,
453    > {
454        let work = self.values.len().max(core::mem::size_of_val(&**self));
455        self.values.allocator().work().charge_work(work as u64)?;
456        Ok(self.values.drain(..))
457    }
458
459    /// Sorts in place without allocating or recursion.
460    ///
461    /// Comparisons and moves are admitted before execution. The comparator
462    /// must admit its own data-dependent work before examining values. On
463    /// refusal, values remain a permutation of the input, partially ordered.
464    pub fn try_sort_unstable_by<E>(
465        &mut self,
466        compare: impl FnMut(&T, &T) -> core::result::Result<core::cmp::Ordering, E>,
467    ) -> core::result::Result<(), SortError<E>> {
468        let work = self.values.allocator().work().clone();
469        sort::sort(&mut self.values, &work, compare)
470    }
471}
472
473impl<T: Copy, W: Work> OwnedVec<T, W> {
474    /// Copies a slice after reserving storage and cumulative work in this domain.
475    pub fn try_extend_from_slice(&mut self, values: &[T]) -> core::result::Result<(), MemoryError> {
476        let work = values.len().max(core::mem::size_of_val(values));
477        self.values.allocator().work().charge_work(work as u64)?;
478        if self.values.capacity() == 0 {
479            self.try_reserve_exact(values.len())?;
480        } else {
481            self.try_reserve(values.len())?;
482        }
483        self.values.extend_from_slice(values);
484        Ok(())
485    }
486    /// Copies values into independently charged storage in the same domain.
487    pub fn try_copy(&self) -> core::result::Result<Self, MemoryError> {
488        let mut copy = self.values.allocator().work().vector();
489        let work = self.values.len().max(core::mem::size_of_val(&**self));
490        self.values.allocator().work().charge_work(work as u64)?;
491        copy.try_reserve_exact(self.values.len())?;
492        // Reservation above guarantees the Copy specialization cannot allocate.
493        copy.values.extend_from_slice(&self.values);
494        Ok(copy)
495    }
496}
497
498impl<T, W: Work> Deref for OwnedVec<T, W> {
499    type Target = [T];
500    fn deref(&self) -> &[T] {
501        &self.values
502    }
503}
504
505impl<T, W: Work> DerefMut for OwnedVec<T, W> {
506    fn deref_mut(&mut self) -> &mut [T] {
507        &mut self.values
508    }
509}
510
511impl<'a, T, W: Work> IntoIterator for &'a OwnedVec<T, W> {
512    type Item = &'a T;
513    type IntoIter = core::slice::Iter<'a, T>;
514    fn into_iter(self) -> Self::IntoIter {
515        self.iter()
516    }
517}
518
519impl<'a, T, W: Work> IntoIterator for &'a mut OwnedVec<T, W> {
520    type Item = &'a mut T;
521    type IntoIter = core::slice::IterMut<'a, T>;
522    fn into_iter(self) -> Self::IntoIter {
523        self.iter_mut()
524    }
525}
526
527/// An owning iterator that retains capacity accounting until storage is freed.
528pub struct OwnedIter<T, W: Work = Memory> {
529    values: allocator_api2::vec::IntoIter<T, allocator::AccountAllocator<W>>,
530}
531
532impl<T, W: Work> Iterator for OwnedIter<T, W> {
533    type Item = T;
534    fn next(&mut self) -> Option<T> {
535        self.values.next()
536    }
537    fn size_hint(&self) -> (usize, Option<usize>) {
538        self.values.size_hint()
539    }
540}
541
542impl<T, W: Work> DoubleEndedIterator for OwnedIter<T, W> {
543    fn next_back(&mut self) -> Option<T> {
544        self.values.next_back()
545    }
546}
547
548impl<T, W: Work> ExactSizeIterator for OwnedIter<T, W> {}
549impl<T, W: Work> core::iter::FusedIterator for OwnedIter<T, W> {}
550
551impl<T, W: Work> IntoIterator for OwnedVec<T, W> {
552    type Item = T;
553    type IntoIter = OwnedIter<T, W>;
554    fn into_iter(self) -> Self::IntoIter {
555        OwnedIter {
556            values: self.values.into_iter(),
557        }
558    }
559}
560
561#[cfg(test)]
562mod tests;