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};
14 use crate::ptr::{self, NonNull};
16 /// The `AllocErr` error indicates an allocation failure
17 /// that may be due to resource exhaustion or to
18 /// something wrong when combining the given input arguments with this
20 #[unstable(feature = "allocator_api", issue = "32838")]
21 #[derive(Clone, PartialEq, Eq, Debug)]
24 // (we need this for downstream impl of trait Error)
25 #[unstable(feature = "allocator_api", issue = "32838")]
26 impl fmt::Display for AllocErr {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 f.write_str("memory allocation failed")
32 /// A desired initial state for allocated memory.
33 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
34 #[unstable(feature = "allocator_api", issue = "32838")]
36 /// The contents of the new memory are undefined.
38 /// Reading uninitialized memory is Undefined Behavior; it must be initialized before use.
40 /// The new memory is guaranteed to be zeroed.
44 /// Represents a block of allocated memory returned by an allocator.
45 #[derive(Debug, Copy, Clone)]
46 #[unstable(feature = "allocator_api", issue = "32838")]
47 pub struct MemoryBlock {
53 /// Initialize the memory block like specified by `init`.
55 /// This behaves like calling [`MemoryBlock::initialize_offset(ptr, layout, 0)`][off].
57 /// [off]: MemoryBlock::init_offset
59 /// [*fit*]: trait.AllocRef.html#memory-fitting
61 #[unstable(feature = "allocator_api", issue = "32838")]
62 pub fn init(&mut self, init: AllocInit) {
63 // SAFETY: 0 is always smaller or equal to the size
64 unsafe { self.init_offset(init, 0) }
67 /// Initialize the memory block like specified by `init` at the specified `offset`.
69 /// This is a no-op for [`AllocInit::Uninitialized`] and writes zeroes for [`AllocInit::Zeroed`]
70 /// at `ptr + offset` until `ptr + layout.size()`.
74 /// * `offset` must be smaller than or equal to `size()`
76 /// [*fit*]: trait.AllocRef.html#memory-fitting
78 #[unstable(feature = "allocator_api", issue = "32838")]
79 pub unsafe fn init_offset(&mut self, init: AllocInit, offset: usize) {
80 debug_assert!(offset <= self.size, "`offset` must be smaller than or equal to `size()`");
82 AllocInit::Uninitialized => (),
83 AllocInit::Zeroed => self.ptr.as_ptr().add(offset).write_bytes(0, self.size - offset),
88 /// A placement constraint when growing or shrinking an existing allocation.
89 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
90 #[unstable(feature = "allocator_api", issue = "32838")]
91 pub enum ReallocPlacement {
92 /// The allocator is allowed to move the allocation to a different memory address.
93 // FIXME(wg-allocators#46): Add a section to the module documentation "What is a legal
94 // allocator" and link it at "valid location".
96 /// If the allocation _does_ move, it's the responsibility of the allocator
97 /// to also move the data from the previous location to the new location.
99 /// The address of the new memory must not change.
101 /// If the allocation would have to be moved to a new location to fit, the
102 /// reallocation request will fail.
106 /// An implementation of `AllocRef` can allocate, grow, shrink, and deallocate arbitrary blocks of
107 /// data described via [`Layout`][].
109 /// `AllocRef` is designed to be implemented on ZSTs, references, or smart pointers because having
110 /// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the
111 /// allocated memory.
113 /// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `AllocRef`. If an underlying
114 /// allocator does not support this (like jemalloc) or return a null pointer (such as
115 /// `libc::malloc`), this case must be caught.
117 /// ### Currently allocated memory
119 /// Some of the methods require that a memory block be *currently allocated* via an allocator. This
122 /// * the starting address for that memory block was previously returned by [`alloc`], [`grow`], or
125 /// * the memory block has not been subsequently deallocated, where blocks are either deallocated
126 /// directly by being passed to [`dealloc`] or were changed by being passed to [`grow`] or
127 /// [`shrink`] that returns `Ok`. If `grow` or `shrink` have returned `Err`, the passed pointer
130 /// [`alloc`]: AllocRef::alloc
131 /// [`grow`]: AllocRef::grow
132 /// [`shrink`]: AllocRef::shrink
133 /// [`dealloc`]: AllocRef::dealloc
135 /// ### Memory fitting
137 /// Some of the methods require that a layout *fit* a memory block. What it means for a layout to
138 /// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the
139 /// following conditions must hold:
141 /// * The block must be allocated with the same alignment as [`layout.align()`], and
143 /// * The provided [`layout.size()`] must fall in the range `min ..= max`, where:
144 /// - `min` is the size of the layout most recently used to allocate the block, and
145 /// - `max` is the latest actual size returned from [`alloc`], [`grow`], or [`shrink`].
147 /// [`layout.align()`]: Layout::align
148 /// [`layout.size()`]: Layout::size
152 /// * Memory blocks returned from an allocator must point to valid memory and retain their validity
153 /// until the instance and all of its clones are dropped, and
155 /// * cloning or moving the allocator must not invalidate memory blocks returned from this
156 /// allocator. A cloned allocator must behave like the same allocator.
158 /// * any pointer to a memory block which is [*currently allocated*] may be passed to any other
159 /// method of the allocator.
161 /// [*currently allocated*]: #currently-allocated-memory
162 #[unstable(feature = "allocator_api", issue = "32838")]
163 pub unsafe trait AllocRef {
164 /// On success, returns a memory block meeting the size and alignment guarantees of `layout`.
166 /// The returned block may have a larger size than specified by `layout.size()` and is
167 /// initialized as specified by [`init`], all the way up to the returned size of the block.
169 /// [`init`]: AllocInit
173 /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
174 /// allocator's size or alignment constraints.
176 /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
177 /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
178 /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
180 /// Clients wishing to abort computation in response to an allocation error are encouraged to
181 /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
183 /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
184 fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr>;
186 /// Deallocates the memory denoted by `memory`.
190 /// `memory` must be a memory block returned by this allocator.
191 unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout);
193 /// Attempts to extend the memory block.
195 /// Returns a new memory block containing a pointer and the actual size of the allocated
196 /// block. The pointer is suitable for holding data described by a new layout with `layout`’s
197 /// alignment and a size given by `new_size`. To accomplish this, the allocator may extend the
198 /// allocation referenced by `ptr` to fit the new layout. If the [`placement`] is
199 /// [`InPlace`], the returned pointer is guaranteed to be the same as the passed `ptr`.
201 /// If `ReallocPlacement::MayMove` is used then ownership of the memory block referenced by `ptr`
202 /// is transferred to this allocator. The memory may or may not be freed, and should be
203 /// considered unusable (unless of course it is transferred back to the caller again via the
204 /// return value of this method).
206 /// If this method returns `Err`, then ownership of the memory block has not been transferred to
207 /// this allocator, and the contents of the memory block are unaltered.
209 /// The memory block will contain the following contents after a successful call to `grow`:
210 /// * Bytes `0..layout.size()` are preserved from the original allocation.
211 /// * Bytes `layout.size()..old_size` will either be preserved or initialized according to
212 /// [`init`], depending on the allocator implementation. `old_size` refers to the size of
213 /// the `MemoryBlock` prior to the `grow` call, which may be larger than the size
214 /// that was originally requested when it was allocated.
215 /// * Bytes `old_size..new_size` are initialized according to [`init`]. `new_size` refers to
216 /// the size of the `MemoryBlock` returned by the `grow` call.
218 /// [`InPlace`]: ReallocPlacement::InPlace
219 /// [`placement`]: ReallocPlacement
220 /// [`init`]: AllocInit
224 /// * `ptr` must be [*currently allocated*] via this allocator,
225 /// * `layout` must [*fit*] the `ptr`. (The `new_size` argument need not fit it.)
226 // We can't require that `new_size` is strictly greater than `memory.size` because of ZSTs.
227 // An alternative would be
228 // * `new_size must be strictly greater than `memory.size` or both are zero
229 /// * `new_size` must be greater than or equal to `layout.size()`
230 /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, must not overflow
231 /// (i.e., the rounded value must be less than `usize::MAX`).
233 /// [*currently allocated*]: #currently-allocated-memory
234 /// [*fit*]: #memory-fitting
238 /// Returns `Err` if the new layout does not meet the allocator's size and alignment
239 /// constraints of the allocator, or if growing otherwise fails.
241 /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
242 /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
243 /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
245 /// Clients wishing to abort computation in response to an allocation error are encouraged to
246 /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
248 /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
254 placement: ReallocPlacement,
256 ) -> Result<MemoryBlock, AllocErr> {
258 ReallocPlacement::InPlace => Err(AllocErr),
259 ReallocPlacement::MayMove => {
260 let size = layout.size();
263 "`new_size` must be greater than or equal to `layout.size()`"
266 if new_size == size {
267 return Ok(MemoryBlock { ptr, size });
270 let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
271 let new_memory = self.alloc(new_layout, init)?;
272 ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), size);
273 self.dealloc(ptr, layout);
279 /// Attempts to shrink the memory block.
281 /// Returns a new memory block containing a pointer and the actual size of the allocated
282 /// block. The pointer is suitable for holding data described by a new layout with `layout`’s
283 /// alignment and a size given by `new_size`. To accomplish this, the allocator may shrink the
284 /// allocation referenced by `ptr` to fit the new layout. If the [`placement`] is
285 /// [`InPlace`], the returned pointer is guaranteed to be the same as the passed `ptr`.
287 /// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
288 /// transferred to this allocator. The memory may or may not have been freed, and should be
289 /// considered unusable unless it was transferred back to the caller again via the
290 /// return value of this method.
292 /// If this method returns `Err`, then ownership of the memory block has not been transferred to
293 /// this allocator, and the contents of the memory block are unaltered.
295 /// The behavior of how the allocator tries to shrink the memory is specified by [`placement`].
297 /// [`InPlace`]: ReallocPlacement::InPlace
298 /// [`placement`]: ReallocPlacement
302 /// * `ptr` must be [*currently allocated*] via this allocator,
303 /// * `layout` must [*fit*] the `ptr`. (The `new_size` argument need not fit it.)
304 // We can't require that `new_size` is strictly smaller than `memory.size` because of ZSTs.
305 // An alternative would be
306 // * `new_size must be strictly smaller than `memory.size` or both are zero
307 /// * `new_size` must be smaller than or equal to `layout.size()`
309 /// [*currently allocated*]: #currently-allocated-memory
310 /// [*fit*]: #memory-fitting
314 /// Returns `Err` if the new layout does not meet the allocator's size and alignment
315 /// constraints of the allocator, or if growing otherwise fails.
317 /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
318 /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
319 /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
321 /// Clients wishing to abort computation in response to an allocation error are encouraged to
322 /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
324 /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
330 placement: ReallocPlacement,
331 ) -> Result<MemoryBlock, AllocErr> {
333 ReallocPlacement::InPlace => Err(AllocErr),
334 ReallocPlacement::MayMove => {
335 let size = layout.size();
338 "`new_size` must be smaller than or equal to `layout.size()`"
341 if new_size == size {
342 return Ok(MemoryBlock { ptr, size });
345 let new_layout = Layout::from_size_align_unchecked(new_size, layout.align());
346 let new_memory = self.alloc(new_layout, AllocInit::Uninitialized)?;
347 ptr::copy_nonoverlapping(ptr.as_ptr(), new_memory.ptr.as_ptr(), new_size);
348 self.dealloc(ptr, layout);