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 {}