tux_owned_alloc/
owned.rs

1use super::{AllocErr, UninitAlloc};
2use std::{
3    alloc::{dealloc, Layout},
4    fmt,
5    marker::PhantomData,
6    mem,
7    ops::{Deref, DerefMut},
8    ptr::NonNull,
9};
10
11/// Dynamic allocation of a `T` whose memory is considered fully initialized.
12/// The allocation and its content are freed on `drop`. Similar to a `Box`. If
13/// the size of the allocation is zero, no allocation is performed and a
14/// dangling pointer is used (just like in `std`). For the drop checker, the
15/// type acts as if it contains a `T` due to usage of `PhantomData<T>`.
16pub struct OwnedAlloc<T>
17where
18    T: ?Sized,
19{
20    nnptr: NonNull<T>,
21    _marker: PhantomData<T>,
22}
23
24impl<T> OwnedAlloc<T> {
25    /// Creates an allocation and initializes it to the passed argument. In case
26    /// of allocation error, the handler registered via stdlib is called.
27    pub fn new(val: T) -> Self {
28        UninitAlloc::new().init(val)
29    }
30
31    /// Creates an allocation and initializes it to the passed argument. In case
32    /// of allocation error, `Err` is returned.
33    pub fn try_new(val: T) -> Result<Self, AllocErr> {
34        UninitAlloc::try_new().map(|alloc| alloc.init(val))
35    }
36
37    /// Moves the stored value out from the allocation. The value and the
38    /// allocation now considered uninitialized are returned.
39    pub fn move_inner(self) -> (T, UninitAlloc<T>) {
40        let val = unsafe { self.nnptr.as_ptr().read() };
41        let alloc = unsafe { UninitAlloc::from_raw(self.nnptr) };
42        mem::forget(self);
43        (val, alloc)
44    }
45}
46
47impl<T> OwnedAlloc<T>
48where
49    T: ?Sized,
50{
51    /// Recreate the `OwnedAlloc` from a raw non-null pointer.
52    ///
53    /// # Safety
54    /// This functions is `unsafe` because passing the wrong pointer leads to
55    /// undefined behaviour. Passing a pointer to uninitialized memory is also
56    /// undefined behaviour.
57    pub unsafe fn from_raw(nnptr: NonNull<T>) -> Self {
58        Self { nnptr, _marker: PhantomData }
59    }
60
61    /// Converts the plain old standard library `Box` into an owned allocation.
62    ///
63    /// # Safety
64    /// This function is `unsafe` because there are no guarantees that `Box` and
65    /// `OwnedAlloc` allocate in the same way. They probably do in the Rust
66    /// version you are using, but there are no future guarantees.
67    pub unsafe fn from_box(boxed: Box<T>) -> Self {
68        Self::from_raw(NonNull::new_unchecked(Box::into_raw(boxed)))
69    }
70
71    /// Returns the raw non-null pointer of the allocation.
72    pub fn raw(&self) -> NonNull<T> {
73        self.nnptr
74    }
75
76    /// "Forgets" dropping both the allocation and its content and returns its
77    /// raw non-null pointer.
78    pub fn into_raw(self) -> NonNull<T> {
79        let nnptr = self.nnptr;
80        mem::forget(self);
81        nnptr
82    }
83
84    /// Converts the owned allocation into a plain old standard library `Box`.
85    ///
86    /// # Safety
87    /// This function is `unsafe` because there are no guarantees that `Box` and
88    /// `OwnedAlloc` allocate in the same way. They probably do in the Rust
89    /// version you are using, but there are no future guarantees.
90    pub unsafe fn into_box(self) -> Box<T> {
91        Box::from_raw(self.into_raw().as_ptr())
92    }
93
94    /// Drops the memory and returns the allocation now considered
95    /// uninitialized.
96    pub fn drop_in_place(self) -> UninitAlloc<T> {
97        unsafe {
98            self.nnptr.as_ptr().drop_in_place();
99            UninitAlloc::from_raw(self.into_raw())
100        }
101    }
102
103    /// "Forgets" about dropping the inner value and returns an uninitialized
104    /// allocation.
105    pub fn forget_inner(self) -> UninitAlloc<T> {
106        unsafe { UninitAlloc::from_raw(self.into_raw()) }
107    }
108}
109
110impl<T> Drop for OwnedAlloc<T>
111where
112    T: ?Sized,
113{
114    fn drop(&mut self) {
115        unsafe {
116            let layout = Layout::for_value(self.nnptr.as_ref());
117            self.nnptr.as_ptr().drop_in_place();
118            if layout.size() != 0 {
119                dealloc(self.nnptr.cast().as_ptr(), layout);
120            }
121        }
122    }
123}
124
125impl<T> Deref for OwnedAlloc<T>
126where
127    T: ?Sized,
128{
129    type Target = T;
130
131    fn deref(&self) -> &T {
132        unsafe { self.nnptr.as_ref() }
133    }
134}
135
136impl<T> DerefMut for OwnedAlloc<T>
137where
138    T: ?Sized,
139{
140    fn deref_mut(&mut self) -> &mut T {
141        unsafe { self.nnptr.as_mut() }
142    }
143}
144
145impl<T> fmt::Debug for OwnedAlloc<T>
146where
147    T: ?Sized,
148{
149    fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
150        write!(fmtr, "{:?}", self.nnptr)
151    }
152}
153
154impl<T> Clone for OwnedAlloc<T>
155where
156    T: Clone,
157{
158    fn clone(&self) -> Self {
159        Self::new((**self).clone())
160    }
161}
162
163impl<T> From<T> for OwnedAlloc<T> {
164    fn from(val: T) -> Self {
165        Self::new(val)
166    }
167}
168
169unsafe impl<T> Send for OwnedAlloc<T> where T: ?Sized + Send {}
170unsafe impl<T> Sync for OwnedAlloc<T> where T: ?Sized + Sync {}
171
172#[cfg(test)]
173mod test {
174    use super::OwnedAlloc;
175
176    #[test]
177    fn inner_eq() {
178        let mut alloc = OwnedAlloc::new(20);
179
180        assert_eq!(*alloc, 20);
181
182        *alloc = 30;
183
184        assert_eq!(*alloc, 30);
185    }
186
187    #[test]
188    fn move_inner_eq() {
189        let alloc = OwnedAlloc::new(20);
190
191        assert_eq!(alloc.move_inner().0, 20);
192    }
193
194    #[test]
195    fn from_into_std_box() {
196        let boxed = unsafe { OwnedAlloc::new([5u128; 32]).into_box() };
197        assert_eq!(*boxed, [5; 32]);
198        let raw = unsafe { OwnedAlloc::from_box(boxed) };
199        assert_eq!(*raw, [5; 32]);
200    }
201}