owned_alloc/
uninit.rs

1use super::{AllocErr, OwnedAlloc, RawVec};
2use std::{
3    alloc::{alloc, dealloc, handle_alloc_error, Layout},
4    fmt,
5    marker::PhantomData,
6    mem,
7    ptr::NonNull,
8};
9
10/// Dynamic allocation of a `T` whose memory is considered uninitialized. The
11/// allocation is freed on `drop`. If the size of the allocation is zero, no
12/// allocation is performed and a dangling pointer is used (just like in `std`).
13/// For the drop checker, the type acts as if it contains a `T` due to usage of
14/// `PhantomData<T>`.
15pub struct UninitAlloc<T>
16where
17    T: ?Sized,
18{
19    nnptr: NonNull<T>,
20    _marker: PhantomData<T>,
21}
22
23impl<T> UninitAlloc<T> {
24    /// Creates room for a `T`. In case of allocation error, the handler
25    /// registered via stdlib is called.
26    pub fn new() -> Self {
27        Self::try_new().unwrap_or_else(|err| handle_alloc_error(err.layout))
28    }
29
30    /// Creates room for a `T`. In case of allocation error, `Err` is returned.
31    pub fn try_new() -> Result<Self, AllocErr> {
32        let layout = Layout::new::<T>();
33
34        let res = if layout.size() == 0 {
35            Ok(NonNull::dangling())
36        } else {
37            NonNull::new(unsafe { alloc(layout) })
38                .map(NonNull::cast::<T>)
39                .ok_or(AllocErr { layout })
40        };
41
42        res.map(|nnptr| Self { nnptr, _marker: PhantomData })
43    }
44
45    /// Initializes the memory and returns the allocation now considered
46    /// initialized.
47    pub fn init(self, val: T) -> OwnedAlloc<T> {
48        let raw = self.into_raw();
49        unsafe {
50            raw.as_ptr().write(val);
51            OwnedAlloc::from_raw(raw)
52        }
53    }
54}
55
56impl<T> UninitAlloc<T>
57where
58    T: ?Sized,
59{
60    /// Calls a function with a mutable reference to uninitialized memory and
61    /// returns the allocation now considered initialized. The passed function
62    /// is expected to initialize the memory.
63    ///
64    /// # Safety
65    /// This function is `unsafe` because the passed function might not
66    /// initialize the memory correctly.
67    pub unsafe fn init_in_place<F>(self, init: F) -> OwnedAlloc<T>
68    where
69        F: FnOnce(&mut T),
70    {
71        let mut raw = self.into_raw();
72        init(raw.as_mut());
73        OwnedAlloc::from_raw(raw)
74    }
75
76    /// Recreate the `UninitAlloc` from a raw non-null pointer.
77    ///
78    /// # Safety
79    /// This functions is `unsafe` because passing the wrong pointer leads to
80    /// undefined behaviour.
81    pub unsafe fn from_raw(nnptr: NonNull<T>) -> Self {
82        Self { nnptr, _marker: PhantomData }
83    }
84
85    /// Returns the raw non-null pointer of the allocation.
86    pub fn raw(&self) -> NonNull<T> {
87        self.nnptr
88    }
89
90    /// "Forgets" dropping the allocation and returns its raw non-null pointer.
91    pub fn into_raw(self) -> NonNull<T> {
92        let nnptr = self.nnptr;
93        mem::forget(self);
94        nnptr
95    }
96}
97
98impl<T> Drop for UninitAlloc<T>
99where
100    T: ?Sized,
101{
102    fn drop(&mut self) {
103        unsafe {
104            let layout = Layout::for_value(self.nnptr.as_ref());
105
106            if layout.size() != 0 {
107                dealloc(self.nnptr.cast().as_ptr(), layout);
108            }
109        }
110    }
111}
112
113impl<T> fmt::Debug for UninitAlloc<T>
114where
115    T: ?Sized,
116{
117    fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
118        write!(fmtr, "{:?}", self.nnptr)
119    }
120}
121
122impl<T> From<RawVec<T>> for UninitAlloc<[T]> {
123    fn from(alloc: RawVec<T>) -> Self {
124        Self { nnptr: alloc.into_raw_slice(), _marker: PhantomData }
125    }
126}
127
128unsafe impl<T> Send for UninitAlloc<T> where T: ?Sized + Send {}
129unsafe impl<T> Sync for UninitAlloc<T> where T: ?Sized + Sync {}
130
131#[cfg(test)]
132mod test {
133    use super::UninitAlloc;
134
135    #[test]
136    fn into_from_raw() {
137        let alloc = UninitAlloc::<usize>::new();
138        let raw_borrowed = alloc.raw();
139        let raw = alloc.into_raw();
140
141        assert_eq!(raw, raw_borrowed);
142
143        let alloc = unsafe { UninitAlloc::from_raw(raw) };
144        assert_eq!(alloc.raw(), raw_borrowed);
145    }
146}