use alloc::alloc::{Layout, LayoutError, alloc, dealloc};
use core::error::Error;
use core::ptr::NonNull;
use core::{fmt, slice};
#[derive(Debug)]
pub struct AlignedBuffer {
ptr: NonNull<u8>,
layout: Layout,
}
impl AlignedBuffer {
pub fn from_size_align(len: usize, alignment: usize) -> Result<Self, LayoutError> {
let layout = Layout::from_size_align(len, alignment)?;
Ok(Self::from_layout(layout))
}
#[must_use]
pub fn from_layout(layout: Layout) -> Self {
let ptr = unsafe { alloc(layout) };
let ptr = NonNull::new(ptr).expect("Allocation failed");
Self { ptr, layout }
}
#[must_use]
pub const fn ptr(&self) -> *const u8 {
self.ptr.as_ptr().cast_const()
}
#[must_use]
pub const fn ptr_mut(&mut self) -> *mut u8 {
self.ptr.as_ptr()
}
#[must_use]
pub const fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.ptr(), self.size()) }
}
#[must_use]
pub const fn as_slice_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.ptr_mut(), self.size()) }
}
#[must_use]
pub const fn size(&self) -> usize {
self.layout.size()
}
pub fn iter(&self) -> impl Iterator<Item = &u8> {
self.as_slice().iter()
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut u8> {
self.as_slice_mut().iter_mut()
}
pub fn copy_from_slice(&mut self, src: &[u8]) {
assert_eq!(self.size(), src.len());
unsafe {
self.ptr_mut().copy_from(src.as_ptr(), src.len());
}
}
pub fn copy_from_iter(&mut self, src: impl Iterator<Item = u8>) {
self.iter_mut()
.zip(src)
.for_each(|(dst, src_byte)| *dst = src_byte);
}
pub fn check_alignment(&self, required_alignment: usize) -> Result<(), AlignmentError> {
if !(self.ptr() as usize).is_multiple_of(required_alignment) {
return Err(AlignmentError); }
Ok(())
}
}
impl Drop for AlignedBuffer {
fn drop(&mut self) {
unsafe {
dealloc(self.ptr_mut(), self.layout);
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AlignmentError;
impl Error for AlignmentError {}
impl fmt::Display for AlignmentError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Buffer alignment does not fulfill requirements.")
}
}
#[cfg(test)]
mod tests {
use super::AlignedBuffer;
#[test]
fn test_invalid_arguments() {
for request_alignment in [0, 3, 5, 7, 9] {
for request_len in [1, 32, 64, 128, 1024] {
assert!(AlignedBuffer::from_size_align(request_len, request_alignment).is_err());
}
}
}
#[test]
fn test_allocation_alignment() {
for request_alignment in [1, 2, 4, 8, 16, 32, 64, 128] {
for request_len in [1_usize, 32, 64, 128, 1024] {
let buffer =
AlignedBuffer::from_size_align(request_len, request_alignment).unwrap();
assert_eq!(buffer.ptr() as usize % request_alignment, 0);
assert_eq!(buffer.size(), request_len);
}
}
}
#[test]
fn test_copy_from_iter() {
let src8: [u8; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
{
let mut bfr = AlignedBuffer::from_size_align(8, 8).unwrap();
bfr.copy_from_iter(src8.iter().cloned());
assert_eq!(bfr.as_slice(), src8);
}
{
let mut bfr = AlignedBuffer::from_size_align(7, 8).unwrap();
bfr.copy_from_iter(src8.iter().cloned());
assert_eq!(bfr.as_slice(), [1, 2, 3, 4, 5, 6, 7]);
}
{
let mut bfr = AlignedBuffer::from_size_align(9, 8).unwrap();
bfr.iter_mut().for_each(|dst| *dst = 0); bfr.copy_from_iter(src8.iter().cloned());
assert_eq!(bfr.as_slice(), [1, 2, 3, 4, 5, 6, 7, 8, 0]);
}
}
}