stdx\alloc/
allocator.rs

1use core::alloc::Layout;
2use core::error::Error;
3use core::fmt;
4use core::ptr::{self, NonNull};
5
6/// Creates a dangling `NonNull<T>` pointer from a given [`Layout`].
7///
8/// This is similar to `NonNull::dangling()`, but uses the alignment from the provided [`Layout`]
9/// to construct the dangling pointer. This is useful when the alignment must match a specific type,
10/// such as when working with allocators or raw memory buffers.
11///
12/// # Safety
13///
14/// The pointer returned is *not* valid for dereferencing. It is only intended for use in
15/// situations where a non-null but invalid pointer is needed (e.g., for type information or alignment).
16///
17/// Internally, this avoids the `NonZero` validity check for performance reasons, assuming that the
18/// `Layout` is already valid (i.e., has a non-zero alignment).
19#[inline]
20const fn non_null_from_layout_dangling<T>(layout: Layout) -> NonNull<T> {
21    let addr = {
22        let this = layout.align();
23        // This transmutes directly to avoid the UbCheck in `NonZero::new_unchecked`
24        // since there's no way for the user to trip that check anyway -- the
25        // validity invariant of the type would have to have been broken earlier --
26        // and emitting it in an otherwise simple method is bad for compile time.
27
28        // SAFETY: All the discriminants are non-zero.
29        unsafe { ::core::num::NonZero::new_unchecked(this) }
30    };
31    let pointer: *const T = ptr::without_provenance(addr.get());
32    // SAFETY: we know `addr` is non-zero.
33    unsafe { NonNull::new_unchecked(pointer.cast_mut()) }
34}
35
36/// Creates a dangling `NonNull<[u8]>` slice of size 0 with the specified layout alignment.
37///
38/// This function is useful for placeholder or sentinel values where a
39/// `NonNull<[u8]>` is required, but no actual memory needs to be allocated.
40///
41/// # Examples
42///
43/// ```
44/// use core::alloc::Layout;
45/// use core::ptr::NonNull;
46///
47/// let layout = Layout::from_size_align(16, 8).unwrap();
48/// let empty = non_null_empty_slice(layout);
49/// assert_eq!(empty.len(), 0);
50/// ```
51#[inline]
52pub const fn non_null_empty_slice(layout: Layout) -> NonNull<[u8]> {
53    NonNull::slice_from_raw_parts(non_null_from_layout_dangling(layout), 0)
54}
55
56/// The `AllocError` error indicates an allocation failure
57/// that may be due to resource exhaustion or to
58/// something wrong when combining the given input arguments with this
59/// allocator.
60// #[unstable(feature = "allocator_api", issue = "32838")]
61#[derive(Copy, Clone, PartialEq, Eq, Debug)]
62pub struct AllocError;
63
64impl Error for AllocError {}
65
66// (we need this for downstream impl of trait Error)
67// #[unstable(feature = "allocator_api", issue = "32838")]
68impl fmt::Display for AllocError {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        f.write_str("memory allocation failed")
71    }
72}
73
74/// An implementation of `Allocator` can allocate, grow, shrink, and deallocate arbitrary blocks of
75/// data described via [`Layout`][].
76///
77/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers.
78/// An allocator for `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the
79/// allocated memory.
80///
81/// In contrast to `GlobalAlloc`, `Allocator` allows zero-sized allocations. If an underlying
82/// allocator does not support this (like jemalloc) or responds by returning a null pointer
83/// (such as `libc::malloc`), this must be caught by the implementation.
84///
85/// ### Currently allocated memory
86///
87/// Some of the methods require that a memory block is *currently allocated* by an allocator.
88/// This means that:
89///  * the starting address for that memory block was previously
90///    returned by [`allocate`], [`grow`], or [`shrink`], and
91///  * the memory block has not subsequently been deallocated.
92///
93/// A memory block is deallocated by a call to [`deallocate`],
94/// or by a call to [`grow`] or [`shrink`] that returns `Ok`.
95/// A call to `grow` or `shrink` that returns `Err`,
96/// does not deallocate the memory block passed to it.
97///
98/// [`allocate`]: Allocator::allocate
99/// [`grow`]: Allocator::grow
100/// [`shrink`]: Allocator::shrink
101/// [`deallocate`]: Allocator::deallocate
102///
103/// ### Memory fitting
104///
105/// Some of the methods require that a `layout` *fit* a memory block or vice versa. This means that the
106/// following conditions must hold:
107///  * the memory block must be *currently allocated* with alignment of [`layout.align()`], and
108///  * [`layout.size()`] must fall in the range `min ..= max`, where:
109///    - `min` is the size of the layout used to allocate the block, and
110///    - `max` is the actual size returned from [`allocate`], [`grow`], or [`shrink`].
111///
112/// [`layout.align()`]: Layout::align
113/// [`layout.size()`]: Layout::size
114///
115/// # Safety
116///
117/// Memory blocks that are [*currently allocated*] by an allocator,
118/// must point to valid memory, and retain their validity while until either:
119///  - the memory block is deallocated, or
120///  - the allocator is dropped.
121///
122/// Copying, cloning, or moving the allocator must not invalidate memory blocks returned from it.
123/// A copied or cloned allocator must behave like the original allocator.
124///
125/// A memory block which is [*currently allocated*] may be passed to
126/// any method of the allocator that accepts such an argument.
127///
128/// [*currently allocated*]: #currently-allocated-memory
129// #[unstable(feature = "allocator_api", issue = "32838")]
130pub unsafe trait Allocator {
131    /// Attempts to allocate a block of memory.
132    ///
133    /// On success, returns a [`NonNull<[u8]>`][NonNull] meeting the size and alignment guarantees of `layout`.
134    ///
135    /// The returned block may have a larger size than specified by `layout.size()`, and may or may
136    /// not have its contents initialized.
137    ///
138    /// The returned block of memory remains valid as long as it is [*currently allocated*] and the shorter of:
139    ///   - the borrow-checker lifetime of the allocator type itself.
140    ///   - as long as at the allocator and all its clones has not been dropped.
141    ///
142    /// # Errors
143    ///
144    /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
145    /// allocator's size or alignment constraints.
146    ///
147    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
148    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
149    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
150    ///
151    /// Clients wishing to abort computation in response to an allocation error are encouraged to
152    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
153    ///
154    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
155    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
156
157    /// Behaves like `allocate`, but also ensures that the returned memory is zero-initialized.
158    ///
159    /// # Errors
160    ///
161    /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
162    /// allocator's size or alignment constraints.
163    ///
164    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
165    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
166    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
167    ///
168    /// Clients wishing to abort computation in response to an allocation error are encouraged to
169    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
170    ///
171    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
172    fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
173        let ptr = self.allocate(layout)?;
174        // SAFETY: `alloc` returns a valid memory block
175        // unsafe { ptr.as_non_null_ptr().as_ptr().write_bytes(0, ptr.len()) }
176        unsafe { ptr.cast::<u8>().as_ptr().write_bytes(0, ptr.len()) }
177        Ok(ptr)
178    }
179
180    /// Deallocates the memory referenced by `ptr`.
181    ///
182    /// # Safety
183    ///
184    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, and
185    /// * `layout` must [*fit*] that block of memory.
186    ///
187    /// [*currently allocated*]: #currently-allocated-memory
188    /// [*fit*]: #memory-fitting
189    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
190
191    /// Attempts to extend the memory block.
192    ///
193    /// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
194    /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
195    /// this, the allocator may extend the allocation referenced by `ptr` to fit the new layout.
196    ///
197    /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
198    /// transferred to this allocator. Any access to the old `ptr` is Undefined Behavior, even if the
199    /// allocation was grown in-place. The newly returned pointer is the only valid pointer
200    /// for accessing this memory now.
201    ///
202    /// If this method returns `Err`, then ownership of the memory block has not been transferred to
203    /// this allocator, and the contents of the memory block are unaltered.
204    ///
205    /// # Safety
206    ///
207    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
208    /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
209    /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
210    ///
211    /// Note that `new_layout.align()` need not be the same as `old_layout.align()`.
212    ///
213    /// [*currently allocated*]: #currently-allocated-memory
214    /// [*fit*]: #memory-fitting
215    ///
216    /// # Errors
217    ///
218    /// Returns `Err` if the new layout does not meet the allocator's size and alignment
219    /// constraints of the allocator, or if growing otherwise fails.
220    ///
221    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
222    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
223    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
224    ///
225    /// Clients wishing to abort computation in response to an allocation error are encouraged to
226    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
227    ///
228    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
229    unsafe fn grow(
230        &self,
231        ptr: NonNull<u8>,
232        old_layout: Layout,
233        new_layout: Layout,
234    ) -> Result<NonNull<[u8]>, AllocError> {
235        debug_assert!(
236            new_layout.size() >= old_layout.size(),
237            "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
238        );
239
240        let new_ptr = self.allocate(new_layout)?;
241
242        // SAFETY: because `new_layout.size()` must be greater than or equal to
243        // `old_layout.size()`, both the old and new memory allocation are valid for reads and
244        // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
245        // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
246        // safe. The safety contract for `dealloc` must be upheld by the caller.
247        unsafe {
248            // ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_mut_ptr(), old_layout.size());
249            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.cast().as_ptr(), old_layout.size());
250            self.deallocate(ptr, old_layout);
251        }
252
253        Ok(new_ptr)
254    }
255
256    /// Behaves like `grow`, but also ensures that the new contents are set to zero before being
257    /// returned.
258    ///
259    /// The memory block will contain the following contents after a successful call to
260    /// `grow_zeroed`:
261    ///   * Bytes `0..old_layout.size()` are preserved from the original allocation.
262    ///   * Bytes `old_layout.size()..old_size` will either be preserved or zeroed, depending on
263    ///     the allocator implementation. `old_size` refers to the size of the memory block prior
264    ///     to the `grow_zeroed` call, which may be larger than the size that was originally
265    ///     requested when it was allocated.
266    ///   * Bytes `old_size..new_size` are zeroed. `new_size` refers to the size of the memory
267    ///     block returned by the `grow_zeroed` call.
268    ///
269    /// # Safety
270    ///
271    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
272    /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
273    /// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
274    ///
275    /// Note that `new_layout.align()` need not be the same as `old_layout.align()`.
276    ///
277    /// [*currently allocated*]: #currently-allocated-memory
278    /// [*fit*]: #memory-fitting
279    ///
280    /// # Errors
281    ///
282    /// Returns `Err` if the new layout does not meet the allocator's size and alignment
283    /// constraints of the allocator, or if growing otherwise fails.
284    ///
285    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
286    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
287    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
288    ///
289    /// Clients wishing to abort computation in response to an allocation error are encouraged to
290    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
291    ///
292    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
293    unsafe fn grow_zeroed(
294        &self,
295        ptr: NonNull<u8>,
296        old_layout: Layout,
297        new_layout: Layout,
298    ) -> Result<NonNull<[u8]>, AllocError> {
299        debug_assert!(
300            new_layout.size() >= old_layout.size(),
301            "`new_layout.size()` must be greater than or equal to `old_layout.size()`"
302        );
303
304        let new_ptr = self.allocate_zeroed(new_layout)?;
305
306        // SAFETY: because `new_layout.size()` must be greater than or equal to
307        // `old_layout.size()`, both the old and new memory allocation are valid for reads and
308        // writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
309        // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
310        // safe. The safety contract for `dealloc` must be upheld by the caller.
311        unsafe {
312            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.cast().as_ptr(), old_layout.size());
313            self.deallocate(ptr, old_layout);
314        }
315
316        Ok(new_ptr)
317    }
318
319    /// Attempts to shrink the memory block.
320    ///
321    /// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
322    /// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
323    /// this, the allocator may shrink the allocation referenced by `ptr` to fit the new layout.
324    ///
325    /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
326    /// transferred to this allocator. Any access to the old `ptr` is Undefined Behavior, even if the
327    /// allocation was shrunk in-place. The newly returned pointer is the only valid pointer
328    /// for accessing this memory now.
329    ///
330    /// If this method returns `Err`, then ownership of the memory block has not been transferred to
331    /// this allocator, and the contents of the memory block are unaltered.
332    ///
333    /// # Safety
334    ///
335    /// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
336    /// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
337    /// * `new_layout.size()` must be smaller than or equal to `old_layout.size()`.
338    ///
339    /// Note that `new_layout.align()` need not be the same as `old_layout.align()`.
340    ///
341    /// [*currently allocated*]: #currently-allocated-memory
342    /// [*fit*]: #memory-fitting
343    ///
344    /// # Errors
345    ///
346    /// Returns `Err` if the new layout does not meet the allocator's size and alignment
347    /// constraints of the allocator, or if shrinking otherwise fails.
348    ///
349    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
350    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
351    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
352    ///
353    /// Clients wishing to abort computation in response to an allocation error are encouraged to
354    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
355    ///
356    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
357    unsafe fn shrink(
358        &self,
359        ptr: NonNull<u8>,
360        old_layout: Layout,
361        new_layout: Layout,
362    ) -> Result<NonNull<[u8]>, AllocError> {
363        debug_assert!(
364            new_layout.size() <= old_layout.size(),
365            "`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
366        );
367
368        let new_ptr = self.allocate(new_layout)?;
369
370        // SAFETY: because `new_layout.size()` must be lower than or equal to
371        // `old_layout.size()`, both the old and new memory allocation are valid for reads and
372        // writes for `new_layout.size()` bytes. Also, because the old allocation wasn't yet
373        // deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
374        // safe. The safety contract for `dealloc` must be upheld by the caller.
375        unsafe {
376            ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.cast().as_ptr(), new_layout.size());
377            self.deallocate(ptr, old_layout);
378        }
379
380        Ok(new_ptr)
381    }
382
383    /// Creates a "by reference" adapter for this instance of `Allocator`.
384    ///
385    /// The returned adapter also implements `Allocator` and will simply borrow this.
386    #[inline(always)]
387    #[allow(unused)]
388    fn by_ref(&self) -> &Self
389    where
390        Self: Sized,
391    {
392        self
393    }
394}
395
396// #[unstable(feature = "allocator_api", issue = "32838")]
397unsafe impl<A> Allocator for &A
398where
399    A: Allocator + ?Sized,
400{
401    #[inline]
402    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
403        (**self).allocate(layout)
404    }
405
406    #[inline]
407    fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
408        (**self).allocate_zeroed(layout)
409    }
410
411    #[inline]
412    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
413        // SAFETY: the safety contract must be upheld by the caller
414        unsafe { (**self).deallocate(ptr, layout) }
415    }
416
417    #[inline]
418    unsafe fn grow(
419        &self,
420        ptr: NonNull<u8>,
421        old_layout: Layout,
422        new_layout: Layout,
423    ) -> Result<NonNull<[u8]>, AllocError> {
424        // SAFETY: the safety contract must be upheld by the caller
425        unsafe { (**self).grow(ptr, old_layout, new_layout) }
426    }
427
428    #[inline]
429    unsafe fn grow_zeroed(
430        &self,
431        ptr: NonNull<u8>,
432        old_layout: Layout,
433        new_layout: Layout,
434    ) -> Result<NonNull<[u8]>, AllocError> {
435        // SAFETY: the safety contract must be upheld by the caller
436        unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) }
437    }
438
439    #[inline]
440    unsafe fn shrink(
441        &self,
442        ptr: NonNull<u8>,
443        old_layout: Layout,
444        new_layout: Layout,
445    ) -> Result<NonNull<[u8]>, AllocError> {
446        // SAFETY: the safety contract must be upheld by the caller
447        unsafe { (**self).shrink(ptr, old_layout, new_layout) }
448    }
449}