use std::alloc::{handle_alloc_error, Layout};
use std::mem::MaybeUninit;
use std::{alloc, ptr};
pub trait Map<T1> {
type Target<T2>;
fn map_box<T2>(self, f: impl FnMut(T1) -> T2) -> Self::Target<T2>;
}
impl<T1> Map<T1> for Box<T1> {
type Target<T2> = Box<T2>;
fn map_box<T2>(self, mut f: impl FnMut(T1) -> T2) -> Self::Target<T2> {
let from_layout = Layout::new::<T1>();
let to_layout = Layout::new::<T2>();
if from_layout.size() == 0
|| to_layout.size() == 0
|| from_layout.align() != to_layout.align()
{
return Box::new(f(*self));
}
let from_ptr = Box::into_raw(self);
let v = unsafe { ptr::read(from_ptr) };
let tmp_box: Box<MaybeUninit<T1>> =
unsafe { Box::from_raw(from_ptr as *mut MaybeUninit<T1>) };
let v = f(v);
Box::into_raw(tmp_box);
let to_ptr = if to_layout.size() != from_layout.size() {
let to_ptr = unsafe {
alloc::realloc(from_ptr as *mut u8, from_layout, to_layout.size()) as *mut T2
};
if to_ptr.is_null() {
unsafe {
alloc::dealloc(from_ptr as *mut u8, from_layout);
}
handle_alloc_error(to_layout)
}
to_ptr
} else {
from_ptr as *mut T2
};
unsafe {
ptr::write(to_ptr, v);
Box::from_raw(to_ptr)
}
}
}
#[cfg(test)]
mod tests {
use crate::Map;
#[test]
fn same_type() {
let b = Box::new(21u64);
let b = b.map_box(|v| v * 2);
assert_eq!(*b, 42);
}
#[test]
fn same_size() {
let b = Box::new(42u64);
let b = b.map_box(|v| v as i64);
assert_eq!(*b, 42);
}
#[test]
fn down_size() {
let b = Box::new(42u64);
let b = b.map_box(|v| v as u32);
assert_eq!(*b, 42);
}
#[test]
fn up_size() {
let b = Box::new(42u32);
let b = b.map_box(|v| v as u64);
assert_eq!(*b, 42);
}
#[test]
fn zst_in() {
let b = Box::new(());
let b = b.map_box(|_| 42u64);
assert_eq!(*b, 42);
}
#[test]
fn zst_out() {
let b = Box::new(42u64);
let b = b.map_box(|_| ());
assert_eq!(*b, ());
}
#[test]
fn zst_both() {
let b = Box::new(());
let b: Box<[u64; 0]> = b.map_box(|_| []);
assert_eq!(*b, []);
}
#[test]
#[should_panic]
fn panic() {
let b = Box::new(42u64);
b.map_box(|_| panic!());
}
#[test]
fn align_up_high() {
#[repr(align(16))]
struct Test1(u64);
#[repr(align(128))]
struct Test2(u64);
let b = Box::new(Test1(42));
let b = b.map_box(|Test1(v)| Test2(v));
assert_eq!(b.0, 42);
}
#[test]
fn align_down_high() {
#[repr(align(128))]
struct Test1(u64);
#[repr(align(16))]
struct Test2(u64);
let b = Box::new(Test1(42));
let b = b.map_box(|Test1(v)| Test2(v));
assert_eq!(b.0, 42);
}
#[test]
fn align_up_low() {
#[repr(align(1))]
struct Test1(u64);
#[repr(align(8))]
struct Test2(u64);
let b = Box::new(Test1(42));
let b = b.map_box(|Test1(v)| Test2(v));
assert_eq!(b.0, 42);
}
#[test]
fn align_down_low() {
#[repr(align(8))]
struct Test1(u64);
#[repr(align(1))]
struct Test2(u64);
let b = Box::new(Test1(42));
let b = b.map_box(|Test1(v)| Test2(v));
assert_eq!(b.0, 42);
}
}