1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
//! Stuff to boost things in the `alloc` crate. //! //! You must use the crate with the `extern_crate_alloc` feature for the content //! in this module to be compiled in! use super::*; use alloc::{ alloc::{alloc_zeroed, Layout}, boxed::Box, vec::Vec, }; /// As [`try_cast_box`](try_cast_box), but unwraps for you. #[inline] pub fn cast_box<A: Pod, B: Pod>(input: Box<A>) -> Box<B> { try_cast_box(input).map_err(|(e, _v)| e).unwrap() } /// Attempts to cast the content type of a [`Box`](alloc::boxed::Box). /// /// On failure you get back an error along with the starting `Box`. /// /// ## Failure /// /// * The start and end content type of the `Box` must have the exact same /// alignment. /// * The start and end size of the `Box` must have the exact same size. #[inline] pub fn try_cast_box<A: Pod, B: Pod>(input: Box<A>) -> Result<Box<B>, (PodCastError, Box<A>)> { if align_of::<A>() != align_of::<B>() { Err((PodCastError::AlignmentMismatch, input)) } else if size_of::<A>() != size_of::<B>() { Err((PodCastError::SizeMismatch, input)) } else { // Note(Lokathor): This is much simpler than with the Vec casting! let ptr: *mut B = Box::into_raw(input).cast::<B>(); Ok(unsafe { Box::from_raw(ptr) }) } } /// Allocates a `Box<T>` with all of the contents being zeroed out. /// /// This uses the global allocator to create a zeroed allocation and _then_ /// turns it into a Box. In other words, it's 100% assured that the zeroed data /// won't be put temporarily on the stack. You can make a box of any size /// without fear of a stack overflow. /// /// ## Failure /// /// This fails if the allocation fails. #[inline] pub fn try_zeroed_box<T: Zeroable>() -> Result<Box<T>, ()> { if size_of::<T>() == 0 { return Ok(Box::new(T::zeroed())); } let layout = Layout::from_size_align(size_of::<T>(), align_of::<T>()).unwrap(); let ptr = unsafe { alloc_zeroed(layout) }; if ptr.is_null() { // we don't know what the error is because `alloc_zeroed` is a dumb API Err(()) } else { Ok(unsafe { Box::<T>::from_raw(ptr.cast::<T>()) }) } } /// As [`try_zeroed_box`], but unwraps for you. #[inline] pub fn zeroed_box<T: Zeroable>() -> Box<T> { try_zeroed_box().unwrap() } /// As [`try_cast_vec`](try_cast_vec), but unwraps for you. #[inline] pub fn cast_vec<A: Pod, B: Pod>(input: Vec<A>) -> Vec<B> { try_cast_vec(input).map_err(|(e, _v)| e).unwrap() } /// Attempts to cast the content type of a [`Vec`](alloc::vec::Vec). /// /// On failure you get back an error along with the starting `Vec`. /// /// ## Failure /// /// * The start and end content type of the `Vec` must have the exact same /// alignment. /// * The start and end size of the `Vec` must have the exact same size. /// * In the future this second restriction might be lessened by having the /// capacity and length get adjusted during transmutation, but for now it's /// absolute. #[inline] pub fn try_cast_vec<A: Pod, B: Pod>(input: Vec<A>) -> Result<Vec<B>, (PodCastError, Vec<A>)> { if align_of::<A>() != align_of::<B>() { Err((PodCastError::AlignmentMismatch, input)) } else if size_of::<A>() != size_of::<B>() { // Note(Lokathor): Under some conditions it would be possible to cast // between Vec content types of the same alignment but different sizes by // changing the capacity and len values in the output Vec. However, we will // not attempt that for now. Err((PodCastError::SizeMismatch, input)) } else { // Note(Lokathor): First we record the length and capacity, which don't have // any secret provenance metadata. let length: usize = input.len(); let capacity: usize = input.capacity(); // Note(Lokathor): Next we "pre-forget" the old Vec by wrapping with // ManuallyDrop, because if we used `core::mem::forget` after taking the // pointer then that would invalidate our pointer. In nightly there's a // "into raw parts" method, which we can switch this too eventually. let mut manual_drop_vec = ManuallyDrop::new(input); let vec_ptr: *mut A = manual_drop_vec.as_mut_ptr(); let ptr: *mut B = vec_ptr.cast::<B>(); Ok(unsafe { Vec::from_raw_parts(ptr, length, capacity) }) } }