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