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}