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 count of elements already initialized by the backend.
89    ///
90    /// # Safety
91    /// Caller must guarantee that `fill` fully initializes the uninitialized portion.
92    ///
93    /// ### Incorrect usage
94    /// ```no_run
95    /// # use allocator_api2::alloc::Global;
96    /// # use std::mem::MaybeUninit;
97    /// # use platform_mem::Result;
98    /// use platform_mem::{Alloc, RawMem};
99    ///
100    /// let mut alloc = Alloc::new(Global);
101    /// unsafe {
102    ///     alloc.grow(10, |_init, (_, _uninit): (_, &mut [MaybeUninit<u64>])| {
103    ///         // `RawMem` relies on the fact that we initialize memory
104    ///         // even if they are primitives
105    ///     })?;
106    /// }
107    /// # Result::Ok(())
108    /// ```
109    unsafe fn grow(
110        &mut self,
111        cap: usize,
112        fill: impl FnOnce(usize, (&mut [Self::Item], &mut [MaybeUninit<Self::Item>])),
113    ) -> Result<&mut [Self::Item]>;
114
115    /// Removes the last `cap` elements, dropping them.
116    fn shrink(&mut self, cap: usize) -> Result<()>;
117
118    /// Returns an optional hint about the total capacity, if known.
119    fn size_hint(&self) -> Option<usize> {
120        None
121    }
122
123    /// [`grow`] which assumes that the memory is already initialized
124    ///
125    /// # Safety
126    ///
127    /// When calling this method, you have to ensure that one of the following is true:
128    ///
129    /// * memory already initialized as [`Item`]
130    ///
131    /// * memory is initialized bytes and [`Item`] can be represented as bytes
132    ///
133    /// # Examples
134    ///
135    /// ```no_run
136    /// # use platform_mem::Result;
137    /// use platform_mem::{FileMapped, RawMem};
138    ///
139    /// let mut file = FileMapped::from_path("..")?;
140    /// // file is always represents as initialized bytes
141    /// // and usize is transparent as bytes
142    /// let _: &mut [usize] = unsafe { file.grow_assumed(10)? };
143    /// # Result::Ok(())
144    /// ```
145    ///
146    /// [`grow`]: Self::grow
147    /// [`Item`]: Self::Item
148    unsafe fn grow_assumed(&mut self, cap: usize) -> Result<&mut [Self::Item]> {
149        unsafe {
150            self.grow(cap, |inited, (_, uninit)| {
151                // fixme: maybe change it to `assert_eq!`
152                debug_assert_eq!(
153                    inited,
154                    uninit.len(),
155                    "grown memory must be initialized, \
156                 usually allocators-like provide uninitialized memory, \
157                 which is only safe for writing"
158                )
159            })
160        }
161    }
162
163    /// # Safety
164    /// [`Item`](Self::Item) must satisfy the [initialization invariant][inv] for
165    /// [`core::mem::zeroed`].
166    ///
167    ///  [inv]: MaybeUninit#initialization-invariant
168    ///
169    /// # Examples
170    /// Correct usage of this function: initializing an integral-like types with zeroes:
171    /// ```
172    /// # use platform_mem::Error;
173    /// use platform_mem::{Global, RawMem};
174    ///
175    /// let mut alloc = Global::new();
176    /// let zeroes: &mut [(u8, u16)] = unsafe {
177    ///     alloc.grow_zeroed(10)?
178    /// };
179    ///
180    /// assert_eq!(zeroes, [(0, 0); 10]);
181    /// # Ok::<_, Error>(())
182    /// ```
183    ///
184    /// Incorrect usage of this function: initializing a reference with zero:
185    /// ```no_run
186    /// # use platform_mem::Error;
187    /// use platform_mem::{Global, RawMem};
188    ///
189    /// let mut alloc = Global::new();
190    /// let zeroes: &mut [&'static str] = unsafe {
191    ///     alloc.grow_zeroed(10)? // Undefined behavior!
192    /// };
193    ///
194    /// # Ok::<_, Error>(())
195    /// ```
196    ///
197    unsafe fn grow_zeroed(&mut self, cap: usize) -> Result<&mut [Self::Item]> {
198        unsafe {
199            self.grow(cap, |_, (_, uninit)| {
200                uninit.as_mut_ptr().write_bytes(0u8, uninit.len());
201            })
202        }
203    }
204
205    /// # Safety
206    /// [`Item`](Self::Item) must satisfy the [initialization invariant](MaybeUninit#initialization-invariant) for zeroed memory.
207    unsafe fn grow_zeroed_exact(&mut self, cap: usize) -> Result<&mut [Self::Item]> {
208        unsafe {
209            self.grow(cap, |inited, (_, uninit)| {
210                uninit.get_unchecked_mut(inited..).as_mut_ptr().write_bytes(0u8, uninit.len());
211            })
212        }
213    }
214
215    /// Grows by `addition` elements, initializing each with the closure `f`.
216    fn grow_with(
217        &mut self,
218        addition: usize,
219        f: impl FnMut() -> Self::Item,
220    ) -> Result<&mut [Self::Item]> {
221        unsafe {
222            self.grow(addition, |_, (_, uninit)| {
223                uninit::fill_with(uninit, f);
224            })
225        }
226    }
227
228    /// # Safety
229    /// The caller must ensure that `addition` accounts for already-initialized elements in the underlying buffer.
230    unsafe fn grow_with_exact(
231        &mut self,
232        addition: usize,
233        f: impl FnMut() -> Self::Item,
234    ) -> Result<&mut [Self::Item]> {
235        unsafe {
236            self.grow(addition, |inited, (_, uninit)| {
237                uninit::fill_with(&mut uninit[inited..], f);
238            })
239        }
240    }
241
242    /// Grows by `cap` elements, each cloned from `value`.
243    fn grow_filled(&mut self, cap: usize, value: Self::Item) -> Result<&mut [Self::Item]>
244    where
245        Self::Item: Clone,
246    {
247        unsafe {
248            self.grow(cap, |_, (_, uninit)| {
249                uninit::fill(uninit, value);
250            })
251        }
252    }
253
254    /// # Safety
255    /// The caller must ensure that `cap` accounts for already-initialized elements in the underlying buffer.
256    unsafe fn grow_filled_exact(
257        &mut self,
258        cap: usize,
259        value: Self::Item,
260    ) -> Result<&mut [Self::Item]>
261    where
262        Self::Item: Clone,
263    {
264        unsafe {
265            self.grow(cap, |inited, (_, uninit)| {
266                uninit::fill(&mut uninit[inited..], value);
267            })
268        }
269    }
270
271    /// Grows by cloning a sub-range of the currently allocated data.
272    fn grow_within<R: RangeBounds<usize>>(&mut self, range: R) -> Result<&mut [Self::Item]>
273    where
274        Self::Item: Clone,
275    {
276        let Range { start, end } = range_bounds_to_range(range, self.allocated().len());
277        unsafe {
278            self.grow(end - start, |_, (within, uninit)| {
279                uninit::write_clone_of_slice(uninit, &within[start..end]);
280            })
281        }
282    }
283
284    /// Grows by cloning all elements from `src`.
285    fn grow_from_slice(&mut self, src: &[Self::Item]) -> Result<&mut [Self::Item]>
286    where
287        Self::Item: Clone,
288    {
289        unsafe {
290            self.grow(src.len(), |_, (_, uninit)| {
291                uninit::write_clone_of_slice(uninit, src);
292            })
293        }
294    }
295}
296
297/// A callable trait for fill functions, usable as a trait object.
298/// This is a stable alternative to implementing `FnMut` manually.
299pub trait FillFn<T> {
300    /// Invoke the fill function with the given initialized count and memory slices.
301    fn call(&mut self, inited: usize, slices: (&mut [T], &mut [MaybeUninit<T>]));
302}
303
304/// Implements `FillFn` for any `FnMut` closure.
305impl<T, F> FillFn<T> for F
306where
307    F: FnMut(usize, (&mut [T], &mut [MaybeUninit<T>])),
308{
309    fn call(&mut self, inited: usize, slices: (&mut [T], &mut [MaybeUninit<T>])) {
310        self(inited, slices)
311    }
312}
313
314/// A wrapper that allows calling a `FnOnce` through the `FillFn` interface.
315/// Used to pass `FnOnce` closures to erased trait objects that expect `FillFn`.
316struct CallOnce<F> {
317    inner: Option<F>,
318}
319
320impl<F> CallOnce<F> {
321    fn new(f: F) -> Self {
322        Self { inner: Some(f) }
323    }
324}
325
326impl<T, F> FillFn<T> for CallOnce<F>
327where
328    F: FnOnce(usize, (&mut [T], &mut [MaybeUninit<T>])),
329{
330    fn call(&mut self, inited: usize, slices: (&mut [T], &mut [MaybeUninit<T>])) {
331        let f = self.inner.take().expect("CallOnce::call called more than once");
332        f(inited, slices);
333    }
334}
335
336/// # Safety
337/// Implementors must uphold the same memory-safety invariants as [`RawMem`].
338pub unsafe trait ErasedMem {
339    /// The element type.
340    type Item;
341
342    /// Returns a shared slice of initialized elements (type-erased).
343    fn erased_allocated(&self) -> &[Self::Item];
344    /// Returns a mutable slice of initialized elements (type-erased).
345    fn erased_allocated_mut(&mut self) -> &mut [Self::Item];
346
347    /// # Safety
348    /// The caller must guarantee that `fill` fully initializes the uninitialized portion.
349    unsafe fn erased_grow(
350        &mut self,
351        cap: usize,
352        fill: &mut dyn FillFn<Self::Item>,
353    ) -> Result<&mut [Self::Item]>;
354
355    /// Removes the last `cap` elements (type-erased).
356    fn erased_shrink(&mut self, cap: usize) -> Result<()>;
357
358    /// Returns an optional capacity hint (type-erased).
359    fn erased_size_hint(&self) -> Option<usize> {
360        None
361    }
362}
363
364macro_rules! impl_erased {
365    ($ty:ty => $($imp:tt)+) => {
366        impl $($imp)+ {
367            type Item = $ty;
368
369            fn allocated(&self) -> &[Self::Item] {
370                (**self).erased_allocated()
371            }
372
373            fn allocated_mut(&mut self) -> &mut [Self::Item] {
374                (**self).erased_allocated_mut()
375            }
376
377            unsafe fn grow(
378                &mut self,
379                cap: usize,
380                fill: impl FnOnce(usize, (&mut [Self::Item], &mut [MaybeUninit<Self::Item>])),
381            ) -> Result<&mut [Self::Item]> { unsafe {
382                (**self).erased_grow(cap, &mut CallOnce::new(fill))
383            }}
384
385            fn shrink(&mut self, cap: usize) -> Result<()> {
386                (**self).erased_shrink(cap)
387            }
388
389            fn size_hint(&self) -> Option<usize> {
390                (**self).erased_size_hint()
391            }
392        }
393    };
394}
395
396impl_erased!(All::Item => <'a, All: ?Sized + RawMem> RawMem for &'a mut All);
397
398impl_erased!(I => <'a, I> RawMem for Box<dyn ErasedMem<Item = I> + 'a>);
399impl_erased!(I => <'a, I> RawMem for Box<dyn ErasedMem<Item = I> + Sync + 'a>);
400impl_erased!(I => <'a, I> RawMem for Box<dyn ErasedMem<Item = I> + Sync + Send + 'a>);
401
402unsafe impl<All: RawMem + ?Sized> ErasedMem for All {
403    type Item = All::Item;
404
405    fn erased_allocated(&self) -> &[Self::Item] {
406        self.allocated()
407    }
408
409    fn erased_allocated_mut(&mut self) -> &mut [Self::Item] {
410        self.allocated_mut()
411    }
412
413    unsafe fn erased_grow(
414        &mut self,
415        cap: usize,
416        fill: &mut dyn FillFn<Self::Item>,
417    ) -> Result<&mut [Self::Item]> {
418        unsafe { self.grow(cap, |inited, slices| fill.call(inited, slices)) }
419    }
420
421    fn erased_shrink(&mut self, cap: usize) -> Result<()> {
422        self.shrink(cap)
423    }
424
425    fn erased_size_hint(&self) -> Option<usize> {
426        self.size_hint()
427    }
428}
429
430/// Utilities for initializing `MaybeUninit` slices.
431///
432/// These are stable alternatives to nightly-only `MaybeUninit` slice methods.
433pub mod uninit {
434    use std::{mem, mem::MaybeUninit, ptr, slice};
435
436    /// Stable alternative to `MaybeUninit::slice_assume_init_mut`.
437    ///
438    /// # Safety
439    /// All elements of `s` must be initialized.
440    #[inline]
441    pub unsafe fn assume_init_mut<T>(s: &mut [MaybeUninit<T>]) -> &mut [T] {
442        // SAFETY: The caller guarantees all elements are initialized.
443        // MaybeUninit<T> has the same layout as T.
444        unsafe { slice::from_raw_parts_mut(s.as_mut_ptr().cast::<T>(), s.len()) }
445    }
446
447    /// Stable alternative to `MaybeUninit::write_slice_cloned` / `write_clone_of_slice`.
448    ///
449    /// # Panics
450    /// Panics if `uninit.len() != src.len()`.
451    pub fn write_clone_of_slice<T: Clone>(uninit: &mut [MaybeUninit<T>], src: &[T]) {
452        assert_eq!(uninit.len(), src.len(), "slice lengths must match");
453
454        for (dst, val) in uninit.iter_mut().zip(src.iter()) {
455            dst.write(val.clone());
456        }
457    }
458
459    /// Initializes all elements of `uninit` by cloning `val`. Panic-safe.
460    pub fn fill<T: Clone>(uninit: &mut [MaybeUninit<T>], val: T) {
461        let mut guard = Guard { slice: uninit, init: 0 };
462
463        if let Some((last, elems)) = guard.slice.split_last_mut() {
464            for el in elems.iter_mut() {
465                el.write(val.clone());
466                guard.init += 1;
467            }
468            last.write(val);
469            guard.init += 1;
470        }
471
472        mem::forget(guard);
473    }
474
475    /// Initializes all elements of `uninit` using the closure `fill`. Panic-safe.
476    pub fn fill_with<T>(uninit: &mut [MaybeUninit<T>], mut fill: impl FnMut() -> T) {
477        let mut guard = Guard { slice: uninit, init: 0 };
478
479        for el in guard.slice.iter_mut() {
480            el.write(fill());
481            guard.init += 1;
482        }
483
484        mem::forget(guard);
485    }
486
487    struct Guard<'a, T> {
488        slice: &'a mut [MaybeUninit<T>],
489        init: usize,
490    }
491
492    impl<T> Drop for Guard<'_, T> {
493        fn drop(&mut self) {
494            debug_assert!(self.init <= self.slice.len());
495            // SAFETY: this raw slice will contain only initialized objects
496            // that's why, it is allowed to drop it.
497            unsafe {
498                let inited_slice = self.slice.get_unchecked_mut(..self.init);
499                ptr::drop_in_place(assume_init_mut(inited_slice));
500            }
501        }
502    }
503}