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}