allocator_api2/alloc/
mod.rs

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