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}