memapi2/
lib.rs

1//! A small, `no_std`-friendly memory allocation interface for managing raw
2//! buffers, suitable for use in collections.
3//!
4//! This crate focuses on a minimal API:
5//! - [`Alloc`]: a trait defining basic allocation, zero-allocation, deallocation, and simple
6//!   grow/shrink helpers.
7//! - [`DefaultAlloc`]: a tiny wrapper delegating to the global allocator.
8//! - [`AllocError`]: an error type describing allocation failures.
9//!
10//! Some utilities and marker traits are provided under [`data`]:
11//! - [`PtrProps`](data::type_props::PtrProps)
12//! - [`SizedProps`](data::type_props::SizedProps)
13//! - [`VarSized`](data::type_props::VarSized)
14//! - [`UnsizedCopy`](data::marker::UnsizedCopy)
15//! - [`Thin`](data::marker::Thin)
16
17// TODO: add more tests and benches
18// TODO: test on other platforms/targets
19
20#![allow(unknown_lints)]
21#![warn(
22    clippy::all,
23    clippy::pedantic,
24    clippy::nursery,
25    // TEMPORARY
26    // clippy::undocumented_unsafe_blocks,
27    // clippy::multiple_unsafe_ops_per_block,
28    // clippy::missing_docs_in_private_items,
29)]
30#![allow(clippy::borrow_as_ptr)]
31#![warn(unknown_lints)]
32#![allow(
33    rustdoc::broken_intra_doc_links,
34    // does anyone else hate the Self keyword? that capital letter there looks so ugly idk why
35    clippy::use_self
36)]
37#![deny(missing_docs, unused_unsafe)]
38#![cfg_attr(not(feature = "std"), no_std)]
39#![cfg_attr(feature = "nightly", feature(allocator_api))]
40#![cfg_attr(feature = "metadata", feature(ptr_metadata))]
41#![cfg_attr(feature = "clone_to_uninit", feature(clone_to_uninit))]
42#![cfg_attr(feature = "sized_hierarchy", feature(sized_hierarchy))]
43
44// TODO: add any missing cfg_attr(miri, track_caller) attributes, remove unnecessary ones
45// TODO: a lot of helpers and unstable utils would be good to have in another crate, maybe?
46
47macro_rules! const_if {
48    (
49        $feature:literal,
50        $docs:literal,
51        $(#[$attr:meta])*
52        // this is also pretty poorly done, but it makes type param and optional req work
53        pub const fn $name:ident $(<$generic_ty:ident $(: $req:ident)?>)? ( $($args:tt)* )
54        $(-> $ret:ty)?
55        // also kinda poorly done, but it makes a single where clause work
56        $(where $where_ty:ident : $where_req:ident)?
57        $body:block
58    ) => {
59        // when the feature is enabled, emit a `const fn`
60        #[cfg(feature = $feature)]
61        #[doc = $docs]
62        // feature should only be enabled on compatible versions, so we allow this
63        #[allow(clippy::incompatible_msrv)]
64        $(#[$attr])*
65        pub const fn $name $(<$generic_ty $(: $req)?>)? ($($args)*)
66        $(-> $ret)? $(where $where_ty: $where_req)? $body
67
68        // when the feature is disabled, drop the `const`
69        #[cfg(not(feature = $feature))]
70        #[doc = $docs]
71        $(#[$attr])*
72        #[allow(unknown_lints, clippy::missing_const_for_fn)]
73        pub fn $name $(<$generic_ty $(: $req)?>)? ($($args)*)
74        $(-> $ret)? $(where $where_ty: $where_req)? $body
75    };
76
77    (
78        $feature:literal,
79        $docs:literal,
80        $(#[$attr:meta])*
81        // this is also pretty poorly done, but it makes type param and optional req work
82        const unsafe fn $name:ident $(<$generic_ty:ident $(: ?$req:ident)?>)? ( $($args:tt)* )
83        $(-> $ret:ty)?
84        // also kinda poorly done, but it makes a single where clause work
85        $(where $where_ty:ident : $where_req:ident)?
86        $body:block
87    ) => {
88        // when the feature is enabled, emit a `const fn`
89        #[cfg(feature = $feature)]
90        #[doc = $docs]
91        // feature should only be enabled on compatible versions, so we allow this
92        #[allow(clippy::incompatible_msrv)]
93        $(#[$attr])*
94        const unsafe fn $name $(<$generic_ty $(: ?$req)?>)? ($($args)*)
95        $(-> $ret)? $(where $where_ty: $where_req)? $body
96
97        // when the feature is disabled, drop the `const`
98        #[cfg(not(feature = $feature))]
99        #[doc = $docs]
100        $(#[$attr])*
101        #[allow(unknown_lints, clippy::missing_const_for_fn)]
102        unsafe fn $name $(<$generic_ty $(: ?$req)?>)? ($($args)*)
103        $(-> $ret)? $(where $where_ty: $where_req)? $body
104    };
105
106    // branch for a lifetime instead of type param
107    (
108        $feature:literal,
109        $docs:literal,
110        $(#[$attr:meta])*
111        // this is also pretty poorly done, but it makes type param and optional req work
112        pub const fn $name:ident $(<$lt:lifetime>)? ( $($args:tt)* )
113        $(-> $ret:ty)?
114        $body:block
115    ) => {
116        #[cfg(feature = $feature)]
117        #[doc = $docs]
118        #[allow(clippy::incompatible_msrv)]
119        $(#[$attr])*
120        pub const fn $name $(<$lt>)? ($($args)*)
121        $(-> $ret)? $body
122
123        #[cfg(not(feature = $feature))]
124        #[doc = $docs]
125        $(#[$attr])*
126        pub fn $name $(<$lt>)? ($($args)*) $(-> $ret)? $body
127    };
128    // branch for unsafe functions
129    (
130        $feature:literal,
131        $docs:literal,
132        $(#[$attr:meta])*
133        //                                kinda poorly done, but it makes a type param work, which
134        //                                is all i need.
135        pub const unsafe fn $name:ident $(<$generic_ty:ident $(: $req:ident)?>)? ( $($args:tt)* )
136        $(-> $ret:ty)?
137        $body:block
138    ) => {
139        #[cfg(feature = $feature)]
140        #[doc = $docs]
141        #[allow(clippy::incompatible_msrv)]
142        $(#[$attr])*
143        pub const unsafe fn $name$(<$generic_ty $(: $req)?>)?($($args)*) $(-> $ret)? $body
144
145        #[cfg(not(feature = $feature))]
146        #[doc = $docs]
147        $(#[$attr])*
148        pub unsafe fn $name$(<$generic_ty $(: $req)?>)?($($args)*) $(-> $ret)? $body
149    };
150    // branch for relaxed bound + second bound
151    (
152        $feature:literal,
153        $docs:literal,
154        $(#[$attr:meta])*
155        pub const fn $name:ident <$generic_ty:ident: ?Sized + $req:ident> ($($args:tt)*)
156        $(-> $ret:ty)?
157        $body:block
158    ) => {
159        #[cfg(feature = $feature)]
160        #[doc = $docs]
161        #[allow(clippy::incompatible_msrv)]
162        $(#[$attr])*
163        pub const fn $name <$generic_ty: ?Sized + $req> ($($args)*)
164        $(-> $ret)? $body
165
166        #[cfg(not(feature = $feature))]
167        #[doc = $docs]
168        $(#[$attr])*
169        pub fn $name <$generic_ty: ?Sized + $req> ($($args)*)
170        $(-> $ret)? $body
171    }
172}
173
174/// This macro is theoretically faster than `<fallible>?`.
175macro_rules! tri {
176    (do $($fallible:expr)+) => {
177        match $($fallible)+ {
178            Ok(s) => s,
179            Err(e) => return Err(e),
180        }
181    };
182    // (AllocError::$n:ident($($fallible:expr)+)) => {
183    //     match $($fallible)+ {
184    //         Ok(s) => s,
185    //         Err(e) => return Err(crate::error::AllocError::$n(e)),
186    //     }
187    // };
188}
189
190// #[allow(unused_macros)]
191// macro_rules! assume {
192//     ($e:expr) => {
193//         #[cfg(feature = "assumptions")]
194//         {
195//             let res = $e;
196//
197//             #[cfg(debug_assertions)]
198//             {
199//                 assert!(res, concat!("assertion failed: ", stringify!($e)));
200//             }
201//             crate::assert_unreachable(res);
202//         }
203//     };
204//     (u_pre $e:expr, $msg:literal) => {
205//         #[cfg(feature = "assumptions")]
206//         {
207//             let res = $e;
208//             #[cfg(debug_assertions)]
209//             {
210//                 assert!(
211//                     res,
212//                     concat!("unsafe precondition `", stringify!($e), "` violated: ", $msg)
213//                 );
214//             }
215//             crate::assert_unreachable(res);
216//         }
217//     };
218// }
219//
220// #[cfg(feature = "assumptions")]
221// /// Asserts a boolean value to be true, and the false condition to be unreachable.
222// ///
223// /// # Safety
224// ///
225// /// This is only safe to call if `cond` is `true`. See
226// /// [`unreachable_unchecked`](core::hint::unreachable_unchecked) for more details.
227// #[cfg_attr(not(feature = "dev"), doc(hidden))]
228// pub const unsafe fn assert_unreachable(cond: bool) {
229//     if !cond {
230//         #[allow(clippy::incompatible_msrv)]
231//         core::hint::unreachable_unchecked();
232//     }
233// }
234
235// TODO: split crate into smaller crates (memapi-jemalloc, memapi-mimalloc, etc.)
236//  (removed stuff is stuff which would go in new crate)
237
238extern crate alloc;
239extern crate core;
240
241/// Module for anything related specifically to data.
242///
243/// This includes marker traits, type properties, and miscellaneous data-handling traits.
244pub mod data;
245
246/// Small alternatives to Rust functions that are unstable as of the most recent release.
247pub mod unstable_util;
248
249/// Errors that can occur during allocation.
250pub mod error;
251
252use {
253    core::{
254        alloc::Layout,
255        cmp::Ordering,
256        ptr::{self, NonNull}
257    },
258    error::AllocError,
259    helpers::alloc_then
260};
261
262/// Default allocator, delegating to the global allocator.
263#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
264pub struct DefaultAlloc;
265
266#[allow(unused_macros)]
267macro_rules! default_alloc_impl {
268    ($ty:ty) => {
269        impl crate::Alloc for $ty {
270            #[cfg_attr(miri, track_caller)]
271            #[inline(always)]
272            fn alloc(
273                &self,
274                layout: Layout
275            ) -> Result<core::ptr::NonNull<u8>, crate::error::AllocError> {
276                crate::helpers::null_q_zsl_check(
277                    layout,
278                    // SAFETY: we check the layout is non zero-sized before use.
279                    |layout| unsafe { alloc::alloc::alloc(layout) },
280                    crate::helpers::null_q_dyn
281                )
282            }
283
284            #[cfg_attr(miri, track_caller)]
285            #[inline(always)]
286            fn zalloc(
287                &self,
288                layout: Layout
289            ) -> Result<core::ptr::NonNull<u8>, crate::error::AllocError> {
290                crate::helpers::null_q_zsl_check(
291                    layout,
292                    // SAFETY: we check the layout is non zero-sized before use.
293                    |layout| unsafe { alloc::alloc::alloc_zeroed(layout) },
294                    crate::helpers::null_q_dyn
295                )
296            }
297
298            #[cfg_attr(miri, track_caller)]
299            #[inline(always)]
300            unsafe fn dealloc(&self, ptr: core::ptr::NonNull<u8>, layout: Layout) {
301                if layout.size() != 0 {
302                    alloc::alloc::dealloc(ptr.as_ptr(), layout);
303                }
304            }
305        }
306    };
307}
308
309// SAFETY: DefaultAlloc doesn't unwind, and all layout operations are correct
310unsafe impl alloc::alloc::GlobalAlloc for DefaultAlloc {
311    #[cfg_attr(miri, track_caller)]
312    #[inline]
313    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
314        alloc::alloc::alloc(layout)
315    }
316
317    #[cfg_attr(miri, track_caller)]
318    #[inline]
319    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
320        alloc::alloc::dealloc(ptr, layout);
321    }
322
323    #[cfg_attr(miri, track_caller)]
324    #[inline]
325    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
326        alloc::alloc::alloc_zeroed(layout)
327    }
328
329    #[cfg_attr(miri, track_caller)]
330    #[inline]
331    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
332        alloc::alloc::realloc(ptr, layout, new_size)
333    }
334}
335
336default_alloc_impl!(DefaultAlloc);
337
338// MAYBEDO: split this trait into multiple:
339//  - Alloc
340//  - Dealloc
341//  - Grow: Alloc + Dealloc
342//  - Shrink: Alloc + Dealloc
343//  - Realloc: Grow + Shrink
344//  and a supertrait:
345//  - BasicAlloc: Alloc + Dealloc
346/// A memory allocation interface.
347///
348/// This trait does _not_ require `Self: Allocator` and is `no_std`-compatible.
349pub trait Alloc {
350    /// Attempts to allocate a block of memory fitting the given [`Layout`].
351    ///
352    /// # Errors
353    ///
354    /// - [`AllocError::AllocFailed`] if allocation fails.
355    /// - [`AllocError::ZeroSizedLayout`] if `layout` has a size of zero.
356    fn alloc(&self, layout: Layout) -> Result<NonNull<u8>, AllocError>;
357
358    /// Attempts to allocate a zeroed block of memory fitting the given [`Layout`].
359    ///
360    /// # Errors
361    ///
362    /// - [`AllocError::AllocFailed`] if allocation fails.
363    /// - [`AllocError::ZeroSizedLayout`] if `layout` has a size of zero.
364    #[cfg_attr(miri, track_caller)]
365    #[inline]
366    fn zalloc(&self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
367        // SAFETY: alloc returns at least layout.size() allocated bytes
368        unsafe {
369            alloc_then(self, layout, (), |p, ()| {
370                ptr::write_bytes(p.as_ptr(), 0, layout.size());
371                p
372            })
373        }
374    }
375
376    /// Deallocates a previously allocated block.
377    ///
378    /// This is a noop if `layout.size() == 0`.
379    ///
380    /// # Safety
381    ///
382    /// - `ptr` must point to a block of memory allocated using this allocator.
383    /// - `layout` must describe exactly the same block.
384    ///
385    /// # Panics
386    ///
387    /// Some implementations may choose to panic if `ptr` or `layout` are invalid.
388    unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout);
389
390    /// Grow the given block to a new, larger layout.
391    ///
392    /// Returns the new pointer, possibly reallocated elsewhere.
393    ///
394    /// # Safety
395    ///
396    /// - `ptr` must point to a block of memory allocated using this allocator.
397    /// - `old_layout` must describe exactly the same block.
398    ///
399    /// # Errors
400    /// - [`AllocError::AllocFailed`] if allocation fails.
401    /// - [`AllocError::GrowSmallerNewLayout`] if `new_layout.size() < old_layout.size()`.
402    /// - [`AllocError::ZeroSizedLayout`] if `new_layout` has a size of zero.
403    ///
404    /// On failure, the original memory won't be deallocated or modified.
405    #[cfg_attr(miri, track_caller)]
406    #[inline]
407    unsafe fn grow(
408        &self,
409        ptr: NonNull<u8>,
410        old_layout: Layout,
411        new_layout: Layout
412    ) -> Result<NonNull<u8>, AllocError> {
413        grow(self, ptr, old_layout, new_layout, AllocPattern::Uninitialized)
414    }
415
416    /// Grows the given block to a new, larger layout, zeroing any newly allocated bytes.
417    ///
418    /// Returns the new pointer, possibly reallocated elsewhere.
419    ///
420    /// # Safety
421    ///
422    /// - `ptr` must point to a block of memory allocated using this allocator.
423    /// - `old_layout` must describe exactly the same block.
424    ///
425    /// # Errors
426    /// - [`AllocError::AllocFailed`] if allocation fails.
427    /// - [`AllocError::GrowSmallerNewLayout`] in `new_layout.size() < old_layout.size()`.
428    /// - [`AllocError::ZeroSizedLayout`] if `new_layout` has a size of zero.
429    ///
430    /// On failure, the original memory won't be deallocated or modified.
431    #[cfg_attr(miri, track_caller)]
432    unsafe fn zgrow(
433        &self,
434        ptr: NonNull<u8>,
435        old_layout: Layout,
436        new_layout: Layout
437    ) -> Result<NonNull<u8>, AllocError> {
438        grow(self, ptr, old_layout, new_layout, AllocPattern::Zeroed)
439    }
440
441    /// Shrink the given block to a new, smaller layout.
442    ///
443    /// # Safety
444    ///
445    /// - `ptr` must point to a block of memory allocated using this allocator.
446    /// - `old_layout` must describe exactly the same block.
447    ///
448    /// # Errors
449    ///
450    /// - [`AllocError::AllocFailed`] if allocation fails.
451    /// - [`AllocError::ShrinkLargerNewLayout`] if `new_layout.size() > old_layout.size()`.
452    /// - [`AllocError::ZeroSizedLayout`] if `new_layout` has a size of zero.
453    ///
454    /// On failure, the original memory won't be deallocated or modified.
455    #[cfg_attr(miri, track_caller)]
456    unsafe fn shrink(
457        &self,
458        ptr: NonNull<u8>,
459        old_layout: Layout,
460        new_layout: Layout
461    ) -> Result<NonNull<u8>, AllocError> {
462        shrink(self, ptr, old_layout, new_layout)
463    }
464
465    /// Reallocates a block, growing or shrinking as needed.
466    ///
467    ///
468    /// On grow, preserves existing contents up to `old_layout.size()`, and
469    /// on shrink, truncates to `new_layout.size()`.
470    ///
471    /// # Safety
472    ///
473    /// - `ptr` must point to a block previously allocated with this allocator.
474    /// - `old_layout` must describe exactly that block.
475    ///
476    /// # Errors
477    ///
478    /// - [`AllocError::AllocFailed`] if allocation fails.
479    /// - [`AllocError::ZeroSizedLayout`] if `new_layout` has a size of zero.
480    ///
481    /// On failure, the original memory won't be deallocated or modified.
482    #[cfg_attr(miri, track_caller)]
483    unsafe fn realloc(
484        &self,
485        ptr: NonNull<u8>,
486        old_layout: Layout,
487        new_layout: Layout
488    ) -> Result<NonNull<u8>, AllocError> {
489        ralloc(self, ptr, old_layout, new_layout, AllocPattern::Uninitialized)
490    }
491
492    /// Reallocates a block, growing or shrinking as needed, zeroing any newly
493    /// allocated bytes.
494    ///
495    /// # Safety
496    ///
497    /// - `ptr` must point to a block previously allocated with this allocator.
498    /// - `old_layout` must describe exactly that block.
499    ///
500    /// # Errors
501    ///
502    /// - [`AllocError::AllocFailed`] if allocation fails.
503    /// - [`AllocError::ZeroSizedLayout`] if `new_layout` has a size of zero.
504    ///
505    /// On failure, the original memory won't be deallocated or modified.
506    #[cfg_attr(miri, track_caller)]
507    unsafe fn rezalloc(
508        &self,
509        ptr: NonNull<u8>,
510        old_layout: Layout,
511        new_layout: Layout
512    ) -> Result<NonNull<u8>, AllocError> {
513        ralloc(self, ptr, old_layout, new_layout, AllocPattern::Zeroed)
514    }
515}
516
517#[cfg(feature = "nightly")]
518/// The primary module for when `nightly` is enabled.
519pub(crate) mod nightly {
520    use core::alloc::Layout;
521    // SAFETY: DefaultAlloc's allocated memory isn't deallocated until a deallocation method is
522    //  called. as a ZST allocator, copying/cloning it doesn't change behavior or invalidate
523    //  allocations.
524    unsafe impl alloc::alloc::Allocator for crate::DefaultAlloc {
525        #[cfg_attr(miri, track_caller)]
526        #[inline]
527        fn allocate(
528            &self,
529            layout: Layout
530        ) -> Result<core::ptr::NonNull<[u8]>, alloc::alloc::AllocError> {
531            alloc::alloc::Allocator::allocate(&alloc::alloc::Global, layout)
532        }
533
534        #[cfg_attr(miri, track_caller)]
535        #[inline]
536        fn allocate_zeroed(
537            &self,
538            layout: Layout
539        ) -> Result<core::ptr::NonNull<[u8]>, alloc::alloc::AllocError> {
540            alloc::alloc::Allocator::allocate_zeroed(&alloc::alloc::Global, layout)
541        }
542
543        #[cfg_attr(miri, track_caller)]
544        #[inline]
545        unsafe fn deallocate(&self, ptr: core::ptr::NonNull<u8>, layout: Layout) {
546            alloc::alloc::Allocator::deallocate(&alloc::alloc::Global, ptr.cast(), layout);
547        }
548
549        #[cfg_attr(miri, track_caller)]
550        #[inline]
551        unsafe fn grow(
552            &self,
553            ptr: core::ptr::NonNull<u8>,
554            old_layout: Layout,
555            new_layout: Layout
556        ) -> Result<core::ptr::NonNull<[u8]>, alloc::alloc::AllocError> {
557            alloc::alloc::Allocator::grow(&alloc::alloc::Global, ptr.cast(), old_layout, new_layout)
558        }
559
560        #[cfg_attr(miri, track_caller)]
561        #[inline]
562        unsafe fn grow_zeroed(
563            &self,
564            ptr: core::ptr::NonNull<u8>,
565            old_layout: Layout,
566            new_layout: Layout
567        ) -> Result<core::ptr::NonNull<[u8]>, alloc::alloc::AllocError> {
568            alloc::alloc::Allocator::grow_zeroed(
569                &alloc::alloc::Global,
570                ptr.cast(),
571                old_layout,
572                new_layout
573            )
574        }
575
576        #[cfg_attr(miri, track_caller)]
577        #[inline]
578        unsafe fn shrink(
579            &self,
580            ptr: core::ptr::NonNull<u8>,
581            old_layout: Layout,
582            new_layout: Layout
583        ) -> Result<core::ptr::NonNull<[u8]>, alloc::alloc::AllocError> {
584            alloc::alloc::Allocator::shrink(
585                &alloc::alloc::Global,
586                ptr.cast(),
587                old_layout,
588                new_layout
589            )
590        }
591    }
592
593    default_alloc_impl!(alloc::alloc::Global);
594}
595
596#[allow(clippy::inline_always)]
597impl<A: Alloc + ?Sized> Alloc for &A {
598    #[cfg_attr(miri, track_caller)]
599    #[inline(always)]
600    fn alloc(&self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
601        (**self).alloc(layout)
602    }
603
604    #[cfg_attr(miri, track_caller)]
605    #[inline(always)]
606    fn zalloc(&self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
607        (**self).zalloc(layout)
608    }
609
610    #[cfg_attr(miri, track_caller)]
611    #[inline(always)]
612    unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout) {
613        (**self).dealloc(ptr, layout);
614    }
615}
616
617#[cfg(feature = "std")]
618impl Alloc for std::alloc::System {
619    #[cfg_attr(miri, track_caller)]
620    #[inline]
621    fn alloc(&self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
622        helpers::null_q_zsl_check(
623            layout,
624            // SAFETY: System::alloc is only called after the layout is verified non-zero-sized.
625            |layout| unsafe { alloc::alloc::GlobalAlloc::alloc(self, layout) },
626            helpers::null_q_dyn
627        )
628    }
629
630    #[cfg_attr(miri, track_caller)]
631    #[inline]
632    fn zalloc(&self, layout: Layout) -> Result<NonNull<u8>, AllocError> {
633        helpers::null_q_zsl_check(
634            layout,
635            // SAFETY: System::alloc_zeroed is only called after the layout is verified
636            //  non-zero-sized.
637            |layout| unsafe { alloc::alloc::GlobalAlloc::alloc_zeroed(self, layout) },
638            helpers::null_q_dyn
639        )
640    }
641
642    #[cfg_attr(miri, track_caller)]
643    #[inline]
644    unsafe fn dealloc(&self, ptr: NonNull<u8>, layout: Layout) {
645        if layout.size() != 0 {
646            alloc::alloc::GlobalAlloc::dealloc(self, ptr.as_ptr(), layout);
647        }
648    }
649}
650
651/// Internal helper to grow the allocation at `ptr` by deallocating using `old_layout` and
652/// reallocating using `new_layout`, filling new bytes using `pattern.`
653#[allow(clippy::missing_errors_doc, clippy::missing_safety_doc)]
654#[cfg_attr(not(feature = "dev"), doc(hidden))]
655#[cfg_attr(miri, track_caller)]
656pub unsafe fn grow<A: Alloc + ?Sized>(
657    a: &A,
658    ptr: NonNull<u8>,
659    old_layout: Layout,
660    new_layout: Layout,
661    pattern: AllocPattern
662) -> Result<NonNull<u8>, AllocError> {
663    match old_layout.size().cmp(&new_layout.size()) {
664        Ordering::Less => grow_unchecked(a, ptr, old_layout, new_layout, pattern),
665        Ordering::Equal => {
666            if new_layout.align() == old_layout.align() {
667                Ok(ptr)
668            } else {
669                grow_unchecked(&a, ptr, old_layout, new_layout, pattern)
670            }
671        }
672        Ordering::Greater => Err(AllocError::grow_smaller(old_layout.size(), new_layout.size()))
673    }
674}
675
676/// Internal helper to shrink the allocation at `ptr` by deallocating using `old_layout` and
677/// reallocating using `new_layout`.
678#[allow(clippy::missing_errors_doc, clippy::missing_safety_doc)]
679#[cfg_attr(not(feature = "dev"), doc(hidden))]
680#[cfg_attr(miri, track_caller)]
681pub unsafe fn shrink<A: Alloc + ?Sized>(
682    a: &A,
683    ptr: NonNull<u8>,
684    old_layout: Layout,
685    new_layout: Layout
686) -> Result<NonNull<u8>, AllocError> {
687    match old_layout.size().cmp(&new_layout.size()) {
688        Ordering::Less => Err(AllocError::shrink_larger(old_layout.size(), new_layout.size())),
689        Ordering::Equal => {
690            if new_layout.align() == old_layout.align() {
691                Ok(ptr)
692            } else {
693                shrink_unchecked(&a, ptr, old_layout, new_layout)
694            }
695        }
696        Ordering::Greater => shrink_unchecked(a, ptr, old_layout, new_layout)
697    }
698}
699
700/// Internal helper to grow the allocation at `ptr` by deallocating using `old_layout` and
701/// reallocating using `new_layout`.
702///
703/// # Safety
704///
705/// This function doesn't check for layout validity.
706/// Callers must ensure `new_layout.size()` is greater than `old_layout.size()`.
707#[allow(clippy::needless_pass_by_value)]
708#[cfg_attr(miri, track_caller)]
709unsafe fn grow_unchecked<A: Alloc + ?Sized>(
710    a: &A,
711    ptr: NonNull<u8>,
712    old_layout: Layout,
713    new_layout: Layout,
714    pattern: AllocPattern
715) -> Result<NonNull<u8>, AllocError> {
716    let old_size = old_layout.size();
717    let new_ptr = match pattern {
718        AllocPattern::Uninitialized => tri!(do a.alloc(new_layout)),
719        AllocPattern::Zeroed => tri!(do a.zalloc(new_layout)),
720        AllocPattern::Shrink => core::hint::unreachable_unchecked()
721    };
722
723    ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr(), old_size);
724    if old_size != 0 {
725        a.dealloc(ptr, old_layout);
726    }
727
728    Ok(new_ptr)
729}
730
731/// Internal helper to shrink the allocation at `ptr` by deallocating using `old_layout` and
732/// reallocating using `new_layout`.
733///
734/// # Safety
735///
736/// This function doesn't check for layout validity.
737/// Callers must ensure `new_layout.size()` is greater than `old_layout.size()`.
738#[cfg_attr(miri, track_caller)]
739unsafe fn shrink_unchecked<A: Alloc + ?Sized>(
740    a: &A,
741    ptr: NonNull<u8>,
742    old_layout: Layout,
743    new_layout: Layout
744) -> Result<NonNull<u8>, AllocError> {
745    let new_ptr = tri!(do a.alloc(new_layout));
746
747    ptr::copy_nonoverlapping(ptr.as_ptr(), new_ptr.as_ptr(), new_layout.size());
748    if old_layout.size() != 0 {
749        a.dealloc(ptr, old_layout);
750    }
751
752    Ok(new_ptr)
753}
754
755/// Helper for realloc to reduce repetition.
756#[allow(clippy::missing_errors_doc, clippy::missing_safety_doc)]
757#[cfg_attr(not(feature = "dev"), doc(hidden))]
758#[cfg_attr(miri, track_caller)]
759pub unsafe fn ralloc<A: Alloc + ?Sized>(
760    a: &A,
761    ptr: NonNull<u8>,
762    old_layout: Layout,
763    new_layout: Layout,
764    pat: AllocPattern
765) -> Result<NonNull<u8>, AllocError> {
766    match old_layout.size().cmp(&new_layout.size()) {
767        Ordering::Less => grow_unchecked(&a, ptr, old_layout, new_layout, pat),
768        Ordering::Greater => shrink_unchecked(&a, ptr, old_layout, new_layout),
769        Ordering::Equal => {
770            if new_layout.align() == old_layout.align() {
771                Ok(ptr)
772            } else {
773                grow_unchecked(&a, ptr, old_layout, new_layout, pat)
774            }
775        }
776    }
777}
778
779/// A byte pattern.
780///
781/// This is used to determine or represent the pattern new bytes will be or were filled with.
782#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
783#[cfg_attr(not(feature = "dev"), doc(hidden))]
784#[repr(u8)]
785pub enum AllocPattern {
786    /// Uninitialized bytes.
787    Uninitialized,
788    /// Zeroed bytes.
789    Zeroed,
790    /// No new bytes.
791    Shrink
792}
793
794/// Helpers that tend to be useful in other libraries as well.
795pub mod helpers;