pub const ALIGN: usize = 64;
pub struct AlignedVec<T: Default + Clone> {
storage: Vec<T>,
offset: usize,
len: usize,
}
impl<T: Default + Clone> AlignedVec<T> {
#[must_use]
pub fn new_aligned(len: usize) -> Self {
assert!(len > 0, "AlignedVec: len must be > 0");
let elem_size = std::mem::size_of::<T>().max(1);
let extra = ALIGN.div_ceil(elem_size);
let alloc_len = len.checked_add(extra).expect("AlignedVec: size overflow");
let storage: Vec<T> = vec![T::default(); alloc_len];
let base_ptr = storage.as_ptr() as usize;
let offset = if base_ptr % ALIGN == 0 {
0
} else {
let byte_gap = ALIGN - (base_ptr % ALIGN);
byte_gap.div_ceil(elem_size)
};
assert!(
offset + len <= alloc_len,
"AlignedVec: alignment offset calculation overflow (offset={offset}, len={len}, alloc={alloc_len})",
);
Self {
storage,
offset,
len,
}
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.len
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
#[must_use]
pub fn as_slice(&self) -> &[T] {
&self.storage[self.offset..self.offset + self.len]
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.storage[self.offset..self.offset + self.len]
}
#[inline]
#[must_use]
pub fn as_ptr(&self) -> *const T {
self.as_slice().as_ptr()
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut T {
self.as_mut_slice().as_mut_ptr()
}
}
impl<T: Default + Clone> Clone for AlignedVec<T> {
fn clone(&self) -> Self {
let mut new_vec = AlignedVec::new_aligned(self.len);
new_vec.as_mut_slice().clone_from_slice(self.as_slice());
new_vec
}
}
impl<T: Default + Clone + std::fmt::Debug> std::fmt::Debug for AlignedVec<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("AlignedVec")
.field("len", &self.len)
.field("data_ptr", &format_args!("{:#x}", self.as_ptr() as usize))
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn data_pointer_is_64_byte_aligned() {
for len in [1_usize, 4, 7, 16, 63, 64, 65, 128, 256, 1024, 4097] {
let av = AlignedVec::<f32>::new_aligned(len);
let ptr = av.as_ptr() as usize;
assert_eq!(
ptr % 64,
0,
"pointer 0x{ptr:x} not 64-byte aligned for len={len}"
);
}
}
#[test]
fn zero_initialised() {
let av = AlignedVec::<u8>::new_aligned(128);
assert!(av.as_slice().iter().all(|&b| b == 0));
let av_f32 = AlignedVec::<f32>::new_aligned(32);
assert!(av_f32.as_slice().iter().all(|&v| v == 0.0_f32));
}
#[test]
fn len_is_correct() {
let av = AlignedVec::<u32>::new_aligned(100);
assert_eq!(av.len(), 100);
assert!(!av.is_empty());
}
#[test]
fn mutable_write_and_read() {
let mut av = AlignedVec::<u8>::new_aligned(4);
av.as_mut_slice().copy_from_slice(&[10, 20, 30, 40]);
assert_eq!(av.as_slice(), &[10_u8, 20, 30, 40]);
}
#[test]
fn clone_is_independent_and_aligned() {
let mut av = AlignedVec::<u32>::new_aligned(8);
for (i, v) in av.as_mut_slice().iter_mut().enumerate() {
*v = i as u32;
}
let av2 = av.clone();
assert_eq!(av.as_slice(), av2.as_slice());
av.as_mut_slice()[0] = 99;
assert_eq!(av2.as_slice()[0], 0_u32);
assert_eq!(av.as_ptr() as usize % 64, 0);
assert_eq!(av2.as_ptr() as usize % 64, 0);
}
#[test]
fn alignment_for_different_types() {
let av_u8 = AlignedVec::<u8>::new_aligned(64);
assert_eq!(av_u8.as_ptr() as usize % 64, 0);
let av_f64 = AlignedVec::<f64>::new_aligned(32);
assert_eq!(av_f64.as_ptr() as usize % 64, 0);
let av_i16 = AlignedVec::<i16>::new_aligned(128);
assert_eq!(av_i16.as_ptr() as usize % 64, 0);
}
#[test]
fn alignment_stress() {
for n in 1..=200_usize {
let av = AlignedVec::<f32>::new_aligned(n);
assert_eq!(av.as_ptr() as usize % 64, 0, "failed for n={n}");
}
}
#[test]
fn mut_ptr_same_address() {
let mut av = AlignedVec::<u8>::new_aligned(16);
let const_ptr = av.as_ptr() as usize;
let mut_ptr = av.as_mut_ptr() as usize;
assert_eq!(const_ptr, mut_ptr);
}
#[test]
fn debug_does_not_panic() {
let av = AlignedVec::<u32>::new_aligned(4);
let _ = format!("{av:?}");
}
#[test]
fn large_allocation_aligned() {
let av = AlignedVec::<f64>::new_aligned(1_024 * 1_024); assert_eq!(av.as_ptr() as usize % 64, 0);
assert_eq!(av.len(), 1_024 * 1_024);
}
}