use std::alloc::{Layout, alloc_zeroed, dealloc, handle_alloc_error};
use std::fmt;
use std::ptr::NonNull;
use std::slice;
pub struct AlignedBuffer {
pointer: NonNull<u8>,
layout: Layout,
}
impl AlignedBuffer {
#[must_use]
pub fn zeroed(len: usize, align: usize) -> Self {
let layout = Layout::from_size_align(len, align)
.expect("an alignment that is a power of two, and a size that does not overflow");
if len == 0 {
let pointer = NonNull::new(align as *mut u8).expect("a non-zero alignment");
return Self { pointer, layout };
}
let raw = unsafe { alloc_zeroed(layout) };
let Some(pointer) = NonNull::new(raw) else {
handle_alloc_error(layout);
};
Self { pointer, layout }
}
#[must_use]
pub fn from_bytes(bytes: &[u8], align: usize) -> Self {
let mut buffer = Self::zeroed(bytes.len(), align);
buffer.as_mut_slice().copy_from_slice(bytes);
buffer
}
#[must_use]
pub fn len(&self) -> usize {
self.layout.size()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn align(&self) -> usize {
self.layout.align()
}
#[must_use]
pub fn as_slice(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.pointer.as_ptr(), self.layout.size()) }
}
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.pointer.as_ptr(), self.layout.size()) }
}
#[must_use]
pub fn as_ptr(&self) -> *const u8 {
self.pointer.as_ptr()
}
#[must_use]
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.pointer.as_ptr()
}
}
impl Drop for AlignedBuffer {
fn drop(&mut self) {
if self.layout.size() == 0 {
return;
}
unsafe { dealloc(self.pointer.as_ptr(), self.layout) };
}
}
impl Clone for AlignedBuffer {
fn clone(&self) -> Self {
Self::from_bytes(self.as_slice(), self.align())
}
}
impl fmt::Debug for AlignedBuffer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AlignedBuffer")
.field("len", &self.len())
.field("align", &self.align())
.finish_non_exhaustive()
}
}
impl PartialEq for AlignedBuffer {
fn eq(&self, other: &Self) -> bool {
self.align() == other.align() && self.as_slice() == other.as_slice()
}
}
impl Eq for AlignedBuffer {}
unsafe impl Send for AlignedBuffer {}
unsafe impl Sync for AlignedBuffer {}
#[cfg(test)]
mod tests;