1 //! Memory allocation APIs
3 #![stable(feature = "alloc_module", since = "1.28.0")]
8 #[stable(feature = "global_alloc", since = "1.28.0")]
9 pub use self::global::GlobalAlloc;
10 #[stable(feature = "alloc_layout", since = "1.28.0")]
11 pub use self::layout::{Layout, LayoutErr};
15 use crate::ptr::{self, NonNull, Unique};
17 /// The `AllocErr` error indicates an allocation failure
18 /// that may be due to resource exhaustion or to
19 /// something wrong when combining the given input arguments with this
21 #[unstable(feature = "allocator_api", issue = "32838")]
22 #[derive(Clone, PartialEq, Eq, Debug)]
25 // (we need this for downstream impl of trait Error)
26 #[unstable(feature = "allocator_api", issue = "32838")]
27 impl fmt::Display for AllocErr {
28 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29 f.write_str("memory allocation failed")
33 /// A desired initial state for allocated memory.
34 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
35 #[unstable(feature = "allocator_api", issue = "32838")]
37 /// The contents of the new memory are undefined.
39 /// Reading uninitialized memory is Undefined Behavior; it must be initialized before use.
41 /// The new memory is guaranteed to be zeroed.
45 /// Represents a block of allocated memory returned by an allocator.
47 #[unstable(feature = "allocator_api", issue = "32838")]
48 #[must_use = "`MemoryBlock` should be passed to `AllocRef::dealloc`"]
49 pub struct MemoryBlock {
55 /// Creates a new `MemoryBlock`.
59 /// * The block must be allocated with the same alignment as [`layout.align()`], and
60 /// * The provided [`layout.size()`] must fall in the range `min ..= max`, where:
61 /// - `min` is the size requested size when allocating the block, and
62 /// - `max` is the size of the memory block.
64 #[unstable(feature = "allocator_api", issue = "32838")]
65 pub const unsafe fn new(ptr: NonNull<u8>, layout: Layout) -> Self {
66 Self { ptr: Unique::new_unchecked(ptr.as_ptr()), layout }
69 /// Acquires the underlying `NonNull<u8>` pointer.
71 #[unstable(feature = "allocator_api", issue = "32838")]
72 pub const fn ptr(&self) -> NonNull<u8> {
73 // SAFETY: Unique<T> is always non-null
74 unsafe { NonNull::new_unchecked(self.ptr.as_ptr()) }
77 /// Returns the layout describing the memory block.
79 #[unstable(feature = "allocator_api", issue = "32838")]
80 pub const fn layout(&self) -> Layout {
84 /// Returns the size of the memory block.
86 #[unstable(feature = "allocator_api", issue = "32838")]
87 pub const fn size(&self) -> usize {
91 /// Returns the minimum alignment of the memory block.
93 #[unstable(feature = "allocator_api", issue = "32838")]
94 pub const fn align(&self) -> usize {
98 /// Initialize the memory block like specified by `init`.
100 /// This behaves like calling [`MemoryBlock::initialize_offset(ptr, layout, 0)`][off].
102 /// [off]: MemoryBlock::init_offset
104 /// [*fit*]: trait.AllocRef.html#memory-fitting
106 #[unstable(feature = "allocator_api", issue = "32838")]
107 pub fn init(&mut self, init: AllocInit) {
108 // SAFETY: 0 is always smaller or equal to the size
109 unsafe { self.init_offset(init, 0) }
112 /// Initialize the memory block like specified by `init` at the specified `offset`.
114 /// This is a no-op for [`AllocInit::Uninitialized`] and writes zeroes for [`AllocInit::Zeroed`]
115 /// at `ptr + offset` until `ptr + layout.size()`.
119 /// * `offset` must be smaller than or equal to `size()`
121 /// [*fit*]: trait.AllocRef.html#memory-fitting
123 #[unstable(feature = "allocator_api", issue = "32838")]
124 pub unsafe fn init_offset(&mut self, init: AllocInit, offset: usize) {
125 debug_assert!(offset <= self.size(), "`offset` must be smaller than or equal to `size()`");
127 AllocInit::Uninitialized => (),
128 AllocInit::Zeroed => {
129 self.ptr().as_ptr().add(offset).write_bytes(0, self.size() - offset)
135 /// A placement constraint when growing or shrinking an existing allocation.
136 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
137 #[unstable(feature = "allocator_api", issue = "32838")]
138 pub enum ReallocPlacement {
139 /// The allocator is allowed to move the allocation to a different memory address.
140 // FIXME(wg-allocators#46): Add a section to the module documentation "What is a legal
141 // allocator" and link it at "valid location".
143 /// If the allocation _does_ move, it's the responsibility of the allocator
144 /// to also move the data from the previous location to the new location.
146 /// The address of the new memory must not change.
148 /// If the allocation would have to be moved to a new location to fit, the
149 /// reallocation request will fail.
153 /// An implementation of `AllocRef` can allocate, grow, shrink, and deallocate arbitrary blocks of
154 /// data described via [`Layout`][].
156 /// `AllocRef` is designed to be implemented on ZSTs, references, or smart pointers because having
157 /// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the
158 /// allocated memory.
160 /// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `AllocRef`. If an underlying
161 /// allocator does not support this (like jemalloc) or return a null pointer (such as
162 /// `libc::malloc`), this case must be caught.
166 /// * Memory blocks returned from an allocator must point to valid memory and retain their validity
167 /// until the instance and all of its clones are dropped, and
169 /// * cloning or moving the allocator must not invalidate memory blocks returned from this
170 /// allocator. A cloned allocator must behave like the same allocator.
172 /// [*currently allocated*]: #currently-allocated-memory
173 #[unstable(feature = "allocator_api", issue = "32838")]
174 pub unsafe trait AllocRef {
175 /// On success, returns a memory block meeting the size and alignment guarantees of `layout`.
177 /// The returned block may have a larger size than specified by `layout.size()` and is
178 /// initialized as specified by [`init`], all the way up to the returned size of the block.
180 /// [`init`]: AllocInit
184 /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
185 /// allocator's size or alignment constraints.
187 /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
188 /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
189 /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
191 /// Clients wishing to abort computation in response to an allocation error are encouraged to
192 /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
194 /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
195 fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr>;
197 /// Deallocates the memory denoted by `memory`.
201 /// `memory` must be a memory block returned by this allocator.
202 unsafe fn dealloc(&mut self, memory: MemoryBlock);
204 /// Attempts to extend the memory block.
206 /// The behavior of how the allocator tries to grow the memory is specified by [`placement`].
207 /// The first `memory.size()` bytes are preserved or copied as appropriate from `ptr`, and the
208 /// remaining bytes up to the new `memory.size()` are initialized according to [`init`].
210 /// [`placement`]: ReallocPlacement
211 /// [`init`]: AllocInit
215 /// * `memory` must be a memory block returned by this allocator.
216 // We can't require that `new_size` is strictly greater than `memory.size()` because of ZSTs.
217 // An alternative would be
218 // * `new_size must be strictly greater than `memory.size()` or both are zero
219 /// * `new_size` must be greater than or equal to `memory.size()`
220 /// * `new_size`, when rounded up to the nearest multiple of `memory.align()`, must not overflow
221 /// (i.e., the rounded value must be less than `usize::MAX`).
223 /// [*currently allocated*]: #currently-allocated-memory
224 /// [*fit*]: #memory-fitting
228 /// Returns `Err` if the new layout does not meet the allocator's size and alignment
229 /// constraints of the allocator, or if growing otherwise fails.
231 /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
232 /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
233 /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
235 /// Clients wishing to abort computation in response to an allocation error are encouraged to
236 /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
238 /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
241 memory: &mut MemoryBlock,
243 placement: ReallocPlacement,
245 ) -> Result<(), AllocErr> {
247 ReallocPlacement::InPlace => Err(AllocErr),
248 ReallocPlacement::MayMove => {
249 let old_size = memory.size();
251 new_size >= old_size,
252 "`new_size` must be greater than or equal to `memory.size()`"
255 if new_size == old_size {
259 let new_layout = Layout::from_size_align_unchecked(new_size, memory.align());
260 let new_memory = self.alloc(new_layout, init)?;
261 ptr::copy_nonoverlapping(
262 memory.ptr().as_ptr(),
263 new_memory.ptr().as_ptr(),
266 self.dealloc(mem::replace(memory, new_memory));
272 /// Attempts to shrink the memory block.
274 /// The behavior of how the allocator tries to shrink the memory is specified by [`placement`].
276 /// [`placement`]: ReallocPlacement
280 /// * `memory` must be a memory block returned by this allocator.
281 // We can't require that `new_size` is strictly smaller than `memory.size()` because of ZSTs.
282 // An alternative would be
283 // * `new_size must be strictly smaller than `memory.size()` or both are zero
284 /// * `new_size` must be smaller than or equal to `memory.size()`
286 /// [*currently allocated*]: #currently-allocated-memory
287 /// [*fit*]: #memory-fitting
291 /// Returns `Err` if the new layout does not meet the allocator's size and alignment
292 /// constraints of the allocator, or if growing otherwise fails.
294 /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
295 /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
296 /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
298 /// Clients wishing to abort computation in response to an allocation error are encouraged to
299 /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
301 /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
304 memory: &mut MemoryBlock,
306 placement: ReallocPlacement,
307 ) -> Result<(), AllocErr> {
309 ReallocPlacement::InPlace => Err(AllocErr),
310 ReallocPlacement::MayMove => {
311 let old_size = memory.size();
313 new_size <= old_size,
314 "`new_size` must be smaller than or equal to `layout.size()`"
317 if new_size == old_size {
321 let new_layout = Layout::from_size_align_unchecked(new_size, memory.align());
322 let new_memory = self.alloc(new_layout, AllocInit::Uninitialized)?;
323 ptr::copy_nonoverlapping(
324 memory.ptr().as_ptr(),
325 new_memory.ptr().as_ptr(),
328 self.dealloc(mem::replace(memory, new_memory));