use replace_with::on_return_or_unwind;
use std::{alloc::Layout, marker::PhantomData, mem::ManuallyDrop};
use super::{r#try, Try};
struct Input<T> {
start: *mut T,
ptr: *mut T,
len: usize,
cap: usize,
drop: PhantomData<T>,
}
impl<T> From<Vec<T>> for Input<T> {
fn from(vec: Vec<T>) -> Self {
let mut vec = ManuallyDrop::new(vec);
let ptr = vec.as_mut_ptr();
Self {
start: ptr,
ptr,
len: vec.len(),
cap: vec.capacity(),
drop: PhantomData,
}
}
}
pub trait VecExt: Sized {
type T;
fn map<U, F: FnMut(Self::T) -> U>(self, mut f: F) -> Vec<U> {
use std::convert::Infallible;
match self.try_map(move |x| Ok::<_, Infallible>(f(x))) {
Ok(x) => x,
Err(x) => match x {},
}
}
fn try_map<U, R: Try<Ok = U>, F: FnMut(Self::T) -> R>(self, f: F) -> Result<Vec<U>, R::Error>;
fn recycle<U>(self) -> Vec<U>;
}
impl<T> VecExt for Vec<T> {
type T = T;
fn try_map<U, R: Try<Ok = U>, F: FnMut(Self::T) -> R>(self, f: F) -> Result<Vec<U>, R::Error> {
if Layout::new::<T>() == Layout::new::<U>() {
let iter = MapIter {
init_len: 0,
data: Input::from(self),
drop: PhantomData,
};
iter.try_into_vec(f)
} else {
self.into_iter().map(f).map(R::into_result).collect()
}
}
fn recycle<U>(mut self) -> Vec<U> {
self.clear();
self.map(|_| unsafe { std::hint::unreachable_unchecked() })
}
}
struct MapIter<T, U> {
init_len: usize,
data: Input<T>,
drop: PhantomData<U>,
}
impl<T, U> MapIter<T, U> {
fn try_into_vec<R: Try<Ok = U>, F: FnMut(T) -> R>(
mut self, mut f: F,
) -> Result<Vec<U>, R::Error> {
while self.init_len < self.data.len {
unsafe {
let value = r#try!(f(self.data.ptr.read()));
(self.data.ptr as *mut U).write(value);
self.data.ptr = self.data.ptr.add(1);
self.init_len += 1;
}
}
let vec = ManuallyDrop::new(self);
unsafe {
Ok(Vec::from_raw_parts(
vec.data.start as *mut U,
vec.data.len,
vec.data.cap,
))
}
}
}
impl<T, U> Drop for MapIter<T, U> {
fn drop(&mut self) {
unsafe {
on_return_or_unwind(
|| {
std::ptr::drop_in_place(std::slice::from_raw_parts_mut(
self.data.ptr.add(1),
self.data.len - self.init_len - 1,
));
},
|| {
let _ = Vec::from_raw_parts(
self.data.start as *mut U,
self.init_len,
self.data.cap,
);
},
)
}
}
}