use std::alloc::{self, Layout};
use std::ptr::NonNull;
use std::slice;
pub(crate) struct AlignedBuffer {
ptr: NonNull<u8>,
layout: Layout,
}
unsafe impl Send for AlignedBuffer {}
unsafe impl Sync for AlignedBuffer {}
impl AlignedBuffer {
pub(crate) fn new_zeroed(size: usize, align: usize) -> Self {
let layout = match Layout::from_size_align(size, align) {
Ok(layout) => layout,
Err(_) => alloc::handle_alloc_error(
Layout::new::<u8>(),
),
};
let raw = unsafe { alloc::alloc_zeroed(layout) };
let ptr = match NonNull::new(raw) {
Some(ptr) => ptr,
None => alloc::handle_alloc_error(layout),
};
Self { ptr, layout }
}
#[inline]
pub(crate) fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.layout.size()) }
}
#[inline]
pub(crate) fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.layout.size()) }
}
}
impl Drop for AlignedBuffer {
fn drop(&mut self) {
unsafe { alloc::dealloc(self.ptr.as_ptr(), self.layout) }
}
}
impl Clone for AlignedBuffer {
fn clone(&self) -> Self {
let mut copy = Self::new_zeroed(self.layout.size(), self.layout.align());
copy.as_mut_slice().copy_from_slice(self.as_slice());
copy
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used)]
use super::*;
#[test]
fn test_aligned_buffer_is_aligned_and_zeroed() {
let buf = AlignedBuffer::new_zeroed(8192, 4096);
assert_eq!(buf.as_slice().len(), 8192);
assert_eq!(buf.as_slice().as_ptr() as usize % 4096, 0);
assert!(buf.as_slice().iter().all(|&b| b == 0));
}
#[test]
fn test_aligned_buffer_clone_is_independent() {
let mut a = AlignedBuffer::new_zeroed(4096, 4096);
a.as_mut_slice()[0] = 0xAB;
let b = a.clone();
a.as_mut_slice()[0] = 0xCD;
assert_eq!(b.as_slice()[0], 0xAB);
assert_eq!(a.as_slice()[0], 0xCD);
}
}