vec_utils/vec.rs
1use std::alloc::Layout;
2use std::marker::PhantomData;
3use std::mem::ManuallyDrop;
4
5use super::{r#try, Try};
6
7mod general_zip;
8
9pub use general_zip::*;
10
11/// A type that contains useful meta-data about a
12/// the Vec<_> that it was created from
13pub struct Input<T> {
14 // the start of the vec data segment
15 start: *mut T,
16
17 // the current position in the vec data segment
18 ptr: *mut T,
19
20 // the length of the vec data segment
21 len: usize,
22
23 // the capacity of the vec data segment
24 cap: usize,
25
26 drop_alloc: bool,
27 drop: PhantomData<T>,
28}
29
30/// An write only buffer that may overlap with some input buffer
31/// this allows reuse of that input buffer to turn it into a
32/// `Vec<_>` inside of `tuple::try_into_vec`
33pub struct Output<T> {
34 // the start of the vec data segment
35 start: *mut T,
36
37 // the current position in the vec data segment
38 ptr: *mut T,
39
40 // the capacity of the vec data segment
41 cap: usize,
42 drop: PhantomData<T>,
43}
44
45impl<T> Output<T> {
46 /// Create a new output buffer, this buffer will own it's data segment
47 /// from `start` to `start.add(cap)`
48 pub unsafe fn new(start: *mut T, cap: usize) -> Self {
49 Self {
50 start,
51 ptr: start,
52 cap,
53 drop: PhantomData,
54 }
55 }
56}
57
58impl<T> From<Vec<T>> for Input<T> {
59 fn from(vec: Vec<T>) -> Self {
60 let mut vec = ManuallyDrop::new(vec);
61
62 let ptr = vec.as_mut_ptr();
63
64 Self {
65 start: ptr,
66 ptr,
67 len: vec.len(),
68 cap: vec.capacity(),
69 drop_alloc: true,
70 drop: PhantomData,
71 }
72 }
73}
74
75/// Extension methods for `Vec<T>`
76pub trait VecExt: Sized {
77 /// The type that the `Vec<T>` stores
78 type T;
79
80 /// Map a vector to another vector, will try and reuse the allocation if the
81 /// allocation layouts of the two types match, i.e. if
82 /// `std::alloc::Layout::<T>::new() == std::alloc::Layout::<U>::new()`
83 /// then the allocation will be reused
84 fn map<U, F: FnMut(Self::T) -> U>(self, mut f: F) -> Vec<U> {
85 use std::convert::Infallible;
86
87 match self.try_map(move |x| Ok::<_, Infallible>(f(x))) {
88 Ok(x) => x,
89 Err(x) => match x {},
90 }
91 }
92
93 /// Map a vector to another vector, will try and reuse the allocation if the
94 /// allocation layouts of the two types match, i.e. if
95 /// `std::alloc::Layout::<T>::new() == std::alloc::Layout::<U>::new()`
96 /// then the allocation will be reused
97 ///
98 /// The mapping function can be fallible, and on early return, it will drop all previous values,
99 /// and the rest of the input vector. Thre error will be returned as a `Result`
100 fn try_map<U, R: Try<Ok = U>, F: FnMut(Self::T) -> R>(self, f: F) -> Result<Vec<U>, R::Error>;
101
102 /// Zip a vector to another vector and combine them, the result will be returned,
103 /// the allocation will be reused if possible, the larger allocation of the input vectors
104 /// will be used if all of `T`, `U`, and `V` have the same allocation layouts.
105 fn zip_with<U, V, F: FnMut(Self::T, U) -> V>(self, other: Vec<U>, mut f: F) -> Vec<V> {
106 use std::convert::Infallible;
107
108 match self.try_zip_with(other, move |x, y| Ok::<_, Infallible>(f(x, y))) {
109 Ok(x) => x,
110 Err(x) => match x {},
111 }
112 }
113
114 /// Zip a vector to another vector and combine them, the result will be returned,
115 /// the allocation will be reused if possible, the larger allocation of the input vectors
116 /// will be used if all of `T`, `U`, and `V` have the same allocation layouts.
117 ///
118 /// The mapping function can be fallible, and on early return, it will drop all previous values,
119 /// and the rest of the input vectors. Thre error will be returned as a `Result`
120 fn try_zip_with<U, V, R: Try<Ok = V>, F: FnMut(Self::T, U) -> R>(
121 self,
122 other: Vec<U>,
123 f: F,
124 ) -> Result<Vec<V>, R::Error>;
125
126 /// Drops all of the values in the vector and
127 /// create a new vector from it if the layouts are compatible
128 ///
129 /// if layouts are not compatible, then return `Vec::new()`
130 fn drop_and_reuse<U>(self) -> Vec<U>;
131}
132
133impl<T> VecExt for Vec<T> {
134 type T = T;
135
136 fn try_map<U, R: Try<Ok = U>, F: FnMut(Self::T) -> R>(self, f: F) -> Result<Vec<U>, R::Error> {
137 // try_zip_with! { self => |x| { f(x) } }
138
139 if Layout::new::<T>() == Layout::new::<U>() {
140 let iter = MapIter {
141 init_len: 0,
142 data: Input::from(self),
143 drop: PhantomData,
144 };
145
146 iter.try_into_vec(f)
147 } else {
148 self.into_iter().map(f).map(R::into_result).collect()
149 }
150 }
151
152 fn try_zip_with<U, V, R: Try<Ok = V>, F: FnMut(Self::T, U) -> R>(
153 self,
154 other: Vec<U>,
155 mut f: F,
156 ) -> Result<Vec<V>, R::Error> {
157 // try_zip_with! { self, other => |x, y| { f(x, y) } }
158
159 let len = self.len().min(other.len());
160 match (
161 Layout::new::<T>() == Layout::new::<V>(),
162 Layout::new::<U>() == Layout::new::<V>(),
163 self.capacity() >= other.capacity(),
164 ) {
165 (true, true, true) | (true, false, _) => ZipWithIter {
166 init_len: len,
167 min_len: len,
168 drop: PhantomData,
169
170 left: Input::from(self),
171 right: Input::from(other),
172 }
173 .try_into_vec(f),
174 (true, true, false) | (false, true, _) => ZipWithIter {
175 init_len: len,
176 min_len: len,
177 drop: PhantomData,
178
179 left: Input::from(other),
180 right: Input::from(self),
181 }
182 .try_into_vec(move |y, x| f(x, y)),
183 (false, false, _) => self
184 .into_iter()
185 .zip(other.into_iter())
186 .map(move |(x, y)| f(x, y))
187 .map(R::into_result)
188 .collect(),
189 }
190 }
191
192 fn drop_and_reuse<U>(mut self) -> Vec<U> {
193 self.clear();
194
195 // no more elements in the vector
196 self.map(|_| unsafe { std::hint::unreachable_unchecked() })
197 }
198}
199
200struct MapIter<T, U> {
201 init_len: usize,
202
203 data: Input<T>,
204
205 // for drop check
206 drop: PhantomData<U>,
207}
208
209impl<T, U> MapIter<T, U> {
210 fn try_into_vec<R: Try<Ok = U>, F: FnMut(T) -> R>(
211 mut self,
212 mut f: F,
213 ) -> Result<Vec<U>, R::Error> {
214 // does a pointer walk, easy for LLVM to optimize
215 while self.init_len < self.data.len {
216 unsafe {
217 let value = r#try!(f(self.data.ptr.read()));
218
219 (self.data.ptr as *mut U).write(value);
220
221 self.data.ptr = self.data.ptr.add(1);
222 self.init_len += 1;
223 }
224 }
225
226 let vec = ManuallyDrop::new(self);
227
228 // we don't want to free the memory
229 // which is what dropping this `MapIter` will do
230 unsafe {
231 Ok(Vec::from_raw_parts(
232 vec.data.start as *mut U,
233 vec.data.len,
234 vec.data.cap,
235 ))
236 }
237 }
238}
239
240impl<T, U> Drop for MapIter<T, U> {
241 fn drop(&mut self) {
242 unsafe {
243 // destroy the initialized output
244 defer! {
245 Vec::from_raw_parts(
246 self.data.start as *mut U,
247 self.init_len,
248 self.data.cap
249 );
250 }
251
252 // offset by 1 because self.ptr is pointing to
253 // memory that was just read from, dropping that
254 // would lead to a double free
255 std::ptr::drop_in_place(std::slice::from_raw_parts_mut(
256 self.data.ptr.add(1),
257 self.data.len - self.init_len - 1,
258 ));
259 }
260 }
261}
262
263// The size of these structures don't matter since they are transient
264// So I didn't bother optimizing the size of them, and instead put all the
265// useful information I wanted, so that it could be initialized all at once
266struct ZipWithIter<T, U, V> {
267 // This left buffer is the one that will be reused
268 // to write the output into
269 left: Input<T>,
270
271 // We will only read from this buffer
272 //
273 // I considered using `std::vec::IntoIter`, but that lead to worse code
274 // because LLVM wasn't able to elide the bounds check on the iterator
275 right: Input<U>,
276
277 // the length of the output that has been written to
278 init_len: usize,
279 // the length of the vectors that must be traversed
280 min_len: usize,
281
282 // for drop check
283 drop: PhantomData<V>,
284}
285
286impl<T, U, V> ZipWithIter<T, U, V> {
287 fn try_into_vec<R: Try<Ok = V>, F: FnMut(T, U) -> R>(
288 mut self,
289 mut f: F,
290 ) -> Result<Vec<V>, R::Error> {
291 debug_assert_eq!(Layout::new::<T>(), Layout::new::<V>());
292
293 // this does a pointer walk and reads from left and right in lock-step
294 // then passes those values to the function to be processed
295 while let Some(min_len) = self.min_len.checked_sub(1) {
296 unsafe {
297 self.min_len = min_len;
298
299 let out = self.left.ptr as *mut V;
300 let left = self.left.ptr;
301 let right = self.right.ptr;
302
303 self.left.ptr = self.left.ptr.add(1);
304 self.right.ptr = self.right.ptr.add(1);
305
306 let value = r#try!(f(left.read(), right.read()));
307
308 out.write(value);
309 }
310 }
311
312 // We don't want to drop `self` if dropping the excess elements panics
313 // as that could lead to double drops
314 let vec = ManuallyDrop::new(self);
315 let output;
316
317 unsafe {
318 // create the vector now, so that if we panic in drop, we don't leak it
319 output = Vec::from_raw_parts(vec.left.start as *mut V, vec.init_len, vec.left.cap);
320
321 // yay for defers running in reverse order and cleaning up the
322 // old vecs properly
323
324 // cleans up the right vec
325 defer! {
326 Vec::from_raw_parts(vec.right.start, 0, vec.right.cap);
327 }
328
329 // drops the remaining elements of the right vec
330 defer! {
331 std::ptr::drop_in_place(std::slice::from_raw_parts_mut(
332 vec.right.ptr,
333 vec.right.len - vec.init_len
334 ));
335 }
336
337 // drop the remaining elements of the left vec
338 std::ptr::drop_in_place(std::slice::from_raw_parts_mut(
339 vec.left.ptr,
340 vec.left.len - vec.init_len,
341 ));
342 }
343
344 Ok(output)
345 }
346}
347
348impl<T, U, V> Drop for ZipWithIter<T, U, V> {
349 fn drop(&mut self) {
350 unsafe {
351 let len = self.init_len - self.min_len;
352
353 // This will happen last
354 //
355 // frees the allocated memory, but does not run destructors
356 defer! {
357 Vec::from_raw_parts(self.left.start, 0, self.left.cap);
358 Vec::from_raw_parts(self.right.start, 0, self.right.cap);
359 }
360
361 // The order of the next two defers don't matter for correctness
362 //
363 // They free the remaining parts of the two input vectors
364 defer! {
365 std::ptr::drop_in_place(std::slice::from_raw_parts_mut(self.right.ptr, self.right.len - len));
366 }
367
368 defer! {
369 std::ptr::drop_in_place(std::slice::from_raw_parts_mut(self.left.ptr, self.left.len - len));
370 }
371
372 // drop the output that we already calculated
373 std::ptr::drop_in_place(std::slice::from_raw_parts_mut(
374 self.left.start as *mut V,
375 len - 1,
376 ));
377 }
378 }
379}