use std::alloc::{alloc, dealloc, Layout};
use std::mem::MaybeUninit;
use std::ptr;
use std::{fmt, mem};
#[repr(align(64))]
pub struct Align64;
pub struct Aligned<T> {
_alignment: [Align64; 0],
pub data: T,
}
#[cfg(any(test, feature = "bench"))]
impl<const N: usize, T> Aligned<[T; N]> {
#[inline(always)]
pub fn from_fn<F>(cb: F) -> Self
where
F: FnMut(usize) -> T,
{
Aligned { _alignment: [], data: std::array::from_fn(cb) }
}
}
impl<const N: usize, T> Aligned<[MaybeUninit<T>; N]> {
#[inline(always)]
pub const fn uninit_array() -> Self {
Aligned {
_alignment: [],
data: unsafe { MaybeUninit::uninit().assume_init() },
}
}
}
impl<T> Aligned<T> {
pub const fn new(data: T) -> Self {
Aligned { _alignment: [], data }
}
#[allow(clippy::uninit_assumed_init)]
pub const unsafe fn uninitialized() -> Self {
Self::new(MaybeUninit::uninit().assume_init())
}
}
pub struct AlignedBoxedSlice<T> {
ptr: std::ptr::NonNull<T>,
len: usize,
}
impl<T> AlignedBoxedSlice<T> {
cfg_if::cfg_if! {
if #[cfg(target_arch = "wasm32")] {
const DATA_ALIGNMENT_LOG2: usize = 3;
} else {
const DATA_ALIGNMENT_LOG2: usize = 6;
}
}
const fn layout(len: usize) -> Layout {
unsafe {
Layout::from_size_align_unchecked(
len * mem::size_of::<T>(),
1 << Self::DATA_ALIGNMENT_LOG2,
)
}
}
fn alloc(len: usize) -> std::ptr::NonNull<T> {
unsafe { ptr::NonNull::new_unchecked(alloc(Self::layout(len)) as *mut T) }
}
pub fn new(len: usize, val: T) -> Self
where
T: Clone,
{
let mut output = Self { ptr: Self::alloc(len), len };
for a in output.iter_mut() {
*a = val.clone();
}
output
}
}
impl<T: fmt::Debug> fmt::Debug for AlignedBoxedSlice<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<T> std::ops::Deref for AlignedBoxedSlice<T> {
type Target = [T];
fn deref(&self) -> &[T] {
unsafe {
let p = self.ptr.as_ptr();
std::slice::from_raw_parts(p, self.len)
}
}
}
impl<T> std::ops::DerefMut for AlignedBoxedSlice<T> {
fn deref_mut(&mut self) -> &mut [T] {
unsafe {
let p = self.ptr.as_ptr();
std::slice::from_raw_parts_mut(p, self.len)
}
}
}
impl<T> std::ops::Drop for AlignedBoxedSlice<T> {
fn drop(&mut self) {
unsafe {
for a in self.iter_mut() {
ptr::drop_in_place(a)
}
dealloc(self.ptr.as_ptr() as *mut u8, Self::layout(self.len));
}
}
}
unsafe impl<T> Send for AlignedBoxedSlice<T> where T: Send {}
unsafe impl<T> Sync for AlignedBoxedSlice<T> where T: Sync {}
#[cfg(test)]
mod test {
use super::*;
fn is_aligned<T>(ptr: *const T, n: usize) -> bool {
((ptr as usize) & ((1 << n) - 1)) == 0
}
#[test]
fn sanity_stack() {
let a: Aligned<_> = Aligned::new([0u8; 3]);
assert!(is_aligned(a.data.as_ptr(), 4));
}
#[test]
fn sanity_heap() {
let a: AlignedBoxedSlice<_> = AlignedBoxedSlice::new(3, 0u8);
assert!(is_aligned(a.as_ptr(), 4));
}
}