use core::alloc::Layout;
use core::ptr::NonNull;
use core::slice;
pub struct AlignedBuf {
ptr: NonNull<u8>,
len: usize,
capacity: usize,
alignment: usize,
}
#[expect(
unsafe_code,
reason = "raw-pointer wrapper; Send/Sync soundness justified"
)]
unsafe impl Send for AlignedBuf {}
#[expect(
unsafe_code,
reason = "raw-pointer wrapper; Send/Sync soundness justified"
)]
unsafe impl Sync for AlignedBuf {}
impl AlignedBuf {
#[must_use]
pub fn new_zeroed(capacity: usize, alignment: usize) -> Option<Self> {
if !alignment.is_power_of_two() {
return None;
}
if alignment > (isize::MAX as usize) {
return None;
}
let rounded = capacity.checked_add(alignment - 1)? & !(alignment - 1);
if rounded > (isize::MAX as usize) {
return None;
}
if rounded == 0 {
let dangling = {
#[expect(unsafe_code, reason = "non-null dangling for 0-cap buffer")]
unsafe {
NonNull::new_unchecked(core::ptr::without_provenance_mut::<u8>(alignment))
}
};
return Some(Self {
ptr: dangling,
len: 0,
capacity: 0,
alignment,
});
}
let layout = Layout::from_size_align(rounded, alignment).ok()?;
#[expect(unsafe_code, reason = "global allocator call with validated layout")]
let raw = unsafe { alloc::alloc::alloc_zeroed(layout) };
let ptr = NonNull::new(raw)?;
Some(Self {
ptr,
len: 0,
capacity: rounded,
alignment,
})
}
#[must_use]
pub const fn len(&self) -> usize {
self.len
}
#[must_use]
pub const fn capacity(&self) -> usize {
self.capacity
}
#[must_use]
pub const fn alignment(&self) -> usize {
self.alignment
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.len == 0
}
#[must_use]
pub const fn as_ptr(&self) -> *const u8 {
self.ptr.as_ptr().cast_const()
}
#[must_use]
pub const fn as_mut_ptr(&mut self) -> *mut u8 {
self.ptr.as_ptr()
}
#[must_use]
pub const fn as_slice(&self) -> &[u8] {
#[expect(unsafe_code, reason = "slice over owned aligned allocation")]
unsafe {
slice::from_raw_parts(self.ptr.as_ptr(), self.len)
}
}
#[must_use]
pub const fn as_capacity_mut(&mut self) -> &mut [u8] {
#[expect(unsafe_code, reason = "mut slice over owned aligned allocation")]
unsafe {
slice::from_raw_parts_mut(self.ptr.as_ptr(), self.capacity)
}
}
pub const fn set_len(&mut self, new_len: usize) {
assert!(
new_len <= self.capacity,
"AlignedBuf::set_len exceeds capacity",
);
self.len = new_len;
}
pub const fn clear(&mut self) {
self.len = 0;
}
}
impl Drop for AlignedBuf {
fn drop(&mut self) {
if self.capacity == 0 {
return;
}
let Ok(layout) = Layout::from_size_align(self.capacity, self.alignment) else {
return;
};
#[expect(unsafe_code, reason = "matched dealloc for owned allocation")]
unsafe {
alloc::alloc::dealloc(self.ptr.as_ptr(), layout);
}
}
}
#[cfg(test)]
#[expect(clippy::unwrap_used, reason = "test assertions")]
mod tests;