use std::borrow::Cow;
pub unsafe trait LeBytes: Copy {
const SIZE: usize;
fn from_le_slice(bytes: &[u8]) -> Self;
}
macro_rules! impl_le_bytes {
($($t:ty),* $(,)?) => {$(
unsafe impl LeBytes for $t {
const SIZE: usize = std::mem::size_of::<$t>();
#[inline]
fn from_le_slice(bytes: &[u8]) -> Self {
<$t>::from_le_bytes(bytes.try_into().expect("from_le_slice: wrong width"))
}
}
)*};
}
impl_le_bytes!(i8, u8, i16, u16, i32, u32, i64, u64, f32, f64);
#[inline]
pub fn decode_le<T: LeBytes>(data: &[u8]) -> Cow<'_, [T]> {
let n = data.len() / T::SIZE;
if data.as_ptr().align_offset(std::mem::align_of::<T>()) == 0 {
return Cow::Borrowed(unsafe { std::slice::from_raw_parts(data.as_ptr().cast::<T>(), n) });
}
Cow::Owned(
data.chunks_exact(T::SIZE)
.map(|c| T::from_le_slice(c))
.collect(),
)
}
#[inline]
pub fn decode_le_vec<T: LeBytes>(data: &[u8]) -> Vec<T> {
decode_le::<T>(data).into_owned()
}
pub const ALIGNED_BYTES_ALIGN: usize = 64;
#[repr(C, align(64))]
#[derive(Clone, Copy)]
struct Chunk([u8; ALIGNED_BYTES_ALIGN]);
pub struct AlignedBytes {
chunks: Vec<Chunk>,
len: usize,
}
impl AlignedBytes {
pub fn zeroed(len: usize) -> Self {
let n_chunks = len.div_ceil(ALIGNED_BYTES_ALIGN);
Self {
chunks: vec![Chunk([0u8; ALIGNED_BYTES_ALIGN]); n_chunks],
len,
}
}
pub fn from_slice(data: &[u8]) -> Self {
let mut out = Self::zeroed(data.len());
out.as_mut_slice().copy_from_slice(data);
out
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.chunks.as_ptr().cast::<u8>(), self.len) }
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.chunks.as_mut_ptr().cast::<u8>(), self.len) }
}
pub fn as_slice_of<T: LeBytes>(&self) -> &[T] {
debug_assert!(std::mem::align_of::<T>() <= ALIGNED_BYTES_ALIGN);
unsafe { std::slice::from_raw_parts(self.chunks.as_ptr().cast::<T>(), self.len / T::SIZE) }
}
pub fn slice_of<T: LeBytes>(&self, offset: usize, len_bytes: usize) -> &[T] {
let bytes = &self.as_slice()[offset..offset + len_bytes];
assert_eq!(
offset % std::mem::align_of::<T>(),
0,
"AlignedBytes::slice_of: offset {offset} is not {}-aligned",
std::mem::align_of::<T>()
);
unsafe { std::slice::from_raw_parts(bytes.as_ptr().cast::<T>(), len_bytes / T::SIZE) }
}
pub fn slice_of_mut<T: LeBytes>(&mut self, offset: usize, len_bytes: usize) -> &mut [T] {
assert_eq!(
offset % std::mem::align_of::<T>(),
0,
"AlignedBytes::slice_of_mut: offset {offset} is not {}-aligned",
std::mem::align_of::<T>()
);
let bytes = &mut self.as_mut_slice()[offset..offset + len_bytes];
unsafe {
std::slice::from_raw_parts_mut(bytes.as_mut_ptr().cast::<T>(), len_bytes / T::SIZE)
}
}
}
impl Clone for AlignedBytes {
fn clone(&self) -> Self {
Self {
chunks: self.chunks.clone(),
len: self.len,
}
}
}
impl std::fmt::Debug for AlignedBytes {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AlignedBytes")
.field("len", &self.len)
.finish()
}
}
impl std::ops::Deref for AlignedBytes {
type Target = [u8];
fn deref(&self) -> &[u8] {
self.as_slice()
}
}
impl std::ops::DerefMut for AlignedBytes {
fn deref_mut(&mut self) -> &mut [u8] {
self.as_mut_slice()
}
}
impl From<&[u8]> for AlignedBytes {
fn from(v: &[u8]) -> Self {
Self::from_slice(v)
}
}
impl From<Vec<u8>> for AlignedBytes {
fn from(v: Vec<u8>) -> Self {
Self::from_slice(&v)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decode_le_agrees_on_aligned_and_misaligned_input() {
let vals: Vec<f32> = (0..64).map(|i| i as f32 * 0.5 - 3.0).collect();
let n_bytes = vals.len() * 4;
for skew in 0..4usize {
let mut bytes = vec![0u8; skew];
for v in &vals {
bytes.extend_from_slice(&v.to_le_bytes());
}
let got = decode_le::<f32>(&bytes[skew..skew + n_bytes]);
assert_eq!(got.as_ref(), vals.as_slice(), "skew {skew}");
}
}
#[test]
fn decode_le_borrows_when_aligned_and_copies_when_not() {
let mut bytes = vec![0u8; 1];
bytes.extend_from_slice(&1.0f32.to_le_bytes());
assert!(matches!(decode_le::<f32>(&bytes[1..5]), Cow::Owned(_)));
let aligned: Vec<u8> = 2.0f32.to_le_bytes().to_vec();
assert!(matches!(decode_le::<f32>(&aligned), Cow::Borrowed(_)));
}
#[test]
fn decode_le_drops_trailing_partial_element() {
let bytes = [1u8, 0, 0, 0, 7, 7];
assert_eq!(decode_le::<f32>(&bytes).len(), 1);
assert_eq!(decode_le::<f32>(&[]).len(), 0);
}
#[test]
fn aligned_bytes_base_is_over_aligned() {
for len in [0usize, 1, 7, 64, 65, 4096] {
let b = AlignedBytes::zeroed(len);
assert_eq!(b.len(), len);
assert_eq!(
b.as_slice().as_ptr() as usize % ALIGNED_BYTES_ALIGN,
0,
"len {len}"
);
assert!(b.as_slice().iter().all(|&x| x == 0));
}
}
#[test]
fn aligned_bytes_round_trips_typed_slices() {
let vals: Vec<f64> = vec![1.5, -2.25, 1e300];
let mut b = AlignedBytes::zeroed(vals.len() * 8);
b.slice_of_mut::<f64>(0, vals.len() * 8)
.copy_from_slice(&vals);
assert_eq!(b.slice_of::<f64>(0, vals.len() * 8), vals.as_slice());
assert_eq!(b.as_slice_of::<f64>(), vals.as_slice());
assert_eq!(b.slice_of::<f64>(8, 8), &vals[1..2]);
}
#[test]
#[should_panic(expected = "not 8-aligned")]
fn aligned_bytes_rejects_unaligned_offset() {
let b = AlignedBytes::zeroed(64);
let _ = b.slice_of::<f64>(4, 8);
}
}