use crate::alignment::Alignment;
use cfg_if::cfg_if;
use std::ptr::NonNull;
mod cmp;
mod multiple;
#[doc(inline)]
#[allow(unreachable_pub)] pub use cmp::*;
cfg_if! {
if #[cfg(feature = "simd")] {
mod simd;
#[doc(inline)]
#[allow(unreachable_pub)] pub use simd::*;
}
}
pub struct AlignedBytes<A: Alignment> {
bytes_ptr: std::ptr::NonNull<u8>,
size: usize,
phantom: std::marker::PhantomData<A>,
}
impl<A: Alignment> AlignedBytes<A> {
fn get_layout(size: usize) -> std::alloc::Layout {
std::alloc::Layout::from_size_align(size, A::size()).unwrap()
}
#[inline]
#[must_use]
pub unsafe fn new(size: usize) -> Self {
Self::new_impl(size)
}
fn new_impl(size: usize) -> Self {
if size > (isize::MAX as usize) {
panic!("cannot allocate more than `isize::MAX` bytes, attempted to allocate {size}");
}
let layout = Self::get_layout(size);
let raw_ptr = unsafe { std::alloc::alloc(layout) };
let ptr = std::ptr::NonNull::new(raw_ptr).unwrap();
Self {
bytes_ptr: ptr,
size,
phantom: std::marker::PhantomData {},
}
}
#[inline]
pub fn new_initialize<F>(size: usize, mut f: F) -> Self
where
F: FnMut(usize) -> u8,
{
let mut block = unsafe { Self::new(size) };
for i in 0..block.size {
block[i] = f(i);
}
block
}
#[must_use]
#[inline]
pub fn new_zeroed(size: usize) -> Self {
if size == 0 {
return Self::default();
}
let layout = Self::get_layout(size);
let raw_ptr = unsafe { std::alloc::alloc_zeroed(layout) };
let ptr = std::ptr::NonNull::new(raw_ptr).unwrap();
Self {
bytes_ptr: ptr,
size,
phantom: std::marker::PhantomData {},
}
}
#[must_use]
#[inline]
pub fn new_padded(bytes: &[u8]) -> Self {
if bytes.is_empty() {
return Self::default();
}
let size = bytes.len();
let padding = if size % A::size() == 0 {
0
} else {
A::size() - size % A::size()
};
let padded_size = size + padding;
let mut aligned = Self::new_zeroed(padded_size);
aligned[..size].copy_from_slice(bytes);
aligned
}
#[must_use]
#[inline(always)]
pub fn alignment_size(&self) -> usize {
A::size()
}
#[must_use]
#[inline(always)]
pub fn len(&self) -> usize {
self.size
}
#[must_use]
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.size == 0
}
#[must_use]
#[inline]
pub fn as_ptr(&self) -> *const u8 {
self.bytes_ptr.as_ptr()
}
#[must_use]
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.bytes_ptr.as_ptr()
}
}
impl<A: Alignment> Drop for AlignedBytes<A> {
#[inline]
fn drop(&mut self) {
use std::alloc::dealloc;
if self.size == 0 {
return;
}
let layout = Self::get_layout(self.size);
unsafe { dealloc(self.bytes_ptr.as_ptr(), layout) }
}
}
impl<T: AsRef<[u8]>, A: Alignment> From<T> for AlignedBytes<A> {
#[inline]
fn from(s: T) -> Self {
let slice = s.as_ref();
let bytes;
unsafe {
bytes = Self::new(slice.len());
std::ptr::copy(slice.as_ptr(), bytes.bytes_ptr.as_ptr(), slice.len())
};
bytes
}
}
impl<A: Alignment> std::fmt::Debug for AlignedBytes<A> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let deref = &**self;
std::fmt::Debug::fmt(deref, f)
}
}
impl<A: Alignment> Default for AlignedBytes<A> {
#[inline]
fn default() -> Self {
let bytes_ptr = unsafe {
#[cfg(miri)]
let raw_ptr = std::ptr::invalid_mut(A::size());
#[cfg(not(miri))]
let raw_ptr = A::size() as *mut u8;
NonNull::new_unchecked(raw_ptr)
};
Self {
bytes_ptr,
size: 0,
phantom: Default::default(),
}
}
}
#[cfg(test)]
mod tests {
use crate::test::assert_aligned;
use crate::{alignment, AlignedBytes};
#[test]
fn empty_bytes_are_aligned() {
let empty: AlignedBytes<alignment::Eight> = Default::default();
assert_aligned(empty.as_ptr(), 8);
}
#[test]
fn new_initialize_is_aligned() {
let bytes: AlignedBytes<alignment::TwoTo<15>> = AlignedBytes::new_initialize(2137, |_| 1);
assert_aligned(bytes.as_ptr(), 2usize.pow(15));
}
#[test]
fn alignment_size_equal_to_alignment_type() {
let bytes: AlignedBytes<alignment::TwoTo<7>> = AlignedBytes::new_zeroed(1024);
assert_eq!(128, bytes.alignment_size());
}
}