#[cfg(feature = "alloc")]
extern crate alloc;
use core::ptr::NonNull;
#[cfg(feature = "alloc")]
use alloc::alloc::{alloc, alloc_zeroed, dealloc, Layout};
pub const ALIGN: usize = 64;
#[cfg(feature = "alloc")]
pub struct AlignedBuffer {
ptr: NonNull<u8>,
len: usize,
cap: usize, }
#[cfg(feature = "alloc")]
impl AlignedBuffer {
pub(crate) fn new_uninit(len: usize) -> Self {
if len == 0 {
return Self {
ptr: NonNull::dangling(),
len: 0,
cap: 0,
};
}
let cap = Self::padded_cap(len);
let layout = Self::layout(cap);
let ptr = unsafe { alloc(layout) };
let ptr = NonNull::new(ptr).expect("allocation failed");
Self { ptr, len, cap }
}
pub fn zeroed(len: usize) -> Self {
if len == 0 {
return Self {
ptr: NonNull::dangling(),
len: 0,
cap: 0,
};
}
let cap = Self::padded_cap(len);
let layout = Self::layout(cap);
let ptr = unsafe { alloc_zeroed(layout) };
let ptr = NonNull::new(ptr).expect("allocation failed");
Self { ptr, len, cap }
}
pub fn from_slice(src: &[u8]) -> Self {
let mut buf = Self::new_uninit(src.len());
if !src.is_empty() {
unsafe {
core::ptr::copy_nonoverlapping(src.as_ptr(), buf.as_mut_ptr(), src.len());
}
}
buf
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn as_ptr(&self) -> *const u8 {
self.ptr.as_ptr()
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.ptr.as_ptr()
}
#[inline]
pub fn as_slice(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
#[cfg(feature = "alloc")]
pub fn to_vec(&self) -> alloc::vec::Vec<u8> {
self.as_slice().to_vec()
}
#[cfg(feature = "alloc")]
pub fn into_vec(self) -> alloc::vec::Vec<u8> {
self.as_slice().to_vec()
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
}
pub fn fill(&mut self, val: u8) {
self.as_mut_slice().fill(val);
}
pub fn zero(&mut self) {
self.fill(0);
}
#[inline]
fn padded_cap(len: usize) -> usize {
len.checked_add(ALIGN - 1)
.map(|value| value & !(ALIGN - 1))
.expect("AlignedBuffer capacity overflow")
}
#[inline]
fn layout(cap: usize) -> Layout {
Layout::from_size_align(cap, ALIGN).expect("AlignedBuffer layout overflow")
}
}
#[cfg(feature = "alloc")]
impl core::ops::Deref for AlignedBuffer {
type Target = [u8];
#[inline]
fn deref(&self) -> &[u8] {
self.as_slice()
}
}
#[cfg(feature = "alloc")]
impl core::ops::DerefMut for AlignedBuffer {
#[inline]
fn deref_mut(&mut self) -> &mut [u8] {
self.as_mut_slice()
}
}
#[cfg(feature = "alloc")]
impl Drop for AlignedBuffer {
fn drop(&mut self) {
if self.cap > 0 {
let layout = Self::layout(self.cap);
unsafe { dealloc(self.ptr.as_ptr(), layout) };
}
}
}
#[cfg(feature = "alloc")]
unsafe impl Send for AlignedBuffer {}
#[cfg(feature = "alloc")]
unsafe impl Sync for AlignedBuffer {}
#[cfg(feature = "alloc")]
impl core::fmt::Debug for AlignedBuffer {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"AlignedBuffer {{ len: {}, align: {ALIGN}, ptr: {:p} }}",
self.len,
self.as_ptr()
)
}
}
#[cfg(feature = "alloc")]
impl Clone for AlignedBuffer {
fn clone(&self) -> Self {
Self::from_slice(self.as_slice())
}
}
#[cfg(test)]
#[cfg(feature = "alloc")]
mod tests {
use super::*;
#[test]
fn alignment_guarantee() {
for len in [1usize, 15, 16, 63, 64, 65, 1024, 65536] {
let buf = AlignedBuffer::zeroed(len);
assert_eq!(
buf.as_ptr() as usize % ALIGN,
0,
"len={len}: pointer not {ALIGN}-byte aligned"
);
assert_eq!(buf.len(), len);
}
}
#[test]
fn zero_size() {
let buf = AlignedBuffer::zeroed(0);
assert!(buf.is_empty());
assert_eq!(buf.len(), 0);
}
#[test]
#[should_panic(expected = "AlignedBuffer capacity overflow")]
fn oversized_length_panics_before_allocation() {
let _ = AlignedBuffer::zeroed(usize::MAX);
}
#[test]
fn from_slice_roundtrip() {
let data: Vec<u8> = (0u8..128).collect();
let buf = AlignedBuffer::from_slice(&data);
assert_eq!(buf.as_ptr() as usize % ALIGN, 0);
assert_eq!(buf.as_slice(), data.as_slice());
}
#[test]
fn clone_is_independent() {
let mut a = AlignedBuffer::from_slice(&[1u8, 2, 3, 4]);
let b = a.clone();
a.as_mut_slice()[0] = 0xFF;
assert_eq!(b.as_slice()[0], 1); }
#[test]
fn deref_works_with_kernel() {
let x = AlignedBuffer::from_slice(&[0x42u8; 64]);
let mut y = AlignedBuffer::zeroed(64);
crate::kernel::axpy(0x03, &x, &mut y);
let mut y_ref = vec![0u8; 64];
crate::kernel::scalar::axpy(0x03, &x, &mut y_ref);
assert_eq!(y.as_slice(), y_ref.as_slice());
}
}