owned_alloc/
raw_vec.rs

1use super::{AllocErr, LayoutErr, RawVecErr, UninitAlloc};
2use std::{
3    alloc::{alloc, dealloc, handle_alloc_error, realloc, Layout},
4    fmt,
5    marker::PhantomData,
6    mem,
7    ptr::NonNull,
8    slice,
9};
10
11/// Raw Vector allocation. This allocation, instead of holding a pointer to a
12/// single `T`, holds a pointer to as many `T` are required. The allocation is
13/// resizable and is freed on `drop`. No initialization or deinitialization of
14/// the elements is performed. This type may be useful for `Vec`-like types. If
15/// the size of the allocation is zero, no allocation is performed and a
16/// dangling pointer is used (just like in `std`). For the drop checker, the
17/// type acts as if it contains a `T` due to usage of `PhantomData<T>`.
18///
19/// ```rust
20/// extern crate owned_alloc;
21///
22/// use owned_alloc::RawVec;
23///
24/// let mut vec = RawVec::<usize>::with_capacity(200);
25/// assert_eq!(200, vec.cap());
26/// assert_eq!(200, unsafe { vec.as_slice().len() });
27///
28/// vec.resize(354);
29/// assert_eq!(354, vec.cap());
30/// assert_eq!(354, unsafe { vec.as_slice().len() });
31/// ```
32pub struct RawVec<T> {
33    nnptr: NonNull<T>,
34    cap: usize,
35    _marker: PhantomData<T>,
36}
37
38impl<T> RawVec<T> {
39    /// Creates a new `RawVec` of capacity `0` and a dangling pointer. No
40    /// allocation is performed.
41    pub fn new() -> Self {
42        Self { nnptr: NonNull::dangling(), cap: 0, _marker: PhantomData }
43    }
44
45    /// Creates a new `RawVec` with a given capacity. In case of allocation
46    /// error, the handler registered via stdlib is called. In case of overflow
47    /// calculating the total size, the function panics.
48    pub fn with_capacity(cap: usize) -> Self {
49        match Self::try_with_capacity(cap) {
50            Ok(this) => this,
51            Err(RawVecErr::Alloc(err)) => handle_alloc_error(err.layout),
52            Err(RawVecErr::Layout(err)) => {
53                panic!("Capacity overflows memory size: {}", err)
54            },
55        }
56    }
57
58    /// Creates a new `RawVec` with a given capacity. In case of allocation
59    /// error or overflow calculating the total size, `Err` is returned.
60    pub fn try_with_capacity(cap: usize) -> Result<Self, RawVecErr> {
61        let layout = Self::make_layout(cap)?;
62        let res = if layout.size() == 0 {
63            Ok(NonNull::dangling())
64        } else {
65            NonNull::new(unsafe { alloc(layout) })
66                .map(NonNull::cast::<T>)
67                .ok_or(AllocErr { layout }.into())
68        };
69
70        res.map(|nnptr| Self { nnptr, cap, _marker: PhantomData })
71    }
72
73    /// Creates a `RawVec` from a plain old standard library `Vec`. Beware, only
74    /// the pointer and the capacity are saved. The length is discarded. If you
75    /// want to keep track of the length, you will have to store it for
76    /// yourself. Note also that no element is dropped (ever) by the
77    /// `RawVec`.
78    ///
79    /// # Safety
80    /// This function is `unsafe` because there are no guarantees that `Vec` and
81    /// `RawVec` allocate in the same way. They probably do in the Rust version
82    /// you are using, but there are no future guarantees.
83    pub unsafe fn from_vec(mut vec: Vec<T>) -> Self {
84        let this = Self {
85            nnptr: NonNull::new_unchecked(vec.as_mut_ptr()),
86            cap: vec.capacity(),
87            _marker: PhantomData,
88        };
89        mem::forget(vec);
90        this
91    }
92
93    /// Recreate the `RawVec` from a raw non-null pointer and a capacity.
94    ///
95    /// # Safety
96    /// This functions is `unsafe` because passing the wrong pointer leads to
97    /// undefined behaviour. Passing wrong capacity also leads to undefined
98    /// behaviour.
99    pub unsafe fn from_raw_parts(nnptr: NonNull<T>, cap: usize) -> Self {
100        Self { nnptr, cap, _marker: PhantomData }
101    }
102
103    /// Recreate the `RawVec` from a raw non-null pointer to a slice with length
104    /// equal to the `RawVec`'s capacity.
105    ///
106    /// # Safety
107    /// This functions is `unsafe` because passing the wrong pointer leads to
108    /// undefined behaviour, including passing a pointer with the wrong length.
109    pub unsafe fn from_raw_slice(mut raw: NonNull<[T]>) -> Self {
110        Self {
111            nnptr: NonNull::new_unchecked(raw.as_mut().as_mut_ptr()),
112            cap: raw.as_ref().len(),
113            _marker: PhantomData,
114        }
115    }
116
117    /// The requested allocation capacity. It is guaranteed to be the capacity
118    /// passed to the last capacity-modifier method. Those are
119    /// `with_capacity`, `try_with_capacity` and `resize`. The methods `new`
120    /// and `try_new` initialize the capacity to `0`.
121    pub fn cap(&self) -> usize {
122        self.cap
123    }
124
125    /// The raw non-null pointer to the first element.
126    pub fn raw(&self) -> NonNull<T> {
127        self.nnptr
128    }
129
130    /// The raw non-null pointer to the slice with length equal to the
131    /// `RawVec`'s capacity.
132    pub fn raw_slice(&self) -> NonNull<[T]> {
133        unsafe { NonNull::from(self.as_slice()) }
134    }
135
136    /// "Forgets" dropping the allocation and returns a raw non-null pointer to
137    /// the slice with length equal to the `RawVec`'s capacity.
138    pub fn into_raw_slice(self) -> NonNull<[T]> {
139        let ptr = self.raw_slice();
140        mem::forget(self);
141        ptr
142    }
143
144    /// Encodes the `RawVec` as an immutable reference to a slice with length
145    /// equal to the capacity.
146    ///
147    /// # Safety
148    /// This function is `unsafe` because if the index of an uninitialized
149    /// element is accessed incorrectly, undefined behavior occurs.
150    pub unsafe fn as_slice(&self) -> &[T] {
151        slice::from_raw_parts(self.nnptr.as_ptr(), self.cap())
152    }
153
154    /// Encodes the `RawVec` as an mutable reference to a slice with length
155    /// equal to the capacity.
156    ///
157    /// # Safety
158    /// This function is `unsafe` because if the index of an uninitialized
159    /// element is accessed incorrectly, undefined behavior occurs.
160    pub unsafe fn as_mut_slice(&mut self) -> &mut [T] {
161        slice::from_raw_parts_mut(self.nnptr.as_ptr(), self.cap())
162    }
163
164    /// Creates a plain old standard library `Vec` from the `RawVec` and a given
165    /// length.
166    ///
167    /// # Safety
168    /// This function is `unsafe` because there are no guarantees that `Vec` and
169    /// `RawVec` allocate in the same way. They probably do in the Rust version
170    /// you are using, but there are no future guarantees. Also, the length
171    /// argument must be passed correctly, since the elements until the given
172    /// length will be considered correctly, but the `RawVec` initialize no
173    /// element.
174    pub unsafe fn into_vec(self, len: usize) -> Vec<T> {
175        let vec = Vec::from_raw_parts(self.nnptr.as_ptr(), len, self.cap);
176        mem::forget(self);
177        vec
178    }
179
180    /// Resizes the `RawVec` with a given capacity. In case of allocation
181    /// error, the handler registered via stdlib is called. In case of overflow
182    /// calculating the total size, the function panics.
183    pub fn resize(&mut self, new_cap: usize) {
184        match self.try_resize(new_cap) {
185            Err(RawVecErr::Alloc(err)) => handle_alloc_error(err.layout),
186            Err(RawVecErr::Layout(err)) => {
187                panic!("Capacity overflows memory size: {}", err)
188            },
189
190            Ok(_) => (),
191        }
192    }
193
194    /// Resizes the `RawVec` with a given capacity. In case of allocation
195    /// error or overflow calculating the total size, `Err` is returned. In case
196    /// of failure, the original allocation is untouched.
197    pub fn try_resize(&mut self, new_cap: usize) -> Result<(), RawVecErr> {
198        let layout = Self::make_layout(new_cap)?;
199
200        let res = if layout.size() == 0 {
201            self.free();
202            Ok(NonNull::dangling())
203        } else {
204            let old = Self::make_layout(self.cap).unwrap();
205            NonNull::new(unsafe {
206                realloc(self.nnptr.cast().as_ptr(), old, layout.size())
207            })
208            .map(NonNull::cast::<T>)
209            .ok_or(AllocErr { layout }.into())
210        };
211
212        res.map(|nnptr| {
213            self.nnptr = nnptr;
214            self.cap = new_cap;
215        })
216    }
217
218    fn free(&self) {
219        if self.cap != 0 && mem::size_of::<T>() != 0 {
220            let layout = Self::make_layout(self.cap).unwrap();
221            unsafe {
222                dealloc(self.nnptr.cast().as_ptr(), layout);
223            }
224        }
225    }
226
227    fn make_layout(cap: usize) -> Result<Layout, LayoutErr> {
228        let total_size =
229            mem::size_of::<T>().checked_mul(cap).ok_or(LayoutErr)?;
230        Layout::from_size_align(total_size, mem::align_of::<T>())
231            .map_err(Into::into)
232    }
233}
234
235impl<T> fmt::Debug for RawVec<T> {
236    fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
237        write!(
238            fmtr,
239            "RawVec {} pointer {:?}, cap: {} {}",
240            '{', self.nnptr, self.cap, '}'
241        )
242    }
243}
244
245impl<T> Drop for RawVec<T> {
246    fn drop(&mut self) {
247        self.free();
248    }
249}
250
251impl<T> From<UninitAlloc<T>> for RawVec<T> {
252    fn from(alloc: UninitAlloc<T>) -> Self {
253        Self { nnptr: alloc.into_raw(), cap: 1, _marker: PhantomData }
254    }
255}
256
257unsafe impl<T> Send for RawVec<T> where T: Send {}
258unsafe impl<T> Sync for RawVec<T> where T: Sync {}
259
260#[cfg(test)]
261mod test {
262    use super::RawVec;
263
264    #[test]
265    fn cap_is_the_one_passed() {
266        let mut alloc = RawVec::<usize>::with_capacity(20);
267        assert_eq!(alloc.cap(), 20);
268
269        alloc.resize(50);
270        assert_eq!(alloc.cap(), 50);
271
272        alloc.resize(5);
273        assert_eq!(alloc.cap(), 5);
274    }
275
276    #[test]
277    fn from_into_std_vec() {
278        let vec = unsafe { RawVec::<u128>::with_capacity(465).into_vec(0) };
279        assert_eq!(vec.capacity(), 465);
280        let raw = unsafe { RawVec::from_vec(vec) };
281        assert_eq!(raw.cap(), 465);
282    }
283}