ngx/allocator.rs
1//! The allocator module.
2//!
3//! The module provides custom memory allocator support traits and utilities based on the unstable
4//! [feature(allocator_api)].
5//!
6//! Currently implemented as a reexport of parts of the [allocator_api2].
7//!
8//! [feature(allocator_api)]: https://github.com/rust-lang/rust/issues/32838
9
10use ::core::alloc::Layout;
11use ::core::mem;
12use ::core::ptr::{self, NonNull};
13
14pub use allocator_api2::alloc::{AllocError, Allocator};
15
16#[cfg(feature = "alloc")]
17pub use allocator_api2::{alloc::Global, boxed::Box};
18
19/// Explicitly duplicate an object using the specified Allocator.
20pub trait TryCloneIn: Sized {
21 /// Target type, generic over an allocator.
22 type Target<A: Allocator + Clone>;
23
24 /// Attempts to copy the value using `alloc` as an underlying Allocator.
25 fn try_clone_in<A: Allocator + Clone>(&self, alloc: A) -> Result<Self::Target<A>, AllocError>;
26}
27
28/// Moves `value` to the memory backed by `alloc` and returns a pointer.
29///
30/// This should be similar to `Box::into_raw(Box::try_new_in(value, alloc)?)`, except without
31/// `alloc` requirement and intermediate steps.
32///
33/// # Note
34///
35/// The resulting pointer has no owner. The caller is responsible for destroying `T` and releasing
36/// the memory.
37pub fn allocate<T, A>(value: T, alloc: &A) -> Result<NonNull<T>, AllocError>
38where
39 A: Allocator,
40{
41 let layout = Layout::for_value(&value);
42 let ptr: NonNull<T> = alloc.allocate(layout)?.cast();
43
44 // SAFETY: the allocator succeeded and gave us a correctly aligned pointer to an uninitialized
45 // data
46 unsafe { ptr.cast::<mem::MaybeUninit<T>>().as_mut().write(value) };
47
48 Ok(ptr)
49}
50///
51/// Creates a [NonNull] that is dangling, but well-aligned for this [Layout].
52///
53/// See also [::core::alloc::Layout::dangling()]
54#[inline(always)]
55pub(crate) const fn dangling_for_layout(layout: &Layout) -> NonNull<u8> {
56 unsafe {
57 let ptr = ptr::null_mut::<u8>().byte_add(layout.align());
58 NonNull::new_unchecked(ptr)
59 }
60}
61
62#[cfg(feature = "alloc")]
63mod impls {
64 use allocator_api2::boxed::Box;
65
66 use super::*;
67
68 impl<T, OA> TryCloneIn for Box<T, OA>
69 where
70 T: TryCloneIn,
71 OA: Allocator,
72 {
73 type Target<A: Allocator + Clone> = Box<<T as TryCloneIn>::Target<A>, A>;
74
75 fn try_clone_in<A: Allocator + Clone>(
76 &self,
77 alloc: A,
78 ) -> Result<Self::Target<A>, AllocError> {
79 let x = self.as_ref().try_clone_in(alloc.clone())?;
80 Box::try_new_in(x, alloc)
81 }
82 }
83}
84
85/// Allows turning a [`Box<T: Sized, A>`][Box] into a [`Box<U: ?Sized, A>`][Box] where `T` can be
86/// unsizing-coerced into a `U`.
87///
88/// See [allocator_api2::unsize_box] for an explanation why this macro is necessary.
89#[cfg(feature = "alloc")]
90#[doc(inline)]
91pub use crate::__unsize_box as unsize_box;
92
93// We have to reimplement this macro because the original implementation is not reexportable.
94// Macro definitions float to the top of the crate, thus we also mark it as hidden and reexport
95// again from the right namespace.
96#[cfg(feature = "alloc")]
97#[doc(hidden)]
98#[macro_export]
99macro_rules! __unsize_box {
100 ( $boxed:expr $(,)? ) => {{
101 let (ptr, alloc) = $crate::allocator::Box::into_raw_with_allocator($boxed);
102 unsafe { $crate::allocator::Box::from_raw_in(ptr as *mut _, alloc) }
103 }};
104}