Skip to main content

platform_mem/
raw_mem.rs

1use std::{
2    alloc::Layout,
3    mem::MaybeUninit,
4    ops::{Bound, Range, RangeBounds},
5};
6
7/// Converts a `RangeBounds` to a `Range` with bounds checking (stable alternative to `slice::range`)
8fn range_bounds_to_range<R: RangeBounds<usize>>(range: R, len: usize) -> Range<usize> {
9    let start = match range.start_bound() {
10        Bound::Included(&n) => n,
11        Bound::Excluded(&n) => n.checked_add(1).expect("range start overflow"),
12        Bound::Unbounded => 0,
13    };
14    let end = match range.end_bound() {
15        Bound::Included(&n) => n.checked_add(1).expect("range end overflow"),
16        Bound::Excluded(&n) => n,
17        Bound::Unbounded => len,
18    };
19    assert!(start <= end, "range start ({start}) > end ({end})");
20    assert!(end <= len, "range end ({end}) > length ({len})");
21    start..end
22}
23
24/// Errors that can occur during memory operations.
25#[derive(thiserror::Error, Debug)]
26#[non_exhaustive]
27pub enum Error {
28    /// Error due to the computed capacity exceeding the maximum
29    /// (usually `isize::MAX` bytes).
30    ///
31    /// ## Examples
32    ///
33    /// grow more than `isize::MAX` bytes:
34    ///
35    /// ```
36    /// # use allocator_api2::alloc::Global;
37    /// # use platform_mem::{Error, Alloc, RawMem};
38    /// let mut mem = Alloc::new(Global);
39    /// assert!(matches!(mem.grow_filled(usize::MAX, 0u64), Err(Error::CapacityOverflow)));
40    /// ```
41    #[error("exceeding the capacity maximum")]
42    CapacityOverflow,
43
44    /// Cannot grow because the requested size exceeds available capacity.
45    #[error("can't grow {to_grow} elements, only available {available}")]
46    OverGrow {
47        /// Number of elements requested.
48        to_grow: usize,
49        /// Number of elements available.
50        available: usize,
51    },
52
53    /// The memory allocator returned an error
54    #[error("memory allocation of {layout:?} failed")]
55    AllocError {
56        /// The layout of allocation request that failed
57        layout: Layout,
58
59        #[doc(hidden)]
60        non_exhaustive: (),
61    },
62
63    /// An I/O or system-level error.
64    #[error(transparent)]
65    System(#[from] std::io::Error),
66}
67
68/// Alias for `Result<T, Error>`.
69pub type Result<T> = std::result::Result<T, Error>;
70
71/// Unified trait for growable, shrinkable memory regions.
72///
73/// Implementors manage a contiguous region of initialized `Item` elements.
74/// The region can be extended with [`grow`](Self::grow) variants and shortened
75/// with [`shrink`](Self::shrink).
76pub trait RawMem {
77    /// The element type stored in this memory.
78    type Item;
79
80    /// Returns a shared slice of all currently initialized elements.
81    fn allocated(&self) -> &[Self::Item];
82    /// Returns a mutable slice of all currently initialized elements.
83    fn allocated_mut(&mut self) -> &mut [Self::Item];
84
85    /// Low-level growth: extends the memory by `cap` elements.
86    ///
87    /// The `fill` closure receives `(inited, (initialized_slice, uninitialized_slice))`
88    /// where `inited` is the length of the already-initialized prefix of
89    /// `uninitialized_slice`, for example elements loaded from an existing file.
90    /// Initializers can preserve that prefix and initialize only the remaining tail.
91    ///
92    /// # Safety
93    /// Caller must guarantee that `fill` fully initializes the portion after `inited`.
94    ///
95    /// ### Incorrect usage
96    /// ```no_run
97    /// # use allocator_api2::alloc::Global;
98    /// # use std::mem::MaybeUninit;
99    /// # use platform_mem::Result;
100    /// use platform_mem::{Alloc, RawMem};
101    ///
102    /// let mut alloc = Alloc::new(Global);
103    /// unsafe {
104    ///     alloc.grow(10, |_init, (_, _uninit): (_, &mut [MaybeUninit<u64>])| {
105    ///         // `RawMem` relies on the fact that we initialize memory
106    ///         // even if they are primitives
107    ///     })?;
108    /// }
109    /// # Result::Ok(())
110    /// ```
111    unsafe fn grow(
112        &mut self,
113        cap: usize,
114        fill: impl FnOnce(usize, (&mut [Self::Item], &mut [MaybeUninit<Self::Item>])),
115    ) -> Result<&mut [Self::Item]>;
116
117    /// Removes the last `cap` elements, dropping them.
118    fn shrink(&mut self, cap: usize) -> Result<()>;
119
120    /// Returns an optional hint about the total capacity, if known.
121    fn size_hint(&self) -> Option<usize> {
122        None
123    }
124
125    /// [`grow`] which assumes that the memory is already initialized
126    ///
127    /// # Safety
128    ///
129    /// When calling this method, you have to ensure that one of the following is true:
130    ///
131    /// * memory already initialized as [`Item`]
132    ///
133    /// * memory is initialized bytes and [`Item`] can be represented as bytes
134    ///
135    /// # Examples
136    ///
137    /// ```no_run
138    /// # use platform_mem::Result;
139    /// use platform_mem::{FileMapped, RawMem};
140    ///
141    /// let mut file = FileMapped::from_path("..")?;
142    /// // file is always represents as initialized bytes
143    /// // and usize is transparent as bytes
144    /// let _: &mut [usize] = unsafe { file.grow_assumed(10)? };
145    /// # Result::Ok(())
146    /// ```
147    ///
148    /// [`grow`]: Self::grow
149    /// [`Item`]: Self::Item
150    unsafe fn grow_assumed(&mut self, cap: usize) -> Result<&mut [Self::Item]> {
151        // The caller guarantees validity, including bytes the backend has not
152        // counted as initialized (such as fresh zero-filled file padding).
153        unsafe { self.grow(cap, |_, _| {}) }
154    }
155
156    /// Grows by `cap` elements, zeroing only the uninitialized tail.
157    /// Elements already initialized by the backend are preserved.
158    ///
159    /// # Safety
160    /// [`Item`](Self::Item) must satisfy the [initialization invariant][inv] for
161    /// [`core::mem::zeroed`].
162    ///
163    ///  [inv]: MaybeUninit#initialization-invariant
164    ///
165    /// # Examples
166    /// Correct usage of this function: initializing an integral-like types with zeroes:
167    /// ```
168    /// # use platform_mem::Error;
169    /// use platform_mem::{Global, RawMem};
170    ///
171    /// let mut alloc = Global::new();
172    /// let zeroes: &mut [(u8, u16)] = unsafe {
173    ///     alloc.grow_zeroed(10)?
174    /// };
175    ///
176    /// assert_eq!(zeroes, [(0, 0); 10]);
177    /// # Ok::<_, Error>(())
178    /// ```
179    ///
180    /// Incorrect usage of this function: initializing a reference with zero:
181    /// ```no_run
182    /// # use platform_mem::Error;
183    /// use platform_mem::{Global, RawMem};
184    ///
185    /// let mut alloc = Global::new();
186    /// let zeroes: &mut [&'static str] = unsafe {
187    ///     alloc.grow_zeroed(10)? // Undefined behavior!
188    /// };
189    ///
190    /// # Ok::<_, Error>(())
191    /// ```
192    ///
193    unsafe fn grow_zeroed(&mut self, cap: usize) -> Result<&mut [Self::Item]> {
194        unsafe {
195            self.grow(cap, |inited, (_, uninit)| {
196                let uninit = &mut uninit[inited..];
197                uninit.as_mut_ptr().write_bytes(0u8, uninit.len());
198            })
199        }
200    }
201
202    /// Equivalent to [`grow_zeroed`](Self::grow_zeroed), preserving initialized elements.
203    ///
204    /// # Safety
205    /// [`Item`](Self::Item) must satisfy the [initialization invariant](MaybeUninit#initialization-invariant) for zeroed memory.
206    unsafe fn grow_zeroed_exact(&mut self, cap: usize) -> Result<&mut [Self::Item]> {
207        unsafe { self.grow_zeroed(cap) }
208    }
209
210    /// Grows by `addition` elements, calling `f` only for uninitialized elements.
211    /// Elements already initialized by the backend are preserved.
212    fn grow_with(
213        &mut self,
214        addition: usize,
215        f: impl FnMut() -> Self::Item,
216    ) -> Result<&mut [Self::Item]> {
217        unsafe {
218            self.grow(addition, |inited, (_, uninit)| {
219                uninit::fill_with(&mut uninit[inited..], f);
220            })
221        }
222    }
223
224    /// # Safety
225    /// The caller must ensure that `addition` accounts for already-initialized elements in the underlying buffer.
226    unsafe fn grow_with_exact(
227        &mut self,
228        addition: usize,
229        f: impl FnMut() -> Self::Item,
230    ) -> Result<&mut [Self::Item]> {
231        unsafe {
232            self.grow(addition, |inited, (_, uninit)| {
233                uninit::fill_with(&mut uninit[inited..], f);
234            })
235        }
236    }
237
238    /// Grows by `cap` elements, initializing the uninitialized tail from `value`.
239    /// Elements already initialized by the backend are preserved.
240    fn grow_filled(&mut self, cap: usize, value: Self::Item) -> Result<&mut [Self::Item]>
241    where
242        Self::Item: Clone,
243    {
244        unsafe {
245            self.grow(cap, |inited, (_, uninit)| {
246                uninit::fill(&mut uninit[inited..], value);
247            })
248        }
249    }
250
251    /// # Safety
252    /// The caller must ensure that `cap` accounts for already-initialized elements in the underlying buffer.
253    unsafe fn grow_filled_exact(
254        &mut self,
255        cap: usize,
256        value: Self::Item,
257    ) -> Result<&mut [Self::Item]>
258    where
259        Self::Item: Clone,
260    {
261        unsafe {
262            self.grow(cap, |inited, (_, uninit)| {
263                uninit::fill(&mut uninit[inited..], value);
264            })
265        }
266    }
267
268    /// Grows by cloning a sub-range of the currently allocated data.
269    fn grow_within<R: RangeBounds<usize>>(&mut self, range: R) -> Result<&mut [Self::Item]>
270    where
271        Self::Item: Clone,
272    {
273        let Range { start, end } = range_bounds_to_range(range, self.allocated().len());
274        unsafe {
275            self.grow(end - start, |_, (within, uninit)| {
276                uninit::write_clone_of_slice(uninit, &within[start..end]);
277            })
278        }
279    }
280
281    /// Grows by cloning all elements from `src`.
282    fn grow_from_slice(&mut self, src: &[Self::Item]) -> Result<&mut [Self::Item]>
283    where
284        Self::Item: Clone,
285    {
286        unsafe {
287            self.grow(src.len(), |_, (_, uninit)| {
288                uninit::write_clone_of_slice(uninit, src);
289            })
290        }
291    }
292}
293
294/// A callable trait for fill functions, usable as a trait object.
295/// This is a stable alternative to implementing `FnMut` manually.
296pub trait FillFn<T> {
297    /// Invoke the fill function with the given initialized count and memory slices.
298    fn call(&mut self, inited: usize, slices: (&mut [T], &mut [MaybeUninit<T>]));
299}
300
301/// Implements `FillFn` for any `FnMut` closure.
302impl<T, F> FillFn<T> for F
303where
304    F: FnMut(usize, (&mut [T], &mut [MaybeUninit<T>])),
305{
306    fn call(&mut self, inited: usize, slices: (&mut [T], &mut [MaybeUninit<T>])) {
307        self(inited, slices)
308    }
309}
310
311/// A wrapper that allows calling a `FnOnce` through the `FillFn` interface.
312/// Used to pass `FnOnce` closures to erased trait objects that expect `FillFn`.
313struct CallOnce<F> {
314    inner: Option<F>,
315}
316
317impl<F> CallOnce<F> {
318    fn new(f: F) -> Self {
319        Self { inner: Some(f) }
320    }
321}
322
323impl<T, F> FillFn<T> for CallOnce<F>
324where
325    F: FnOnce(usize, (&mut [T], &mut [MaybeUninit<T>])),
326{
327    fn call(&mut self, inited: usize, slices: (&mut [T], &mut [MaybeUninit<T>])) {
328        let f = self.inner.take().expect("CallOnce::call called more than once");
329        f(inited, slices);
330    }
331}
332
333/// # Safety
334/// Implementors must uphold the same memory-safety invariants as [`RawMem`].
335pub unsafe trait ErasedMem {
336    /// The element type.
337    type Item;
338
339    /// Returns a shared slice of initialized elements (type-erased).
340    fn erased_allocated(&self) -> &[Self::Item];
341    /// Returns a mutable slice of initialized elements (type-erased).
342    fn erased_allocated_mut(&mut self) -> &mut [Self::Item];
343
344    /// # Safety
345    /// The caller must guarantee that `fill` fully initializes the uninitialized portion.
346    unsafe fn erased_grow(
347        &mut self,
348        cap: usize,
349        fill: &mut dyn FillFn<Self::Item>,
350    ) -> Result<&mut [Self::Item]>;
351
352    /// Removes the last `cap` elements (type-erased).
353    fn erased_shrink(&mut self, cap: usize) -> Result<()>;
354
355    /// Returns an optional capacity hint (type-erased).
356    fn erased_size_hint(&self) -> Option<usize> {
357        None
358    }
359}
360
361macro_rules! impl_erased {
362    ($ty:ty => $($imp:tt)+) => {
363        impl $($imp)+ {
364            type Item = $ty;
365
366            fn allocated(&self) -> &[Self::Item] {
367                (**self).erased_allocated()
368            }
369
370            fn allocated_mut(&mut self) -> &mut [Self::Item] {
371                (**self).erased_allocated_mut()
372            }
373
374            unsafe fn grow(
375                &mut self,
376                cap: usize,
377                fill: impl FnOnce(usize, (&mut [Self::Item], &mut [MaybeUninit<Self::Item>])),
378            ) -> Result<&mut [Self::Item]> { unsafe {
379                (**self).erased_grow(cap, &mut CallOnce::new(fill))
380            }}
381
382            fn shrink(&mut self, cap: usize) -> Result<()> {
383                (**self).erased_shrink(cap)
384            }
385
386            fn size_hint(&self) -> Option<usize> {
387                (**self).erased_size_hint()
388            }
389        }
390    };
391}
392
393impl_erased!(All::Item => <'a, All: ?Sized + RawMem> RawMem for &'a mut All);
394
395impl_erased!(I => <'a, I> RawMem for Box<dyn ErasedMem<Item = I> + 'a>);
396impl_erased!(I => <'a, I> RawMem for Box<dyn ErasedMem<Item = I> + Sync + 'a>);
397impl_erased!(I => <'a, I> RawMem for Box<dyn ErasedMem<Item = I> + Sync + Send + 'a>);
398
399unsafe impl<All: RawMem + ?Sized> ErasedMem for All {
400    type Item = All::Item;
401
402    fn erased_allocated(&self) -> &[Self::Item] {
403        self.allocated()
404    }
405
406    fn erased_allocated_mut(&mut self) -> &mut [Self::Item] {
407        self.allocated_mut()
408    }
409
410    unsafe fn erased_grow(
411        &mut self,
412        cap: usize,
413        fill: &mut dyn FillFn<Self::Item>,
414    ) -> Result<&mut [Self::Item]> {
415        unsafe { self.grow(cap, |inited, slices| fill.call(inited, slices)) }
416    }
417
418    fn erased_shrink(&mut self, cap: usize) -> Result<()> {
419        self.shrink(cap)
420    }
421
422    fn erased_size_hint(&self) -> Option<usize> {
423        self.size_hint()
424    }
425}
426
427/// Utilities for initializing `MaybeUninit` slices.
428///
429/// These are stable alternatives to nightly-only `MaybeUninit` slice methods.
430pub mod uninit {
431    use std::{mem, mem::MaybeUninit, ptr, slice};
432
433    /// Stable alternative to `MaybeUninit::slice_assume_init_mut`.
434    ///
435    /// # Safety
436    /// All elements of `s` must be initialized.
437    #[inline]
438    pub unsafe fn assume_init_mut<T>(s: &mut [MaybeUninit<T>]) -> &mut [T] {
439        // SAFETY: The caller guarantees all elements are initialized.
440        // MaybeUninit<T> has the same layout as T.
441        unsafe { slice::from_raw_parts_mut(s.as_mut_ptr().cast::<T>(), s.len()) }
442    }
443
444    /// Stable alternative to `MaybeUninit::write_slice_cloned` / `write_clone_of_slice`.
445    ///
446    /// # Panics
447    /// Panics if `uninit.len() != src.len()`.
448    pub fn write_clone_of_slice<T: Clone>(uninit: &mut [MaybeUninit<T>], src: &[T]) {
449        assert_eq!(uninit.len(), src.len(), "slice lengths must match");
450
451        for (dst, val) in uninit.iter_mut().zip(src.iter()) {
452            dst.write(val.clone());
453        }
454    }
455
456    /// Initializes all elements of `uninit` by cloning `val`. Panic-safe.
457    pub fn fill<T: Clone>(uninit: &mut [MaybeUninit<T>], val: T) {
458        let mut guard = Guard { slice: uninit, init: 0 };
459
460        if let Some((last, elems)) = guard.slice.split_last_mut() {
461            for el in elems.iter_mut() {
462                el.write(val.clone());
463                guard.init += 1;
464            }
465            last.write(val);
466            guard.init += 1;
467        }
468
469        mem::forget(guard);
470    }
471
472    /// Initializes all elements of `uninit` using the closure `fill`. Panic-safe.
473    pub fn fill_with<T>(uninit: &mut [MaybeUninit<T>], mut fill: impl FnMut() -> T) {
474        let mut guard = Guard { slice: uninit, init: 0 };
475
476        for el in guard.slice.iter_mut() {
477            el.write(fill());
478            guard.init += 1;
479        }
480
481        mem::forget(guard);
482    }
483
484    struct Guard<'a, T> {
485        slice: &'a mut [MaybeUninit<T>],
486        init: usize,
487    }
488
489    impl<T> Drop for Guard<'_, T> {
490        fn drop(&mut self) {
491            debug_assert!(self.init <= self.slice.len());
492            // SAFETY: this raw slice will contain only initialized objects
493            // that's why, it is allowed to drop it.
494            unsafe {
495                let inited_slice = self.slice.get_unchecked_mut(..self.init);
496                ptr::drop_in_place(assume_init_mut(inited_slice));
497            }
498        }
499    }
500}