transvec/
lib.rs

1#![cfg_attr(feature = "allocator_api", feature(allocator_api))]
2#![cfg_attr(not(feature = "std"), no_std)]
3#![warn(unsafe_op_in_unsafe_fn)]
4#![deny(missing_docs)]
5//! This is a way to "transmute" Vecs soundly.
6//! ```
7//! # #![feature(allocator_api)]
8//! # #[cfg(feature = "allocator_api")]
9//! # {
10//! # /*
11//! #![feature(allocator_api)] // this requires the allocator api because the way that this
12//! // handles deallocating hooks into the allocator api
13//! # */
14//! use transvec::transmute_vec;
15//! let input_vec: Vec<u16> = vec![1, 2, 3, 4, 5, 6, 7, 8];
16//! let output: Vec<u8, _> = match transmute_vec(input_vec) {
17//!     Ok(x) => x,
18//!     // the "transmute" can fail, if the alignment/capacity/length is incorrect
19//!     // consider using `transmute_vec_may_copy`
20//!     Err((old_vec, err)) => return println!("Error: {:?}", err),
21//! };
22//! if cfg!(target_endian = "big") {
23//!     assert_eq!(
24//!         &output,
25//!         &[0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8]
26//!     );
27//! } else {
28//!     assert_eq!(
29//!         &output,
30//!         &[1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0]
31//!     );
32//! }
33//! # }
34//! ```
35
36extern crate alloc;
37
38use alloc::vec::Vec;
39use bytemuck::Pod;
40
41#[cfg(feature = "allocator_api")]
42use core::alloc::{AllocError, Allocator, Layout};
43use core::{
44    cmp::Ordering,
45    fmt::Display,
46    hint,
47    marker::PhantomData,
48    mem::{self, ManuallyDrop},
49    ptr::{self, NonNull},
50    slice,
51    sync::atomic::{self, AtomicPtr},
52};
53
54#[cfg(feature = "std")]
55use std::error::Error;
56
57/// Error for Vec transmutes.
58/// It will always be `Alignment` -> `Length` -> `Capacity`
59#[derive(Debug, PartialEq, Eq)]
60pub enum TransmuteError {
61    /// When the alignment of vec is incorrect.
62    Alignment,
63    /// When the length wouldn't be able to fit.
64    Length,
65    /// When the capacity wouldn't be able to fit.
66    Capacity,
67}
68
69impl Display for TransmuteError {
70    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
71        match self {
72            TransmuteError::Alignment => write!(formatter, "Alignment: alignment of the pointer to I must be equal or greater than the alignment of O"),
73            TransmuteError::Length => write!(formatter, "Length: I's items cant fit in O's (e.g. 1 u8 to 1 u16)"),
74            TransmuteError::Capacity => write!(formatter, "Capacity: capacicty of vec is incorrect"),
75        }
76    }
77}
78
79#[cfg(feature = "std")]
80impl Error for TransmuteError {}
81
82#[cfg(feature = "allocator_api")]
83/// Handling correcting the alignment fed into the inner allocator.
84#[derive(Debug)]
85pub struct AlignmentCorrectorAllocator<I, O, A: Allocator> {
86    allocator: A,
87    ptr: AtomicPtr<O>,
88    phantom: PhantomData<I>,
89}
90#[cfg(feature = "allocator_api")]
91impl<I, O, A: Allocator> AlignmentCorrectorAllocator<I, O, A> {
92    /// Create new `AlignmentCorrectorAllocator`.
93    ///
94    /// # Safety
95    /// The `ptr` must be allocated with the `allocator` in the alignment of `I`.
96    pub unsafe fn new(ptr: NonNull<O>, allocator: A) -> Self {
97        Self {
98            allocator,
99            ptr: AtomicPtr::new(ptr.as_ptr()),
100            phantom: PhantomData::default(),
101        }
102    }
103    /// Create a new `AlignmentCorrectorAllocator` that acts the same as the allocator fed into it.
104    pub fn new_null(allocator: A) -> Self {
105        Self {
106            allocator,
107            ptr: AtomicPtr::new(ptr::null_mut()),
108            phantom: PhantomData::default(),
109        }
110    }
111
112    /// Is the `AlignmentCorrectorAllocator` no longer doing anything?
113    pub fn is_null(&self) -> bool {
114        self.ptr.load(atomic::Ordering::Relaxed).is_null()
115    }
116
117    /// If the [`AlignmentCorrectorAllocator`] is no longer doing anything, returns the inner allocator
118    pub fn into_inner(self) -> Result<A, Self> {
119        if self.is_null() {
120            Ok(self.allocator)
121        } else {
122            Err(self)
123        }
124    }
125
126    unsafe fn get_layout(&self, mut layout: Layout) -> Layout {
127        let mut old = self.ptr.load(atomic::Ordering::Relaxed);
128        if old.is_null() {
129            return layout;
130        }
131        loop {
132            match self.ptr.compare_exchange_weak(
133                old,
134                ptr::null_mut(),
135                atomic::Ordering::SeqCst,
136                atomic::Ordering::Relaxed,
137            ) {
138                Ok(x) if !x.is_null() => {
139                    layout =
140                        // SAFETY: the layout size must be correct for this I's alignment because it the
141                        // creator of a AlignmentCorrectorAllocator ensures that.
142                        // It is now a null ptr because correcting the allocation now would be invalid
143                        // since the pointer has been recovered.
144                        unsafe { Layout::from_size_align_unchecked(layout.size(), mem::align_of::<I>()) };
145
146                    break layout;
147                }
148                Ok(_) => break layout,
149                Err(x) if x.is_null() => break layout,
150                Err(x) => old = x,
151            }
152        }
153    }
154}
155
156#[cfg(feature = "allocator_api")]
157unsafe impl<I, O, A: Allocator> Allocator for AlignmentCorrectorAllocator<I, O, A> {
158    #[inline]
159    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
160        self.allocator.allocate(layout)
161    }
162
163    #[inline]
164    fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
165        self.allocator.allocate_zeroed(layout)
166    }
167
168    #[inline]
169    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
170        // SAFETY: all conditions must be upheld by the caller
171        unsafe { self.allocator.deallocate(ptr, self.get_layout(layout)) }
172    }
173
174    #[inline]
175    unsafe fn grow(
176        &self,
177        ptr: NonNull<u8>,
178        old_layout: Layout,
179        new_layout: Layout,
180    ) -> Result<NonNull<[u8]>, AllocError> {
181        // SAFETY: all conditions must be upheld by the caller
182        unsafe {
183            self.allocator
184                .grow(ptr, self.get_layout(old_layout), new_layout)
185        }
186    }
187
188    #[inline]
189    unsafe fn grow_zeroed(
190        &self,
191        ptr: NonNull<u8>,
192        old_layout: Layout,
193        new_layout: Layout,
194    ) -> Result<NonNull<[u8]>, AllocError> {
195        // SAFETY: all conditions must be upheld by the caller
196        unsafe {
197            self.allocator
198                .grow_zeroed(ptr, self.get_layout(old_layout), new_layout)
199        }
200    }
201
202    #[inline]
203    unsafe fn shrink(
204        &self,
205        ptr: NonNull<u8>,
206        old_layout: Layout,
207        new_layout: Layout,
208    ) -> Result<NonNull<[u8]>, AllocError> {
209        // SAFETY: all conditions must be upheld by the caller
210        unsafe { self.allocator.shrink(ptr, old_layout, new_layout) }
211    }
212}
213
214#[cfg(feature = "allocator_api")]
215unsafe fn from_raw_parts<I, O, A: Allocator>(
216    old_ptr: NonNull<O>,
217    length: usize,
218    capacity: usize,
219    allocator: A,
220) -> Vec<O, AlignmentCorrectorAllocator<I, O, A>> {
221    // SAFETY: the caller uploads the constrants for from_raw_parts except for the alignment of the
222    // allocation that created the old pointer
223    unsafe {
224        Vec::<O, AlignmentCorrectorAllocator<I, O, A>>::from_raw_parts_in(
225            old_ptr.as_ptr(),
226            length,
227            capacity,
228            AlignmentCorrectorAllocator::<I, O, A>::new(old_ptr, allocator),
229        )
230    }
231}
232
233#[cfg(feature = "allocator_api")]
234/// Whether or not a copy occured.
235/// Also the copy variant doesn't have the custom allocator, and is therefore one usize smaller.
236pub enum CopyNot<I, O, A: Allocator> {
237    /// Copy occured.
238    Copy(Vec<O, A>),
239    /// There was no copy.
240    Not(Vec<O, AlignmentCorrectorAllocator<I, O, A>>),
241}
242
243#[cfg(feature = "allocator_api")]
244/// [`transmute_vec_may_copy`] but it tells you whether or not a copy occured and returns a normal
245/// Vec if it doesn't.
246pub fn transmute_vec_copy_enum<I: Pod, O: Pod, A: Allocator>(input: Vec<I, A>) -> CopyNot<I, O, A> {
247    // SAFETY: valid because
248    unsafe { transmute_vec_copy_enum_unsafe(input) }
249}
250/// [`transmute_vec_copy_enum`] but without Pod bounds.
251/// 
252/// See [`transmute_vec_unsafe`] for safety.
253#[cfg(feature = "allocator_api")]
254pub unsafe fn transmute_vec_copy_enum_unsafe<I, O, A: Allocator>(
255    input: Vec<I, A>,
256) -> CopyNot<I, O, A> {
257    // SAFETY: caller ensures this is valid
258    match unsafe { transmute_vec_unsafe(input) } {
259        Ok(x) => CopyNot::Not(x),
260        Err((old_vec, err)) => match err {
261            TransmuteError::Alignment => {
262                // I don't have to deal with ZSTs because transmute_vec will never fail with a ZST.
263                let (ptr, length, capacity, allocator) = {
264                    let mut me = ManuallyDrop::new(old_vec);
265                    (me.as_mut_ptr(), me.len(), me.capacity(), unsafe {
266                        ptr::read(me.allocator())
267                    })
268                };
269
270                // SAFETY: the ptr comes from a vec and the length is calcuated properly
271                let bytes_slice = unsafe {
272                    slice::from_raw_parts(ptr.cast::<u8>(), length * mem::size_of::<I>())
273                };
274                let mut return_vec = Vec::with_capacity_in(
275                    (length * mem::size_of::<I>()) / mem::size_of::<O>(),
276                    allocator,
277                );
278                for x in bytes_slice.chunks_exact(mem::size_of::<O>()) {
279                    // SAFETY: O is Pod and the slice is the same length as the type.
280                    // also its
281                    return_vec.push(unsafe { ptr::read_unaligned(x.as_ptr().cast()) });
282                }
283                // freeing memory
284                // SAFETY: this size and align come from a vec and the allocator is the same one
285                // that allocated the memory. i dont have to call drop because its Pod
286                unsafe {
287                    let align = mem::align_of::<I>();
288                    let size = mem::size_of::<I>() * capacity;
289                    let layout = Layout::from_size_align_unchecked(size, align);
290                    // ensuring i dont deallocate unallocated memory
291                    if size != 0 {
292                        return_vec
293                            .allocator()
294                            .deallocate(NonNull::new_unchecked(ptr.cast()), layout);
295                    }
296                };
297                CopyNot::Copy(return_vec)
298            }
299            TransmuteError::Capacity | TransmuteError::Length => {
300                // I don't have to deal with ZSTs because transmute_vec will never fail with a ZST.
301                // It is aligned because alignment errors happen first
302                let (ptr, length, capacity, allocator) = {
303                    let mut me = ManuallyDrop::new(old_vec);
304                    (me.as_mut_ptr(), me.len(), me.capacity(), unsafe {
305                        ptr::read(me.allocator())
306                    })
307                };
308                let new_length = (length * mem::size_of::<I>()) / mem::size_of::<O>();
309                let mut return_vec = Vec::with_capacity_in(new_length, allocator);
310                unsafe {
311                    // ptrs are valid for length because they came from a Vec
312                    ptr::copy_nonoverlapping(ptr as *const O, return_vec.as_mut_ptr(), new_length);
313                    // data is valid because it was just written
314                    return_vec.set_len(new_length);
315                }
316                // freeing memory
317                // SAFETY: values are from vec, except for allocator which is also the same, just now the other vec is handling it
318                unsafe { deallocate_for_vec(ptr, length, capacity, return_vec.allocator()) }
319
320                CopyNot::Copy(return_vec)
321            }
322        },
323    }
324}
325
326#[cfg(feature = "allocator_api")]
327/// Changes from any allocator to an [`AlignmentCorrectorAllocator`].
328pub fn add_alignment_allocator<I: Pod, O: Pod, A: Allocator>(
329    input: Vec<O, A>,
330) -> Vec<O, AlignmentCorrectorAllocator<I, O, A>> {
331    let (ptr, length, capacity, allocator) = {
332        let mut me = ManuallyDrop::new(input);
333        (me.as_mut_ptr(), me.len(), me.capacity(), unsafe {
334            ptr::read(me.allocator())
335        })
336    };
337
338    // SAFETY: comes directly from vec and AlignmentCorrectorAllocator::new_null
339    // doesn't interfere with the allocator within
340    unsafe {
341        Vec::from_raw_parts_in(
342            ptr,
343            length,
344            capacity,
345            AlignmentCorrectorAllocator::new_null(allocator),
346        )
347    }
348}
349
350#[cfg(feature = "allocator_api")]
351/// Removes [`AlignmentCorrectorAllocator`]s after they've been realloced.
352pub fn remove_alignment_allocator<I: Pod, O: Pod, A: Allocator>(
353    input: Vec<O, AlignmentCorrectorAllocator<I, O, A>>,
354) -> Result<Vec<O, A>, Vec<O, AlignmentCorrectorAllocator<I, O, A>>> {
355    let (ptr, length, capacity, allocator) = {
356        let mut me = ManuallyDrop::new(input);
357        (me.as_mut_ptr(), me.len(), me.capacity(), unsafe {
358            ptr::read(me.allocator())
359        })
360    };
361    unsafe {
362        match allocator.into_inner() {
363            Ok(allocator) => Ok(Vec::from_raw_parts_in(ptr, length, capacity, allocator)),
364            Err(allocator) => Err(Vec::from_raw_parts_in(ptr, length, capacity, allocator)),
365        }
366    }
367}
368
369#[cfg(feature = "allocator_api")]
370/// Same as `transmute_vec` but in case of an error it copies instead.
371/// If it's over the length it removes whatever doesn't fit.
372///
373/// You may want to use [`transmute_vec_copy_enum`].
374pub fn transmute_vec_may_copy<I: Pod, O: Pod, A: Allocator>(
375    input: Vec<I, A>,
376) -> Vec<O, AlignmentCorrectorAllocator<I, O, A>> {
377    match transmute_vec_copy_enum(input) {
378        CopyNot::Copy(x) => add_alignment_allocator(x),
379        CopyNot::Not(x) => x,
380    }
381}
382
383#[cfg(feature = "allocator_api")]
384/// Same as `transmute_vec` but in case of an error it copies instead.
385/// If it's over the length it removes whatever doesn't fit.
386///
387/// You may want to use [`transmute_vec_copy_enum`].
388/// # Safety:
389/// See [`transmute_vec_unsafe`]
390pub unsafe fn transmute_vec_may_copy_unsafe<I: Pod, O: Pod, A: Allocator>(
391    input: Vec<I, A>,
392) -> Vec<O, AlignmentCorrectorAllocator<I, O, A>> {
393    // SAFETY: caller handles safety
394    match unsafe { transmute_vec_copy_enum_unsafe(input) } {
395        CopyNot::Copy(x) => add_alignment_allocator(x),
396        CopyNot::Not(x) => x,
397    }
398}
399
400// # Safety:
401// - ptr, length, capacity, and allocator should have come from a Vec
402#[cfg(feature = "allocator_api")]
403unsafe fn deallocate_for_vec<I, A: Allocator>(
404    ptr: *mut I,
405    length: usize,
406    capacity: usize,
407    allocator: A,
408) {
409    unsafe {
410        let align = mem::align_of::<I>();
411        let size = mem::size_of::<I>() * capacity;
412        if size != 0 {
413            // ensuring i dont deallocate Vecs with 0 capacity.
414            // ptr is properly aligned and is valid for reads and writes because it came from a vec and the capacity is not zero
415            // data is valid because it was just in a vec, and the length is zero
416            ptr::drop_in_place(ptr::slice_from_raw_parts_mut(ptr, length));
417            // SAFETY: this size and align come from a vec and the allocator is the same one
418            // that allocated the memory.
419            let layout = Layout::from_size_align_unchecked(size, align);
420            allocator.deallocate(NonNull::new_unchecked(ptr.cast()), layout);
421        }
422    }
423}
424
425/// Transmute between two types of Vecs
426/// # Safety/Info
427/// - Data is dropped, use [`Vec::set_len`] if you don't want this.
428/// - `&[I]` to `&[O]` should be valid apart from length and alignment checks.
429/// # See also
430/// - [`transmute_vec`]
431/// - [`transmute_vec_copy_enum_unsafe`]
432#[cfg(feature = "allocator_api")]
433#[allow(clippy::type_complexity)]
434pub unsafe fn transmute_vec_unsafe<I, O, A: Allocator>(
435    input: Vec<I, A>,
436) -> Result<Vec<O, AlignmentCorrectorAllocator<I, O, A>>, (Vec<I, A>, TransmuteError)> {
437    let (ptr, length, capacity, allocator) = {
438        let mut me = ManuallyDrop::new(input);
439        (me.as_mut_ptr(), me.len(), me.capacity(), unsafe {
440            ptr::read(me.allocator())
441        })
442    };
443    if mem::size_of::<O>() == 0 {
444        // freeing memory
445        // SAFETY: everything came from a Vec
446        unsafe {
447            deallocate_for_vec(ptr, length, capacity, &allocator);
448        }
449
450        let mut return_vec =
451            Vec::with_capacity_in(capacity, AlignmentCorrectorAllocator::new_null(allocator));
452        unsafe { return_vec.set_len(length) };
453
454        return Ok(return_vec);
455    } else if mem::size_of::<I>() == 0 || capacity == 0 {
456        // freeing memory
457        // SAFETY: everything came from a Vec
458        unsafe {
459            deallocate_for_vec(ptr, length, capacity, &allocator);
460        }
461        return Ok(Vec::new_in(AlignmentCorrectorAllocator::new_null(
462            allocator,
463        )));
464    }
465
466    match mem::size_of::<I>().cmp(&mem::size_of::<O>()) {
467        Ordering::Greater | Ordering::Less => {
468            if ptr.align_offset(mem::align_of::<O>()) != 0 {
469                Err((
470                    // SAFETY: this came directly from a vec
471                    unsafe { Vec::from_raw_parts_in(ptr, length, capacity, allocator) },
472                    TransmuteError::Alignment,
473                ))
474            } else if (length * mem::size_of::<I>()) % mem::size_of::<O>() != 0 {
475                Err((
476                    // SAFETY: this came directly from a vec
477                    unsafe { Vec::from_raw_parts_in(ptr, length, capacity, allocator) },
478                    TransmuteError::Length,
479                ))
480            } else if (capacity * mem::size_of::<I>()) % mem::size_of::<O>() != 0 {
481                Err((
482                    // SAFETY: this came directly from a vec
483                    unsafe { Vec::from_raw_parts_in(ptr, length, capacity, allocator) },
484                    TransmuteError::Capacity,
485                ))
486            } else {
487                // SAFETY: the length and capacity of vec is corrected to be the correct size,
488                // and its not discarding bytes on the end. the alignment is also checked and on
489                // drop, the custom allocator ensures deallocation is handled properly
490                // vecs also only give out nonnull ptrs
491                Ok(unsafe {
492                    from_raw_parts(
493                        NonNull::new_unchecked(ptr).cast(),
494                        (length * mem::size_of::<I>()) / mem::size_of::<O>(),
495                        (capacity * mem::size_of::<I>()) / mem::size_of::<O>(),
496                        allocator,
497                    )
498                })
499            }
500        }
501        Ordering::Equal => {
502            if ptr.align_offset(mem::align_of::<O>()) == 0 {
503                // SAFETY: its aligned and thats all that matters
504                Ok(unsafe {
505                    from_raw_parts(
506                        NonNull::new_unchecked(ptr).cast(),
507                        length,
508                        capacity,
509                        allocator,
510                    )
511                })
512            } else {
513                Err((
514                    // SAFETY: this came directly from a vec
515                    unsafe { Vec::from_raw_parts_in(ptr, length, capacity, allocator) },
516                    TransmuteError::Alignment,
517                ))
518            }
519        }
520    }
521}
522
523/// Allows transmuting of a Vec to another vec of a different size, with 0 copies.
524/// # Example
525/// ```
526/// #![feature(allocator_api)] // this requires the allocator api because the way that this
527/// // handles deallocating hooks into the allocator api
528/// use transvec::transmute_vec;
529/// let input_vec: Vec<u16> = vec![1, 2, 3, 4, 5, 6, 7, 8];
530/// let output: Vec<u8, _> = match transmute_vec(input_vec) {
531///     Ok(x) => x,
532///     // the "transmute" can fail, if the alignment/capacity/length is incorrect
533///     // consider using `transmute_vec_may_copy`
534///     Err((old_vec, err)) => return println!("Error: {:?}", err),
535/// };
536/// if cfg!(target_endian = "big") {
537///     assert_eq!(
538///         &output,
539///         &[0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8]
540///     );
541/// } else {
542///     assert_eq!(
543///         &output,
544///         &[1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0]
545///     );
546/// }
547/// ```
548/// # Errors
549/// This errors when:
550/// 1. The length of the vector wouldn't fit the type.
551/// ```should_panic
552/// # #![feature(allocator_api)]
553/// # use transvec::transmute_vec;
554/// let input: Vec<u8> = vec![1, 2, 3];
555/// let output: Vec<u16, _> = transmute_vec(input).unwrap();
556/// ```
557/// 2. The capacity can't be converted to units of the output type.
558/// ```should_panic
559/// # #![feature(allocator_api)]
560/// # use transvec::transmute_vec;
561/// let input: Vec<u8> = Vec::with_capacity(3);
562/// let output: Vec<u16, _> = transmute_vec(input).unwrap();
563/// ```
564/// 3. The alignment of the vec is wrong.
565///
566/// Alignment, then length, then capacity will always be returned.
567/// # ZSTs
568/// 1. Anything -> ZST
569///     - Keeps length, deallocates data.
570/// 2. ZST -> Non ZST
571///     - New Vec from previous allocator.
572/// 3. Just don't do this.
573///
574/// # See also
575/// - [`transmute_vec_may_copy`] -- Infailable
576/// - [`transmute_vec_basic`] -- Returns a Vec without a specical allocator and works on stable, but only works with types with the same alignment.
577#[cfg(feature = "allocator_api")]
578pub fn transmute_vec<I: Pod, O: Pod, A: Allocator>(
579    input: Vec<I, A>,
580) -> Result<Vec<O, AlignmentCorrectorAllocator<I, O, A>>, (Vec<I, A>, TransmuteError)> {
581    unsafe { transmute_vec_unsafe(input) }
582}
583
584/// If alignment is the same this function is preferred over [`transmute_vec`].
585/// # Errors
586/// 1. The length of the vector wouldn't fit the type.
587/// ```should_panic
588/// # #![feature(allocator_api)]
589/// # use transvec::transmute_vec_basic;
590/// let input: Vec<u8> = vec![1, 2, 3];
591/// let output: Vec<u16, _> = transmute_vec_basic(input).unwrap();
592/// ```
593/// 2. The capacity can't be converted to units of the output type.
594/// ```should_panic
595/// # #![feature(allocator_api)]
596/// # use transvec::transmute_vec_basic;
597/// let input: Vec<u8> = Vec::with_capacity(3);
598/// let output: Vec<u16, _> = transmute_vec_basic(input).unwrap();
599/// ```
600/// Length, then capacity will be returned.
601/// # ZSTs
602/// 1. Anything -> ZST
603///     - Keeps length, drops data.
604/// 2. ZST -> Non ZST
605///     - New Vec from previous allocator.
606/// 3. Just don't do this.
607///
608/// # Panics
609/// Panics if the alignment is not the same.
610///
611/// Otherwise this acts exactly the same as [`transmute_vec`].
612
613pub fn transmute_vec_basic<I: Pod, O: Pod>(
614    input: Vec<I>,
615) -> Result<Vec<O>, (Vec<I>, TransmuteError)> {
616    // SAFETY: safe because of Pod trait bounds.
617    unsafe { transmute_vec_basic_unsafe(input) }
618}
619/// [`transmute_vec_basic`] but without Pod bounds.
620/// 
621/// # Safety:
622/// transmute::<I, O> for each value in your vec should be valid.
623pub unsafe fn transmute_vec_basic_unsafe<I, O>(
624    input: Vec<I>,
625) -> Result<Vec<O>, (Vec<I>, TransmuteError)> {
626    let (ptr, length, capacity) = {
627        let mut me = ManuallyDrop::new(input);
628        (me.as_mut_ptr(), me.len(), me.capacity())
629    };
630
631    if mem::align_of::<I>() != mem::align_of::<O>() {
632        panic!(
633            "Alignment of {} and {} are not the same",
634            core::any::type_name::<I>(),
635            core::any::type_name::<O>(),
636        );
637    } else if mem::size_of::<O>() == 0 {
638        // SAFETY: this came directly from a vec
639        drop(unsafe { Vec::from_raw_parts(ptr, length, capacity) });
640        let mut vec = Vec::with_capacity(capacity);
641        // SAFETY: its a zst so this is allowed
642        unsafe { vec.set_len(length) };
643        return Ok(vec);
644    } else if mem::size_of::<I>() == 0 || capacity == 0 {
645        // SAFETY: this came directly from a vec
646        drop(unsafe { Vec::from_raw_parts(ptr, length, capacity) });
647        return Ok(Vec::new());
648    }
649
650    match mem::size_of::<I>().cmp(&mem::size_of::<O>()) {
651        Ordering::Greater | Ordering::Less => {
652            if (length * mem::size_of::<I>()) % mem::size_of::<O>() != 0 {
653                Err((
654                    // SAFETY: this came directly from a vec
655                    unsafe { Vec::from_raw_parts(ptr, length, capacity) },
656                    TransmuteError::Length,
657                ))
658            } else if (capacity * mem::size_of::<I>()) % mem::size_of::<O>() != 0 {
659                Err((
660                    // SAFETY: this came directly from a vec
661                    unsafe { Vec::from_raw_parts(ptr, length, capacity) },
662                    TransmuteError::Capacity,
663                ))
664            } else {
665                // SAFETY: the length and capacity of vec is corrected to be the correct size,
666                // and its not discarding bytes on the end. the alignment is also checked.
667                Ok(unsafe {
668                    Vec::from_raw_parts(
669                        ptr.cast(),
670                        (length * mem::size_of::<I>()) / mem::size_of::<O>(),
671                        (capacity * mem::size_of::<I>()) / mem::size_of::<O>(),
672                    )
673                })
674            }
675        }
676        Ordering::Equal => {
677            // SAFETY: they are both the same size and Pod
678            Ok(unsafe {
679                Vec::from_raw_parts(
680                    ptr.cast(),
681                    (length * mem::size_of::<I>()) / mem::size_of::<O>(),
682                    (capacity * mem::size_of::<I>()) / mem::size_of::<O>(),
683                )
684            })
685        }
686    }
687}
688/// [`transmute_vec_basic_copy`] but without Pod bounds.
689pub unsafe fn transmute_vec_basic_copy_unsafe<I, O>(input: Vec<I>) -> Vec<O> {
690    match unsafe { transmute_vec_basic_unsafe(input) } {
691        Ok(x) => x,
692        Err((mut old_vec, err)) => match err {
693            TransmuteError::Alignment => unsafe { hint::unreachable_unchecked() },
694            TransmuteError::Length | TransmuteError::Capacity => {
695                let length = old_vec.len();
696                let ptr = old_vec.as_mut_ptr();
697                let mut new_vec = Vec::with_capacity(length);
698                // SAFETY: 
699                unsafe {
700                    ptr::copy_nonoverlapping(ptr, new_vec.as_mut_ptr() as *mut I, length);
701                    new_vec.set_len(length);
702                }
703                new_vec
704            }
705        },
706    }
707}
708
709/// [`transmute_vec_basic`] but on fail it copies instead.
710#[must_use = "You shouldn't use this function if you're not going to use the result as it's useless otherwise"]
711pub fn transmute_vec_basic_copy<I: Pod, O: Pod>(input: Vec<I>) -> Vec<O> {
712    match transmute_vec_basic(input) {
713        Ok(x) => x,
714        Err((old_vec, err)) => match err {
715            TransmuteError::Alignment => unsafe { hint::unreachable_unchecked() },
716            TransmuteError::Capacity | TransmuteError::Length => {
717                // I don't have to deal with ZSTs because transmute_vec will never fail with a ZST.
718                let ptr = old_vec.as_ptr();
719                let length = old_vec.len();
720                // SAFETY: the divide rounds down so the length is correct
721                unsafe {
722                    slice::from_raw_parts(
723                        ptr.cast::<O>(),
724                        (length * mem::size_of::<I>()) / mem::size_of::<O>(),
725                    )
726                }
727                .to_vec()
728            }
729        },
730    }
731}
732
733#[cfg(test)]
734mod tests {
735
736    #[cfg(feature = "allocator_api")]
737    use crate::{transmute_vec, transmute_vec_may_copy};
738    use crate::{transmute_vec_basic, transmute_vec_basic_copy, TransmuteError};
739    use alloc::{vec, vec::Vec};
740
741    #[test]
742    #[cfg(feature = "allocator_api")]
743    // It does work with the same sized types
744    fn basic_functioning() {
745        let input: Vec<u8> = vec![0, 1, 2, 3, 4, 6];
746        let output: Vec<i8, _> = match transmute_vec(input) {
747            Ok(x) => x,
748            Err(_) => return,
749        };
750        assert_eq!(&output, &[0, 1, 2, 3, 4, 6]);
751    }
752    #[test]
753    // It does work with the same sized types
754
755    fn basic_basic_functioning() {
756        let input: Vec<u8> = vec![0, 1, 2, 3, 4, 6];
757        let output: Vec<i8> = match transmute_vec_basic(input) {
758            Ok(x) => x,
759            Err(_) => return,
760        };
761        assert_eq!(&output, &[0, 1, 2, 3, 4, 6]);
762    }
763
764    #[test]
765    #[should_panic]
766    fn unalign_panic() {
767        let _ = transmute_vec_basic::<u8, u16>(Vec::new());
768    }
769
770    #[test]
771    fn different_size_same_align() {
772        let input: Vec<u8> = vec![0, 1, 2, 3, 4, 6];
773        let output: Vec<[u8; 2]> = match transmute_vec_basic(input) {
774            Ok(x) => x,
775            Err(_) => return,
776        };
777        assert_eq!(&output, &[[0, 1], [2, 3], [4, 6]])
778    }
779
780    #[test]
781    #[cfg(feature = "allocator_api")]
782    fn small_to_large() {
783        let input: Vec<u8> = vec![0, 1, 2, 3, 4, 6];
784        let output: Vec<u16, _> = match transmute_vec(input) {
785            Ok(x) => x,
786            Err(_) => return,
787        };
788        if cfg!(target_endian = "big") {
789            assert_eq!(&output, &[1, 515, 1030]);
790        } else {
791            assert_eq!(&output, &[256, 770, 1540]);
792        }
793    }
794    #[test]
795    #[cfg(feature = "allocator_api")]
796    fn large_to_small() {
797        let input: Vec<u16> = vec![1, 2, 3, 4, 5, 6, 7, 8];
798        let output: Vec<u8, _> = match transmute_vec(input) {
799            Ok(x) => x,
800            Err(_) => return,
801        };
802        if cfg!(target_endian = "big") {
803            assert_eq!(&output, &[0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8]);
804        } else {
805            assert_eq!(&output, &[1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0]);
806        }
807    }
808
809    #[test]
810    fn to_zsts_basic() {
811        let input: Vec<u8> = vec![0, 1, 2, 3, 4, 5];
812        let output: Vec<()> = match transmute_vec_basic(input) {
813            Ok(x) => x,
814            Err(_) => return,
815        };
816        assert_eq!(output.len(), 6);
817    }
818
819    #[test]
820    #[cfg(feature = "allocator_api")]
821    fn add_and_remove() {
822        let input: Vec<u16> = vec![1, 2, 3, 4, 5, 6, 7, 8];
823        let mut output: Vec<u8, _> = match transmute_vec(input) {
824            Ok(x) => x,
825            Err(_) => return,
826        };
827        output.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8, 9]);
828        for _ in 0..10 {
829            output.pop();
830        }
831        output.shrink_to_fit()
832    }
833
834    #[test]
835    #[cfg(feature = "allocator_api")]
836    fn wrong_length() {
837        let input: Vec<u8> = vec![1, 2, 3];
838        match transmute_vec::<_, u16, _>(input) {
839            Ok(_) => panic!(),
840            Err((_, err)) => match err {
841                TransmuteError::Alignment | TransmuteError::Length => (),
842                x => panic!("{:?}", x),
843            },
844        };
845    }
846
847    #[test]
848    #[cfg(feature = "allocator_api")]
849    fn wrong_length_copy() {
850        let input: Vec<u8> = vec![1, 2, 3];
851        let output: Vec<u16, _> = transmute_vec_may_copy::<_, u16, _>(input);
852        if cfg!(target_endian = "big") {
853            assert_eq!(&output, &[258]);
854        } else {
855            assert_eq!(&output, &[513]);
856        }
857    }
858
859    #[test]
860    #[cfg(feature = "allocator_api")]
861    fn may_copy() {
862        let input: Vec<u8> = vec![1, 2];
863        let output: Vec<u16, _> = transmute_vec_may_copy::<_, u16, _>(input);
864        if cfg!(target_endian = "big") {
865            assert_eq!(&output, &[258]);
866        } else {
867            assert_eq!(&output, &[513]);
868        }
869    }
870
871    #[test]
872    fn basic_copy() {
873        let input: Vec<u8> = vec![1, 2];
874        let output: Vec<[u8; 2]> = transmute_vec_basic_copy(input);
875        assert_eq!(&output, &[[1, 2]]);
876    }
877
878    #[test]
879    #[cfg(feature = "allocator_api")]
880    fn from_zsts() {
881        let input = vec![(), (), ()];
882        let output: Vec<u8, _> = match transmute_vec(input) {
883            Ok(x) => x,
884            Err(_) => return,
885        };
886        assert_eq!(output.len(), 0);
887    }
888
889    #[test]
890    #[cfg(feature = "allocator_api")]
891    fn to_zsts() {
892        let input: Vec<u8> = vec![0, 1, 2, 3, 4, 5];
893        let output: Vec<(), _> = match transmute_vec(input) {
894            Ok(x) => x,
895            Err(_) => return,
896        };
897        assert_eq!(output.len(), 6);
898    }
899}