untyped_box/
std_conversions.rs

1use core::{alloc::Layout, mem::MaybeUninit, ptr::NonNull};
2
3use alloc::{boxed::Box, vec::Vec};
4
5use crate::{alloc_shim::Allocator, Allocation};
6
7/// Error when converting an [Allocation] to a [Box].
8#[derive(Debug, Clone)]
9#[non_exhaustive]
10pub enum BoxConversionError {
11    /// Indicates that the layout of the requested type does not match the layout of the allocation.
12    LayoutMismatch {
13        /// expected layout for the requested type
14        expected: Layout,
15        /// layout of the allocation
16        allocated: Layout,
17    },
18}
19
20impl BoxConversionError {
21    fn layout_mismatch(expected: Layout, allocated: Layout) -> Self {
22        Self::LayoutMismatch {
23            expected,
24            allocated,
25        }
26    }
27}
28
29/// Error when converting an [Allocation] to a [Vec].
30#[derive(Debug, Clone)]
31#[non_exhaustive]
32pub enum VecConversionError {
33    /// Indicates an alignment mismatch of the requested element type and the allocation.
34    AlignMismatch {
35        /// expected alignment for the requested type
36        expected: usize,
37        /// actual alignment of the allocation
38        allocated: usize,
39    },
40    /// Indicates that there is some slack capacity when trying to fit a whole multiple of
41    /// elements into the allocated memory to determine the vec's capacity.
42    SlackCapacity {
43        /// size of the requested element
44        element_size: usize,
45        /// allocated capacity in bytes
46        allocated: usize,
47    },
48    /// Tried to convert to a Vec of ZSTs. This is currently not supported due to ambiguity
49    /// of the requested capacity. Even if the allocation is zero-sized, we can fit an
50    /// arbitrary amount of elements as capacity.
51    ///
52    /// If you need this conversion to succeed, open an issue.
53    ZeroSizedElements,
54}
55
56impl VecConversionError {
57    fn align_mismatch(expected: usize, allocated: usize) -> Self {
58        Self::AlignMismatch {
59            expected,
60            allocated,
61        }
62    }
63    fn slack_capacity(element_size: usize, allocated: usize) -> Self {
64        Self::SlackCapacity {
65            element_size,
66            allocated,
67        }
68    }
69    fn zero_sized_elements() -> Self {
70        Self::ZeroSizedElements
71    }
72}
73
74// we can NOT write
75// impl<T, A: Allocator> TryFrom<crate::Allocation<A>> for Box<MaybeUninit<T>, A> {}
76// since   ^^^^^^^^^^^^ this is uncovered generic argument               here -^
77// Hence, we only support the conversion into the global allocator via trait.
78// THIS IS STUPID!
79impl<T> TryFrom<crate::Allocation> for Box<MaybeUninit<T>> {
80    type Error = BoxConversionError;
81    fn try_from(alloc: crate::Allocation) -> Result<Self, Self::Error> {
82        alloc.try_into_box::<T>()
83    }
84}
85
86fn check_box_layout<A: Allocator, T>(allocation: &Allocation<A>) -> Result<(), BoxConversionError> {
87    let expected = Layout::new::<T>();
88    let actual = allocation.layout();
89    if expected != actual {
90        return Err(BoxConversionError::layout_mismatch(expected, actual));
91    }
92    Ok(())
93}
94// TODO: conversion for unsized box/pointer metadata
95// TODO: conversion to ThinBox?
96
97fn check_vec_layout<A: Allocator, T>(
98    allocation: &Allocation<A>,
99) -> Result<usize, VecConversionError> {
100    let expected = Layout::new::<T>();
101    let actual = allocation.layout();
102    let element_align = expected.align();
103    let alloc_align = actual.align();
104    if element_align != alloc_align {
105        return Err(VecConversionError::align_mismatch(
106            element_align,
107            alloc_align,
108        ));
109    }
110    let element_size = expected.size();
111    let byte_capacity = actual.size();
112    if (element_size == 0 && byte_capacity != 0) || byte_capacity % element_size != 0 {
113        return Err(VecConversionError::slack_capacity(
114            element_size,
115            byte_capacity,
116        ));
117    }
118    if element_size == 0 {
119        // Can not determine a capacity.
120        // We can not make up ZSTs on the spot, so a capacity of 0 makes sense.
121        // TODO: let the user provide a capacity hint?
122        return Err(VecConversionError::zero_sized_elements());
123    }
124
125    let element_capacity = byte_capacity / element_size;
126    debug_assert!(byte_capacity == element_size * element_capacity);
127    Ok(element_capacity)
128}
129
130impl<T> TryFrom<crate::Allocation> for Vec<T> {
131    type Error = VecConversionError;
132
133    fn try_from(value: crate::Allocation) -> Result<Self, Self::Error> {
134        value.try_into_vec()
135    }
136}
137
138#[cfg(feature = "nightly-std-conversions")]
139mod alloc_allocator_api {
140    macro_rules! box_to_parts {
141        ($value:ident) => {
142            Box::into_raw_with_allocator($value)
143        };
144    }
145    macro_rules! vec_to_parts {
146        ($vec:ident) => {
147            Vec::into_raw_parts_with_alloc($vec)
148        };
149    }
150    macro_rules! box_from_parts {
151        ($ptr:expr, $alloc:expr) => {{
152            Box::from_raw_in($ptr, $alloc)
153        }};
154    }
155    macro_rules! vec_from_parts {
156        ($ptr:expr, $cap:expr, $alloc:expr) => {{
157            Vec::from_raw_parts_in($ptr, 0, $cap, $alloc)
158        }};
159    }
160    macro_rules! allocation_impl {
161        ( $( $imp:tt )* ) => {
162            type ABox<T, A> = alloc::boxed::Box<T, A>;
163            type AVec<T, A> = alloc::vec::Vec<T, A>;
164            impl<A: Allocator> crate::Allocation<A> {
165                $( $imp )*
166            }
167        };
168    }
169    macro_rules! from_box_impl {
170        ( $( #[doc = $doc:literal] )*
171          struct DocAnchor;
172          $( $imp:tt )*
173        ) => {
174            $( #[doc = $doc ] )*
175            impl<T: ?Sized, A: Allocator> From<Box<T, A>> for crate::Allocation<A> {
176                $( $imp )*
177            }
178        };
179    }
180    macro_rules! from_vec_impl {
181        ( $( #[doc = $doc:literal] )*
182          struct DocAnchor;
183          $( $imp:tt )*
184        ) => {
185            $( #[doc = $doc ] )*
186            impl<T, A: Allocator> From<Vec<T, A>> for crate::Allocation<A> {
187                $( $imp )*
188            }
189        };
190    }
191    pub(super) use allocation_impl;
192    pub(super) use box_from_parts;
193    pub(super) use box_to_parts;
194    pub(super) use from_box_impl;
195    pub(super) use from_vec_impl;
196    pub(super) use vec_from_parts;
197    pub(super) use vec_to_parts;
198}
199
200#[cfg(not(feature = "nightly-std-conversions"))]
201mod alloc_no_allocator_api {
202    macro_rules! box_to_parts {
203        ($value:ident) => {
204            (Box::into_raw($value), $crate::alloc_shim::Global)
205        };
206    }
207
208    macro_rules! vec_to_parts {
209        ($vec:ident) => {{
210            let mut $vec = core::mem::ManuallyDrop::new($vec);
211            // TODO: wait for feature(vec_into_raw_parts)
212            (
213                $vec.as_mut_ptr(),
214                $vec.len(),
215                $vec.capacity(),
216                $crate::alloc_shim::Global,
217            )
218        }};
219    }
220
221    macro_rules! box_from_parts {
222        ($ptr:expr, $alloc:expr) => {{
223            let _: $crate::alloc_shim::Global = $alloc;
224            alloc::boxed::Box::from_raw($ptr)
225        }};
226    }
227    macro_rules! vec_from_parts {
228        ($ptr:expr, $cap:expr, $alloc:expr) => {{
229            let _: $crate::alloc_shim::Global = $alloc;
230            alloc::vec::Vec::from_raw_parts($ptr, 0, $cap)
231        }};
232    }
233
234    macro_rules! allocation_impl {
235        ( $( $imp:tt )* ) => {
236            pub trait UseA<A> { type This: ?Sized; }
237            impl<A, T: ?Sized> UseA<A> for T { type This = Self; }
238            type ABox<T, A> = <alloc::boxed::Box<T> as UseA<A>>::This;
239            type AVec<T, A> = <alloc::vec::Vec<T> as UseA<A>>::This;
240
241            type A = $crate::alloc_shim::Global;
242            impl<> crate::Allocation<> {
243                $( $imp )*
244            }
245        };
246    }
247    macro_rules! from_box_impl {
248        ( $( #[doc = $doc:literal] )*
249          struct DocAnchor;
250          $( $imp:tt )*
251        ) => {
252            $( #[doc = $doc ] )*
253            impl<T: ?Sized> From<Box<T>> for crate::Allocation {
254                $( $imp )*
255            }
256        };
257    }
258    macro_rules! from_vec_impl {
259        ( $( #[doc = $doc:literal] )*
260          struct DocAnchor;
261          $( $imp:tt )*
262        ) => {
263            $( #[doc = $doc ] )*
264            impl<T> From<Vec<T>> for crate::Allocation {
265                $( $imp )*
266            }
267        };
268    }
269    pub(super) use allocation_impl;
270    pub(super) use box_from_parts;
271    pub(super) use box_to_parts;
272    pub(super) use from_box_impl;
273    pub(super) use from_vec_impl;
274    pub(super) use vec_from_parts;
275    pub(super) use vec_to_parts;
276}
277
278#[cfg(feature = "nightly-std-conversions")]
279use alloc_allocator_api as api_impl;
280#[cfg(not(feature = "nightly-std-conversions"))]
281use alloc_no_allocator_api as api_impl;
282
283api_impl::allocation_impl! {
284    /// Convert the allocation into a box.
285    ///
286    /// This fails if the allocated layout does not match the requested type. The value might not be initialized,
287    /// use [`Box::assume_init`] in case you have initialized the memory of this allocation correctly.
288    ///
289    /// See also the opposite conversion `Allocation as From<Box<_>>`.
290    // TODO: add intro-doc link to `<Allocation as From<Box<_>>>`
291    pub fn try_into_box<T>(self) -> Result<ABox<MaybeUninit<T>, A>, BoxConversionError> {
292        let () = check_box_layout::<_, T>(&self)?;
293        // Commit to the conversion
294        let (ptr, _, alloc) = self.into_parts_with_alloc();
295        let ptr = ptr.as_ptr().cast();
296        // SAFETY:
297        Ok(unsafe { api_impl::box_from_parts!(ptr, alloc) })
298    }
299
300    /// Convert the allocation into a [`Vec`].
301    ///
302    /// This fails if the allocated size is not a multiple of the requested element size, or if the element type is zero-sized.
303    /// For the latter case, the capacity of the `Vec` would be ambiguous.
304    ///
305    /// The length of the returned vec is always set to `0` and has to be resized manually with [`Vec::set_len`].
306    ///
307    /// See also the opposite conversion `Allocation as From<Vec<_>>`.
308    // TODO: add intro-doc link to `<Allocation as From<Vec<_>>>`
309    pub fn try_into_vec<T>(self) -> Result<AVec<T, A>, VecConversionError> {
310        let capacity = check_vec_layout::<_, T>(&self)?;
311        let (ptr, _, alloc) = self.into_parts_with_alloc();
312        let ptr = ptr.as_ptr().cast();
313        Ok(unsafe { api_impl::vec_from_parts!(ptr, capacity, alloc) })
314    }
315}
316
317// This has to appear side-by-side with allocation_impl because it relies on `A` and `ABox` to be defined
318
319api_impl::from_box_impl! {
320    /// The value in the box will not be dropped, as if passed to [`forget`](core::mem::forget).
321    /// Use the inverse (fallible) conversion to recover the value.
322    ///
323    /// ```
324    /// # use std::mem::MaybeUninit;
325    /// # use untyped_box::Allocation;
326    /// let boxed = Box::new(42);
327    /// let alloc: Allocation = boxed.into();
328    /// let boxed = alloc.try_into_box::<u32>().unwrap();
329    /// let boxed = unsafe { boxed.assume_init() };
330    /// assert_eq!(*boxed, 42);
331    /// ```
332    struct DocAnchor;
333
334    fn from(value: ABox<T, A>) -> Self {
335        let layout = Layout::for_value(&*value);
336        let (ptr, alloc) = api_impl::box_to_parts!(value);
337        let ptr = unsafe { NonNull::new_unchecked(ptr) };
338        unsafe { Self::from_parts_in(ptr.cast(), layout, alloc) }
339    }
340}
341
342// This has to appear side-by-side with allocation_impl because it relies on `A` and `ABox` to be defined
343
344api_impl::from_vec_impl! {
345    /// The values in the `Vec` will not be dropped, as if by a call to [`vec.set_len(0)`](Vec::set_len).
346    ///
347    /// ```
348    /// # use std::mem::MaybeUninit;
349    /// # use untyped_box::Allocation;
350    /// let values = vec![42];
351    /// let alloc: Allocation = values.into();
352    /// let mut values = alloc.try_into_vec::<u32>().unwrap();
353    /// unsafe { values.set_len(1) };
354    /// assert_eq!(values, [42]);
355    /// ```
356    struct DocAnchor;
357
358    fn from(value: AVec<T, A>) -> Self {
359        let mut value = value;
360        unsafe { value.set_len(0) };
361        let layout = Layout::for_value(value.spare_capacity_mut());
362        let (ptr, _, _, alloc) = api_impl::vec_to_parts!(value);
363        let ptr = unsafe { NonNull::new_unchecked(ptr) };
364        unsafe { Self::from_parts_in(ptr.cast(), layout, alloc) }
365    }
366}