aligned_buffer/
alloc.rs

1use crate::cap::Cap;
2use std::{
3	alloc::{self, Layout},
4	ptr::{self, NonNull},
5};
6
7#[non_exhaustive]
8#[derive(Debug, thiserror::Error)]
9#[error("memory allocation failed")]
10pub struct AllocError;
11
12/// Stable version of the Allocator trait.
13///
14/// This trait is a copy of the unstable `std::alloc::Allocator` trait. It is intended to be used as
15/// a stable version of the unstable trait. Once the unstable trait is stabilized, this trait will be
16/// removed and replaced with the standard trait.
17///
18/// An implementation of `Allocator` can allocate, grow, shrink, and deallocate arbitrary blocks of
19/// data described via [`Layout`][].
20///
21/// `Allocator` is designed to be implemented on ZSTs, references, or smart pointers because having
22/// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the
23/// allocated memory.
24///
25/// Unlike [`GlobalAlloc`][std::alloc::GlobalAlloc], zero-sized allocations are allowed in `Allocator`. If an underlying
26/// allocator does not support this (like jemalloc) or return a null pointer (such as
27/// `libc::malloc`), this must be caught by the implementation.
28///
29/// ### Currently allocated memory
30///
31/// Some of the methods require that a memory block be *currently allocated* via an allocator. This
32/// means that:
33///
34/// * the starting address for that memory block was previously returned by [`allocate`], [`grow`], or
35///   [`shrink`], and
36///
37/// * the memory block has not been subsequently deallocated, where blocks are either deallocated
38///   directly by being passed to [`deallocate`] or were changed by being passed to [`grow`] or
39///   [`shrink`] that returns `Ok`. If `grow` or `shrink` have returned `Err`, the passed pointer
40///   remains valid.
41///
42/// [`allocate`]: Allocator::allocate
43/// [`grow`]: Allocator::grow
44/// [`shrink`]: Allocator::shrink
45/// [`deallocate`]: Allocator::deallocate
46///
47/// ### Memory fitting
48///
49/// Some of the methods require that a layout *fit* a memory block. What it means for a layout to
50/// "fit" a memory block means (or equivalently, for a memory block to "fit" a layout) is that the
51/// following conditions must hold:
52///
53/// * The block must be allocated with the same alignment as [`layout.align()`], and
54///
55/// * The provided [`layout.size()`] must fall in the range `min ..= max`, where:
56///   - `min` is the size of the layout most recently used to allocate the block, and
57///   - `max` is the latest actual size returned from [`allocate`], [`grow`], or [`shrink`].
58///
59/// [`layout.align()`]: Layout::align
60/// [`layout.size()`]: Layout::size
61///
62/// # Safety
63///
64/// * Memory blocks returned from an allocator that are [*currently allocated*] must point to
65///   valid memory and retain their validity while they are [*currently allocated*] and at
66///   least one of the instance and all of its clones has not been dropped.
67///
68/// * copying, cloning, or moving the allocator must not invalidate memory blocks returned from this
69///   allocator. A copied or cloned allocator must behave like the same allocator, and
70///
71/// * any pointer to a memory block which is [*currently allocated*] may be passed to any other
72///   method of the allocator.
73///
74/// [*currently allocated*]: #currently-allocated-memory
75pub unsafe trait Allocator {
76	/// Attempts to allocate a block of memory.
77	///
78	/// On success, returns a [`NonNull<[u8]>`][NonNull] meeting the size and alignment guarantees of `layout`.
79	///
80	/// The returned block may have a larger size than specified by `layout.size()`, and may or may
81	/// not have its contents initialized.
82	///
83	/// # Errors
84	///
85	/// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
86	/// allocator's size or alignment constraints.
87	///
88	/// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
89	/// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
90	/// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
91	///
92	/// Clients wishing to abort computation in response to an allocation error are encouraged to
93	/// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
94	///
95	/// [`handle_alloc_error`]: std::alloc::handle_alloc_error
96	fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
97
98	/// Behaves like `allocate`, but also ensures that the returned memory is zero-initialized.
99	///
100	/// # Errors
101	///
102	/// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
103	/// allocator's size or alignment constraints.
104	///
105	/// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
106	/// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
107	/// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
108	///
109	/// Clients wishing to abort computation in response to an allocation error are encouraged to
110	/// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
111	///
112	/// [`handle_alloc_error`]: std::alloc::handle_alloc_error
113	fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
114		let ptr = self.allocate(layout)?;
115		// SAFETY: `alloc` returns a valid memory block
116		unsafe { (ptr.as_ptr() as *mut u8).write_bytes(0, ptr.len()) }
117		Ok(ptr)
118	}
119
120	/// Deallocates the memory referenced by `ptr`.
121	///
122	/// # Safety
123	///
124	/// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, and
125	/// * `layout` must [*fit*] that block of memory.
126	///
127	/// [*currently allocated*]: #currently-allocated-memory
128	/// [*fit*]: #memory-fitting
129	unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
130
131	/// Attempts to extend the memory block.
132	///
133	/// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
134	/// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
135	/// this, the allocator may extend the allocation referenced by `ptr` to fit the new layout.
136	///
137	/// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
138	/// transferred to this allocator. Any access to the old `ptr` is Undefined Behavior, even if the
139	/// allocation was grown in-place. The newly returned pointer is the only valid pointer
140	/// for accessing this memory now.
141	///
142	/// If this method returns `Err`, then ownership of the memory block has not been transferred to
143	/// this allocator, and the contents of the memory block are unaltered.
144	///
145	/// # Safety
146	///
147	/// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
148	/// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
149	/// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
150	///
151	/// Note that `new_layout.align()` need not be the same as `old_layout.align()`.
152	///
153	/// [*currently allocated*]: #currently-allocated-memory
154	/// [*fit*]: #memory-fitting
155	///
156	/// # Errors
157	///
158	/// Returns `Err` if the new layout does not meet the allocator's size and alignment
159	/// constraints of the allocator, or if growing otherwise fails.
160	///
161	/// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
162	/// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
163	/// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
164	///
165	/// Clients wishing to abort computation in response to an allocation error are encouraged to
166	/// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
167	///
168	/// [`handle_alloc_error`]: std::alloc::handle_alloc_error
169	unsafe fn grow(
170		&self,
171		ptr: NonNull<u8>,
172		old_layout: Layout,
173		new_layout: Layout,
174	) -> Result<NonNull<[u8]>, AllocError> {
175		debug_assert!(
176			new_layout.size() >= old_layout.size(),
177			"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
178		);
179
180		let new_ptr = self.allocate(new_layout)?;
181
182		// SAFETY: because `new_layout.size()` must be greater than or equal to
183		// `old_layout.size()`, both the old and new memory allocation are valid for reads and
184		// writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
185		// deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
186		// safe. The safety contract for `dealloc` must be upheld by the caller.
187		unsafe {
188			ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr() as *mut u8, old_layout.size());
189			self.deallocate(ptr, old_layout);
190		}
191
192		Ok(new_ptr)
193	}
194
195	/// Behaves like `grow`, but also ensures that the new contents are set to zero before being
196	/// returned.
197	///
198	/// The memory block will contain the following contents after a successful call to
199	/// `grow_zeroed`:
200	///   * Bytes `0..old_layout.size()` are preserved from the original allocation.
201	///   * Bytes `old_layout.size()..old_size` will either be preserved or zeroed, depending on
202	///     the allocator implementation. `old_size` refers to the size of the memory block prior
203	///     to the `grow_zeroed` call, which may be larger than the size that was originally
204	///     requested when it was allocated.
205	///   * Bytes `old_size..new_size` are zeroed. `new_size` refers to the size of the memory
206	///     block returned by the `grow_zeroed` call.
207	///
208	/// # Safety
209	///
210	/// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
211	/// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
212	/// * `new_layout.size()` must be greater than or equal to `old_layout.size()`.
213	///
214	/// Note that `new_layout.align()` need not be the same as `old_layout.align()`.
215	///
216	/// [*currently allocated*]: #currently-allocated-memory
217	/// [*fit*]: #memory-fitting
218	///
219	/// # Errors
220	///
221	/// Returns `Err` if the new layout does not meet the allocator's size and alignment
222	/// constraints of the allocator, or if growing otherwise fails.
223	///
224	/// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
225	/// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
226	/// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
227	///
228	/// Clients wishing to abort computation in response to an allocation error are encouraged to
229	/// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
230	///
231	/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
232	unsafe fn grow_zeroed(
233		&self,
234		ptr: NonNull<u8>,
235		old_layout: Layout,
236		new_layout: Layout,
237	) -> Result<NonNull<[u8]>, AllocError> {
238		debug_assert!(
239			new_layout.size() >= old_layout.size(),
240			"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
241		);
242
243		let new_ptr = self.allocate_zeroed(new_layout)?;
244
245		// SAFETY: because `new_layout.size()` must be greater than or equal to
246		// `old_layout.size()`, both the old and new memory allocation are valid for reads and
247		// writes for `old_layout.size()` bytes. Also, because the old allocation wasn't yet
248		// deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
249		// safe. The safety contract for `dealloc` must be upheld by the caller.
250		unsafe {
251			ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr() as *mut u8, old_layout.size());
252			self.deallocate(ptr, old_layout);
253		}
254
255		Ok(new_ptr)
256	}
257
258	/// Attempts to shrink the memory block.
259	///
260	/// Returns a new [`NonNull<[u8]>`][NonNull] containing a pointer and the actual size of the allocated
261	/// memory. The pointer is suitable for holding data described by `new_layout`. To accomplish
262	/// this, the allocator may shrink the allocation referenced by `ptr` to fit the new layout.
263	///
264	/// If this returns `Ok`, then ownership of the memory block referenced by `ptr` has been
265	/// transferred to this allocator. Any access to the old `ptr` is Undefined Behavior, even if the
266	/// allocation was shrunk in-place. The newly returned pointer is the only valid pointer
267	/// for accessing this memory now.
268	///
269	/// If this method returns `Err`, then ownership of the memory block has not been transferred to
270	/// this allocator, and the contents of the memory block are unaltered.
271	///
272	/// # Safety
273	///
274	/// * `ptr` must denote a block of memory [*currently allocated*] via this allocator.
275	/// * `old_layout` must [*fit*] that block of memory (The `new_layout` argument need not fit it.).
276	/// * `new_layout.size()` must be smaller than or equal to `old_layout.size()`.
277	///
278	/// Note that `new_layout.align()` need not be the same as `old_layout.align()`.
279	///
280	/// [*currently allocated*]: #currently-allocated-memory
281	/// [*fit*]: #memory-fitting
282	///
283	/// # Errors
284	///
285	/// Returns `Err` if the new layout does not meet the allocator's size and alignment
286	/// constraints of the allocator, or if shrinking otherwise fails.
287	///
288	/// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
289	/// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
290	/// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
291	///
292	/// Clients wishing to abort computation in response to an allocation error are encouraged to
293	/// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
294	///
295	/// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
296	unsafe fn shrink(
297		&self,
298		ptr: NonNull<u8>,
299		old_layout: Layout,
300		new_layout: Layout,
301	) -> Result<NonNull<[u8]>, AllocError> {
302		debug_assert!(
303			new_layout.size() <= old_layout.size(),
304			"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
305		);
306
307		let new_ptr = self.allocate(new_layout)?;
308
309		// SAFETY: because `new_layout.size()` must be lower than or equal to
310		// `old_layout.size()`, both the old and new memory allocation are valid for reads and
311		// writes for `new_layout.size()` bytes. Also, because the old allocation wasn't yet
312		// deallocated, it cannot overlap `new_ptr`. Thus, the call to `copy_nonoverlapping` is
313		// safe. The safety contract for `dealloc` must be upheld by the caller.
314		unsafe {
315			ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr() as *mut u8, new_layout.size());
316			self.deallocate(ptr, old_layout);
317		}
318
319		Ok(new_ptr)
320	}
321
322	/// Creates a "by reference" adapter for this instance of `Allocator`.
323	///
324	/// The returned adapter also implements `Allocator` and will simply borrow this.
325	#[inline(always)]
326	fn by_ref(&self) -> &Self
327	where
328		Self: Sized,
329	{
330		self
331	}
332}
333
334unsafe impl<A> Allocator for &A
335where
336	A: Allocator + ?Sized,
337{
338	#[inline]
339	fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
340		(**self).allocate(layout)
341	}
342
343	#[inline]
344	fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
345		(**self).allocate_zeroed(layout)
346	}
347
348	#[inline]
349	unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
350		// SAFETY: the safety contract must be upheld by the caller
351		unsafe { (**self).deallocate(ptr, layout) }
352	}
353
354	#[inline]
355	unsafe fn grow(
356		&self,
357		ptr: NonNull<u8>,
358		old_layout: Layout,
359		new_layout: Layout,
360	) -> Result<NonNull<[u8]>, AllocError> {
361		// SAFETY: the safety contract must be upheld by the caller
362		unsafe { (**self).grow(ptr, old_layout, new_layout) }
363	}
364
365	#[inline]
366	unsafe fn grow_zeroed(
367		&self,
368		ptr: NonNull<u8>,
369		old_layout: Layout,
370		new_layout: Layout,
371	) -> Result<NonNull<[u8]>, AllocError> {
372		// SAFETY: the safety contract must be upheld by the caller
373		unsafe { (**self).grow_zeroed(ptr, old_layout, new_layout) }
374	}
375
376	#[inline]
377	unsafe fn shrink(
378		&self,
379		ptr: NonNull<u8>,
380		old_layout: Layout,
381		new_layout: Layout,
382	) -> Result<NonNull<[u8]>, AllocError> {
383		// SAFETY: the safety contract must be upheld by the caller
384		unsafe { (**self).shrink(ptr, old_layout, new_layout) }
385	}
386}
387
388/// A trait for types that can allocate aligned buffers.
389pub trait BufferAllocator<const ALIGNMENT: usize>: Allocator {
390	/// Deallocates the buffer.
391	///
392	/// # Safety
393	///
394	/// * `ptr` must denote a block of memory [*currently allocated*] via this allocator, and
395	/// * `layout` must [*fit*] that block of memory.
396	///
397	/// [*currently allocated*]: Allocator#currently-allocated-memory
398	/// [*fit*]: Allocator#memory-fitting
399	unsafe fn deallocate_buffer(&self, raw: RawBuffer<ALIGNMENT>) {
400		let (ptr, layout) = raw.alloc_info();
401		self.deallocate(ptr, layout);
402	}
403}
404
405/// A raw buffer.
406#[derive(Debug, Clone, Copy)]
407pub struct RawBuffer<const ALIGNMENT: usize> {
408	pub(crate) buf: NonNull<u8>,
409	pub(crate) cap: Cap,
410}
411
412impl<const ALIGNMENT: usize> RawBuffer<ALIGNMENT> {
413	/// Creates a new raw buffer.
414	///
415	/// # Safety
416	/// The caller must ensure that the buffer is properly aligned and has the correct capacity.
417	pub(crate) unsafe fn new(buf: NonNull<u8>, capacity: Cap) -> Self {
418		Self { buf, cap: capacity }
419	}
420
421	/// Returns a pointer to the start of the buffer.
422	#[inline]
423	pub fn buf_ptr(&self) -> NonNull<u8> {
424		self.buf
425	}
426
427	/// Returns the capacity of the buffer.
428	#[inline]
429	pub fn capacity(&self) -> Cap {
430		self.cap
431	}
432
433	/// Returns the allocation pointer and layout.
434	#[inline]
435	pub fn alloc_info(&self) -> (NonNull<u8>, Layout) {
436		let layout = crate::raw::RawAlignedBuffer::<ALIGNMENT>::layout(self.cap.0.value())
437			.expect("Invalid layout");
438
439		let header = unsafe {
440			self
441				.buf
442				.as_ptr()
443				.sub(crate::raw::RawAlignedBuffer::<ALIGNMENT>::BUFFER_OFFSET)
444		};
445
446		debug_assert_eq!(self.cap.0.value(), unsafe {
447			(*(header as *const crate::raw::Header)).alloc_buffer_size
448		});
449
450		(unsafe { NonNull::new_unchecked(header) }, layout)
451	}
452}
453
454/// Stable version of the Global allocator.
455///
456/// This is a copy of the unstable `std::alloc::Global` allocator. It is intended to be used as a
457/// stable version of the unstable allocator. Once the unstable allocator is stabilized, this
458/// allocator will be removed and replaced with the standard allocator.
459///
460/// The global memory allocator.
461///
462/// This type implements the [`Allocator`] trait by forwarding calls
463/// to the allocator registered with the `#[global_allocator]` attribute
464/// if there is one, or the `std` crate’s default.
465#[derive(Debug, Clone, Copy, Default)]
466pub struct Global;
467
468impl Global {
469	#[inline]
470	fn alloc_impl(&self, layout: Layout, zeroed: bool) -> Result<NonNull<[u8]>, AllocError> {
471		match layout.size() {
472			0 => Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0)),
473			// SAFETY: `layout` is non-zero in size,
474			size => unsafe {
475				let raw_ptr = if zeroed {
476					alloc::alloc_zeroed(layout)
477				} else {
478					alloc::alloc(layout)
479				};
480				let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
481				Ok(NonNull::slice_from_raw_parts(ptr, size))
482			},
483		}
484	}
485
486	// SAFETY: Same as `Allocator::grow`
487	#[inline]
488	unsafe fn grow_impl(
489		&self,
490		ptr: NonNull<u8>,
491		old_layout: Layout,
492		new_layout: Layout,
493		zeroed: bool,
494	) -> Result<NonNull<[u8]>, AllocError> {
495		debug_assert!(
496			new_layout.size() >= old_layout.size(),
497			"`new_layout.size()` must be greater than or equal to `old_layout.size()`"
498		);
499
500		match old_layout.size() {
501			0 => self.alloc_impl(new_layout, zeroed),
502
503			// SAFETY: `new_size` is non-zero as `old_size` is greater than or equal to `new_size`
504			// as required by safety conditions. Other conditions must be upheld by the caller
505			old_size if old_layout.align() == new_layout.align() => unsafe {
506				let new_size = new_layout.size();
507
508				let raw_ptr = alloc::realloc(ptr.as_ptr(), old_layout, new_size);
509				let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
510				if zeroed {
511					raw_ptr.add(old_size).write_bytes(0, new_size - old_size);
512				}
513				Ok(NonNull::slice_from_raw_parts(ptr, new_size))
514			},
515
516			// SAFETY: because `new_layout.size()` must be greater than or equal to `old_size`,
517			// both the old and new memory allocation are valid for reads and writes for `old_size`
518			// bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
519			// `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
520			// for `dealloc` must be upheld by the caller.
521			old_size => unsafe {
522				let new_ptr = self.alloc_impl(new_layout, zeroed)?;
523				ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr() as *mut u8, old_size);
524				self.deallocate(ptr, old_layout);
525				Ok(new_ptr)
526			},
527		}
528	}
529}
530
531unsafe impl Allocator for Global {
532	#[inline]
533	fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
534		self.alloc_impl(layout, false)
535	}
536
537	#[inline]
538	fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
539		self.alloc_impl(layout, true)
540	}
541
542	#[inline]
543	unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
544		if layout.size() != 0 {
545			// SAFETY: `layout` is non-zero in size,
546			// other conditions must be upheld by the caller
547			unsafe { alloc::dealloc(ptr.as_ptr(), layout) }
548		}
549	}
550
551	#[inline]
552	unsafe fn grow(
553		&self,
554		ptr: NonNull<u8>,
555		old_layout: Layout,
556		new_layout: Layout,
557	) -> Result<NonNull<[u8]>, AllocError> {
558		// SAFETY: all conditions must be upheld by the caller
559		unsafe { self.grow_impl(ptr, old_layout, new_layout, false) }
560	}
561
562	#[inline]
563	unsafe fn grow_zeroed(
564		&self,
565		ptr: NonNull<u8>,
566		old_layout: Layout,
567		new_layout: Layout,
568	) -> Result<NonNull<[u8]>, AllocError> {
569		// SAFETY: all conditions must be upheld by the caller
570		unsafe { self.grow_impl(ptr, old_layout, new_layout, true) }
571	}
572
573	#[inline]
574	unsafe fn shrink(
575		&self,
576		ptr: NonNull<u8>,
577		old_layout: Layout,
578		new_layout: Layout,
579	) -> Result<NonNull<[u8]>, AllocError> {
580		debug_assert!(
581			new_layout.size() <= old_layout.size(),
582			"`new_layout.size()` must be smaller than or equal to `old_layout.size()`"
583		);
584
585		match new_layout.size() {
586			// SAFETY: conditions must be upheld by the caller
587			0 => unsafe {
588				self.deallocate(ptr, old_layout);
589				Ok(NonNull::slice_from_raw_parts(NonNull::dangling(), 0))
590			},
591
592			// SAFETY: `new_size` is non-zero. Other conditions must be upheld by the caller
593			new_size if old_layout.align() == new_layout.align() => unsafe {
594				// `realloc` probably checks for `new_size <= old_layout.size()` or something similar.
595
596				let raw_ptr = alloc::realloc(ptr.as_ptr(), old_layout, new_size);
597				let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
598				Ok(NonNull::slice_from_raw_parts(ptr, new_size))
599			},
600
601			// SAFETY: because `new_size` must be smaller than or equal to `old_layout.size()`,
602			// both the old and new memory allocation are valid for reads and writes for `new_size`
603			// bytes. Also, because the old allocation wasn't yet deallocated, it cannot overlap
604			// `new_ptr`. Thus, the call to `copy_nonoverlapping` is safe. The safety contract
605			// for `dealloc` must be upheld by the caller.
606			new_size => unsafe {
607				let new_ptr = self.allocate(new_layout)?;
608				ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr() as *mut u8, new_size);
609				self.deallocate(ptr, old_layout);
610				Ok(new_ptr)
611			},
612		}
613	}
614}
615
616impl<const ALIGNMENT: usize> BufferAllocator<ALIGNMENT> for Global {}