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}