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}