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)]
5extern 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#[derive(Debug, PartialEq, Eq)]
60pub enum TransmuteError {
61 Alignment,
63 Length,
65 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#[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 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 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 pub fn is_null(&self) -> bool {
114 self.ptr.load(atomic::Ordering::Relaxed).is_null()
115 }
116
117 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 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 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 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 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 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 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")]
234pub enum CopyNot<I, O, A: Allocator> {
237 Copy(Vec<O, A>),
239 Not(Vec<O, AlignmentCorrectorAllocator<I, O, A>>),
241}
242
243#[cfg(feature = "allocator_api")]
244pub fn transmute_vec_copy_enum<I: Pod, O: Pod, A: Allocator>(input: Vec<I, A>) -> CopyNot<I, O, A> {
247 unsafe { transmute_vec_copy_enum_unsafe(input) }
249}
250#[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 match unsafe { transmute_vec_unsafe(input) } {
259 Ok(x) => CopyNot::Not(x),
260 Err((old_vec, err)) => match err {
261 TransmuteError::Alignment => {
262 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 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 return_vec.push(unsafe { ptr::read_unaligned(x.as_ptr().cast()) });
282 }
283 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 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 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 ptr::copy_nonoverlapping(ptr as *const O, return_vec.as_mut_ptr(), new_length);
313 return_vec.set_len(new_length);
315 }
316 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")]
327pub 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 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")]
351pub 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")]
370pub 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")]
384pub 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 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#[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 ptr::drop_in_place(ptr::slice_from_raw_parts_mut(ptr, length));
417 let layout = Layout::from_size_align_unchecked(size, align);
420 allocator.deallocate(NonNull::new_unchecked(ptr.cast()), layout);
421 }
422 }
423}
424
425#[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 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 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 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 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 unsafe { Vec::from_raw_parts_in(ptr, length, capacity, allocator) },
484 TransmuteError::Capacity,
485 ))
486 } else {
487 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 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 unsafe { Vec::from_raw_parts_in(ptr, length, capacity, allocator) },
516 TransmuteError::Alignment,
517 ))
518 }
519 }
520 }
521}
522
523#[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
584pub fn transmute_vec_basic<I: Pod, O: Pod>(
614 input: Vec<I>,
615) -> Result<Vec<O>, (Vec<I>, TransmuteError)> {
616 unsafe { transmute_vec_basic_unsafe(input) }
618}
619pub 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 drop(unsafe { Vec::from_raw_parts(ptr, length, capacity) });
640 let mut vec = Vec::with_capacity(capacity);
641 unsafe { vec.set_len(length) };
643 return Ok(vec);
644 } else if mem::size_of::<I>() == 0 || capacity == 0 {
645 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 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 unsafe { Vec::from_raw_parts(ptr, length, capacity) },
662 TransmuteError::Capacity,
663 ))
664 } else {
665 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 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}
688pub 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 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#[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 let ptr = old_vec.as_ptr();
719 let length = old_vec.len();
720 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 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 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}