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