208 lines
8.5 KiB
Rust
208 lines
8.5 KiB
Rust
use crate::alloc::Layout;
|
||
use crate::cmp;
|
||
use crate::ptr;
|
||
|
||
/// A memory allocator that can be registered as the standard library’s default
|
||
/// through the `#[global_allocator]` attribute.
|
||
///
|
||
/// Some of the methods require that a memory block be *currently
|
||
/// allocated* via an allocator. This means that:
|
||
///
|
||
/// * the starting address for that memory block was previously
|
||
/// returned by a previous call to an allocation method
|
||
/// such as `alloc`, and
|
||
///
|
||
/// * the memory block has not been subsequently deallocated, where
|
||
/// blocks are deallocated either by being passed to a deallocation
|
||
/// method such as `dealloc` or by being
|
||
/// passed to a reallocation method that returns a non-null pointer.
|
||
///
|
||
///
|
||
/// # Example
|
||
///
|
||
/// ```no_run
|
||
/// use std::alloc::{GlobalAlloc, Layout, alloc};
|
||
/// use std::ptr::null_mut;
|
||
///
|
||
/// struct MyAllocator;
|
||
///
|
||
/// unsafe impl GlobalAlloc for MyAllocator {
|
||
/// unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { null_mut() }
|
||
/// unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
|
||
/// }
|
||
///
|
||
/// #[global_allocator]
|
||
/// static A: MyAllocator = MyAllocator;
|
||
///
|
||
/// fn main() {
|
||
/// unsafe {
|
||
/// assert!(alloc(Layout::new::<u32>()).is_null())
|
||
/// }
|
||
/// }
|
||
/// ```
|
||
///
|
||
/// # Safety
|
||
///
|
||
/// The `GlobalAlloc` trait is an `unsafe` trait for a number of reasons, and
|
||
/// implementors must ensure that they adhere to these contracts:
|
||
///
|
||
/// * It's undefined behavior if global allocators unwind. This restriction may
|
||
/// be lifted in the future, but currently a panic from any of these
|
||
/// functions may lead to memory unsafety.
|
||
///
|
||
/// * `Layout` queries and calculations in general must be correct. Callers of
|
||
/// this trait are allowed to rely on the contracts defined on each method,
|
||
/// and implementors must ensure such contracts remain true.
|
||
#[stable(feature = "global_alloc", since = "1.28.0")]
|
||
pub unsafe trait GlobalAlloc {
|
||
/// Allocate memory as described by the given `layout`.
|
||
///
|
||
/// Returns a pointer to newly-allocated memory,
|
||
/// or null to indicate allocation failure.
|
||
///
|
||
/// # Safety
|
||
///
|
||
/// This function is unsafe because undefined behavior can result
|
||
/// if the caller does not ensure that `layout` has non-zero size.
|
||
///
|
||
/// (Extension subtraits might provide more specific bounds on
|
||
/// behavior, e.g., guarantee a sentinel address or a null pointer
|
||
/// in response to a zero-size allocation request.)
|
||
///
|
||
/// The allocated block of memory may or may not be initialized.
|
||
///
|
||
/// # Errors
|
||
///
|
||
/// Returning a null pointer indicates that either memory is exhausted
|
||
/// or `layout` does not meet this allocator's size or alignment constraints.
|
||
///
|
||
/// Implementations are encouraged to return null on memory
|
||
/// exhaustion rather than aborting, but this is not
|
||
/// a strict requirement. (Specifically: it is *legal* to
|
||
/// implement this trait atop an underlying native allocation
|
||
/// library that aborts on memory exhaustion.)
|
||
///
|
||
/// Clients wishing to abort computation in response to an
|
||
/// allocation error are encouraged to call the [`handle_alloc_error`] function,
|
||
/// rather than directly invoking `panic!` or similar.
|
||
///
|
||
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
|
||
#[stable(feature = "global_alloc", since = "1.28.0")]
|
||
unsafe fn alloc(&self, layout: Layout) -> *mut u8;
|
||
|
||
/// Deallocate the block of memory at the given `ptr` pointer with the given `layout`.
|
||
///
|
||
/// # Safety
|
||
///
|
||
/// This function is unsafe because undefined behavior can result
|
||
/// if the caller does not ensure all of the following:
|
||
///
|
||
/// * `ptr` must denote a block of memory currently allocated via
|
||
/// this allocator,
|
||
///
|
||
/// * `layout` must be the same layout that was used
|
||
/// to allocate that block of memory,
|
||
#[stable(feature = "global_alloc", since = "1.28.0")]
|
||
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout);
|
||
|
||
/// Behaves like `alloc`, but also ensures that the contents
|
||
/// are set to zero before being returned.
|
||
///
|
||
/// # Safety
|
||
///
|
||
/// This function is unsafe for the same reasons that `alloc` is.
|
||
/// However the allocated block of memory is guaranteed to be initialized.
|
||
///
|
||
/// # Errors
|
||
///
|
||
/// Returning a null pointer indicates that either memory is exhausted
|
||
/// or `layout` does not meet allocator's size or alignment constraints,
|
||
/// just as in `alloc`.
|
||
///
|
||
/// Clients wishing to abort computation in response to an
|
||
/// allocation error are encouraged to call the [`handle_alloc_error`] function,
|
||
/// rather than directly invoking `panic!` or similar.
|
||
///
|
||
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
|
||
#[stable(feature = "global_alloc", since = "1.28.0")]
|
||
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
|
||
let size = layout.size();
|
||
// SAFETY: the safety contract for `alloc` must be upheld by the caller.
|
||
let ptr = unsafe { self.alloc(layout) };
|
||
if !ptr.is_null() {
|
||
// SAFETY: as allocation succeeded, the region from `ptr`
|
||
// of size `size` is guaranteed to be valid for writes.
|
||
unsafe { ptr::write_bytes(ptr, 0, size) };
|
||
}
|
||
ptr
|
||
}
|
||
|
||
/// Shrink or grow a block of memory to the given `new_size`.
|
||
/// The block is described by the given `ptr` pointer and `layout`.
|
||
///
|
||
/// If this returns a non-null pointer, then ownership of the memory block
|
||
/// referenced by `ptr` has been transferred to this allocator.
|
||
/// The memory may or may not have been deallocated,
|
||
/// and should be considered unusable (unless of course it was
|
||
/// transferred back to the caller again via the return value of
|
||
/// this method). The new memory block is allocated with `layout`, but
|
||
/// with the `size` updated to `new_size`.
|
||
///
|
||
/// If this method returns null, then ownership of the memory
|
||
/// block has not been transferred to this allocator, and the
|
||
/// contents of the memory block are unaltered.
|
||
///
|
||
/// # Safety
|
||
///
|
||
/// This function is unsafe because undefined behavior can result
|
||
/// if the caller does not ensure all of the following:
|
||
///
|
||
/// * `ptr` must be currently allocated via this allocator,
|
||
///
|
||
/// * `layout` must be the same layout that was used
|
||
/// to allocate that block of memory,
|
||
///
|
||
/// * `new_size` must be greater than zero.
|
||
///
|
||
/// * `new_size`, when rounded up to the nearest multiple of `layout.align()`,
|
||
/// must not overflow (i.e., the rounded value must be less than `usize::MAX`).
|
||
///
|
||
/// (Extension subtraits might provide more specific bounds on
|
||
/// behavior, e.g., guarantee a sentinel address or a null pointer
|
||
/// in response to a zero-size allocation request.)
|
||
///
|
||
/// # Errors
|
||
///
|
||
/// Returns null if the new layout does not meet the size
|
||
/// and alignment constraints of the allocator, or if reallocation
|
||
/// otherwise fails.
|
||
///
|
||
/// Implementations are encouraged to return null on memory
|
||
/// exhaustion rather than panicking or aborting, but this is not
|
||
/// a strict requirement. (Specifically: it is *legal* to
|
||
/// implement this trait atop an underlying native allocation
|
||
/// library that aborts on memory exhaustion.)
|
||
///
|
||
/// Clients wishing to abort computation in response to a
|
||
/// reallocation error are encouraged to call the [`handle_alloc_error`] function,
|
||
/// rather than directly invoking `panic!` or similar.
|
||
///
|
||
/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
|
||
#[stable(feature = "global_alloc", since = "1.28.0")]
|
||
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
|
||
// SAFETY: the caller must ensure that the `new_size` does not overflow.
|
||
// `layout.align()` comes from a `Layout` and is thus guaranteed to be valid.
|
||
let new_layout = unsafe { Layout::from_size_align_unchecked(new_size, layout.align()) };
|
||
// SAFETY: the caller must ensure that `new_layout` is greater than zero.
|
||
let new_ptr = unsafe { self.alloc(new_layout) };
|
||
if !new_ptr.is_null() {
|
||
// SAFETY: the previously allocated block cannot overlap the newly allocated block.
|
||
// The safety contract for `dealloc` must be upheld by the caller.
|
||
unsafe {
|
||
ptr::copy_nonoverlapping(ptr, new_ptr, cmp::min(layout.size(), new_size));
|
||
self.dealloc(ptr, layout);
|
||
}
|
||
}
|
||
new_ptr
|
||
}
|
||
}
|