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