transmute_bytes/
transmute.rs

1use crate::FromBytes;
2use std::{
3    borrow::Cow,
4    mem::{self, size_of},
5    slice,
6};
7
8fn is_aligned_slice<T>(slice: &[u8]) -> bool {
9    slice.as_ptr() as usize % mem::align_of::<T>() == 0
10}
11
12unsafe fn transmute_borrowed<T: FromBytes>(bytes: &[u8]) -> &[T] {
13    slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len() / size_of::<T>())
14}
15
16fn transmute_owned<T: FromBytes>(bytes: &[u8]) -> Vec<T> {
17    (0..bytes.len())
18        .step_by(size_of::<T>())
19        .map(|i| {
20            let size = size_of::<T>();
21            let bytes = if i + size >= bytes.len() {
22                &bytes[i..]
23            } else {
24                &bytes[i..size]
25            };
26            // SAFETY: `bytes.len()` is less than or equal to `size_of::<T>()`
27            unsafe { T::from_bytes(bytes).unwrap_unchecked() }
28        })
29        .collect()
30}
31
32pub fn transmute_bytes<T: Clone + FromBytes>(bytes: &impl AsRef<[u8]>) -> Cow<'_, [T]> {
33    let bytes = bytes.as_ref();
34    if is_aligned_slice::<T>(bytes) && bytes.len() % size_of::<T>() == 0 {
35        // SAFETY: `bytes` is aligned and size is a multiple of `size_of::<T>()`
36        unsafe { transmute_borrowed(bytes).into() }
37    } else {
38        transmute_owned(bytes).into()
39    }
40}