#![warn(missing_docs)]
#![warn(missing_doc_code_examples)]
#![warn(rust_2018_idioms)]
#[derive(Debug)]
pub enum AlignedBoxError {
TooManyElements,
OutOfMemory,
ZeroAlloc,
}
impl std::error::Error for AlignedBoxError {}
impl std::fmt::Display for AlignedBoxError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AlignedBoxError::TooManyElements => write!(f, "Too many elements for a slice."),
AlignedBoxError::OutOfMemory => write!(f, "Memory allocation failed. Out of memory?"),
AlignedBoxError::ZeroAlloc => write!(f, "Zero byte allocations not supported."),
}
}
}
pub struct AlignedBox<T: ?Sized> {
container: std::option::Option<std::boxed::Box<T>>,
layout: std::alloc::Layout,
}
impl<T: ?Sized> std::ops::Deref for AlignedBox<T> {
type Target = T;
fn deref(&self) -> &T {
return self.container.as_deref().unwrap();
}
}
impl<T: ?Sized> std::ops::DerefMut for AlignedBox<T> {
fn deref_mut(&mut self) -> &mut T {
return self.container.as_deref_mut().unwrap();
}
}
impl<T: ?Sized> Drop for AlignedBox<T> {
fn drop(&mut self) {
let container = self.container.take().unwrap();
unsafe {
std::alloc::dealloc(std::boxed::Box::into_raw(container) as *mut u8, self.layout);
}
}
}
impl<T: Clone + ?Sized> Clone for AlignedBox<T> {
fn clone(&self) -> Self {
let ptr = unsafe { std::alloc::alloc(self.layout) as *mut T };
if ptr.is_null() {
panic!("Error when allocating memory for a clone of AlignedBox");
}
let mut b = unsafe { AlignedBox::<T>::from_raw_parts(ptr, self.layout) };
unsafe { std::ptr::write(&mut *b, *self.container.clone().unwrap()) };
b
}
}
impl<T: ?Sized> AlignedBox<T> {
pub fn into_raw_parts(mut from: AlignedBox<T>) -> (*mut T, std::alloc::Layout) {
let container = from.container.take().unwrap();
let ptr = std::boxed::Box::into_raw(container);
let layout = from.layout;
std::mem::forget(from);
(ptr, layout)
}
pub unsafe fn from_raw_parts(ptr: *mut T, layout: std::alloc::Layout) -> AlignedBox<T> {
let container = Some(std::boxed::Box::from_raw(ptr));
AlignedBox::<T> { container, layout }
}
}
impl<T> AlignedBox<T> {
pub fn new(
mut alignment: usize,
value: T,
) -> std::result::Result<AlignedBox<T>, std::boxed::Box<dyn std::error::Error>> {
if alignment < std::mem::align_of::<T>() {
alignment = std::mem::align_of::<T>();
}
let memsize: usize = std::mem::size_of::<T>();
if memsize == 0 {
return Err(AlignedBoxError::ZeroAlloc.into());
}
let layout = std::alloc::Layout::from_size_align(memsize, alignment)?;
let ptr = unsafe { std::alloc::alloc(layout) as *mut T };
if ptr.is_null() {
return Err(AlignedBoxError::OutOfMemory.into());
}
let mut b = unsafe { AlignedBox::<T>::from_raw_parts(ptr, layout) };
unsafe { std::ptr::write(&mut *b, value) };
Ok(b)
}
}
impl<T: Default> AlignedBox<[T]> {
pub fn slice_from_default(
mut alignment: usize,
nelems: usize,
) -> std::result::Result<AlignedBox<[T]>, std::boxed::Box<dyn std::error::Error>> {
if alignment < std::mem::align_of::<T>() {
alignment = std::mem::align_of::<T>();
}
let maxelems = (isize::MAX as usize) / std::mem::size_of::<T>();
if nelems > maxelems {
return Err(AlignedBoxError::TooManyElements.into());
}
let memsize: usize = std::mem::size_of::<T>() * nelems;
if memsize == 0 {
return Err(AlignedBoxError::ZeroAlloc.into());
}
let layout = std::alloc::Layout::from_size_align(memsize, alignment)?;
let ptr = unsafe { std::alloc::alloc(layout) as *mut T };
if ptr.is_null() {
return Err(AlignedBoxError::OutOfMemory.into());
}
let slice = unsafe { std::slice::from_raw_parts_mut(ptr, nelems) };
let mut b = unsafe { AlignedBox::<[T]>::from_raw_parts(slice, layout) };
for i in (*b).iter_mut() {
let d = T::default();
unsafe { std::ptr::write(&mut *i, d) };
}
Ok(b)
}
}
impl<T: Copy> AlignedBox<[T]> {
pub fn slice_from_value(
mut alignment: usize,
nelems: usize,
value: T,
) -> std::result::Result<AlignedBox<[T]>, std::boxed::Box<dyn std::error::Error>> {
if alignment < std::mem::align_of::<T>() {
alignment = std::mem::align_of::<T>();
}
let maxelems = (isize::MAX as usize) / std::mem::size_of::<T>();
if nelems > maxelems {
return Err(AlignedBoxError::TooManyElements.into());
}
let memsize: usize = std::mem::size_of::<T>() * nelems;
if memsize == 0 {
return Err(AlignedBoxError::ZeroAlloc.into());
}
let layout = std::alloc::Layout::from_size_align(memsize, alignment)?;
let ptr = unsafe { std::alloc::alloc(layout) as *mut T };
if ptr.is_null() {
return Err(AlignedBoxError::OutOfMemory.into());
}
let slice = unsafe { std::slice::from_raw_parts_mut(ptr, nelems) };
let mut b = unsafe { AlignedBox::<[T]>::from_raw_parts(slice, layout) };
for i in (*b).iter_mut() {
*i = value;
}
Ok(b)
}
}
#[cfg(test)]
mod tests {
use super::AlignedBox;
use lazy_static::lazy_static;
lazy_static! {
static ref SEQ_TEST_MUTEX: std::sync::RwLock<()> = std::sync::RwLock::new(());
}
#[test]
fn alignment() {
let _m = SEQ_TEST_MUTEX.read().unwrap();
let alignments = [
1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536,
131072, 262144, 524288, 1048576,
];
for a in alignments.iter() {
let b = AlignedBox::<[u8]>::slice_from_value(*a, 42, 0).unwrap();
assert_eq!((b.as_ptr() as usize) % *a, 0);
}
}
#[test]
fn size() {
let _m = SEQ_TEST_MUTEX.read().unwrap();
let sizes = [
1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536,
131072, 262144, 524288, 1048576,
];
for s in sizes.iter() {
let b = AlignedBox::<[u8]>::slice_from_value(1, *s, 0).unwrap();
assert_eq!(b.len(), *s);
}
}
#[test]
fn free() {
let _m = SEQ_TEST_MUTEX.write().unwrap();
const ATTEMPTS: usize = 1000;
let alignment = 1024 * 1024;
let size = 1024;
let mut addrs = std::collections::HashSet::new();
for _ in 0..ATTEMPTS {
let b = AlignedBox::<[u8]>::slice_from_value(alignment, size, 0).unwrap();
addrs.insert(b.as_ptr() as usize);
}
assert_ne!(addrs.len(), ATTEMPTS);
}
#[test]
fn aliasing() {
let _m = SEQ_TEST_MUTEX.read().unwrap();
let size = 1024;
let b1 = AlignedBox::<[u8]>::slice_from_value(1, size, 0).unwrap();
let b2 = AlignedBox::<[u8]>::slice_from_value(1, size, 0).unwrap();
let addr1 = b1.as_ptr() as usize;
let addr2 = b2.as_ptr() as usize;
assert_ne!(addr1, addr2);
if addr1 > addr2 {
assert!((addr1 - addr2) >= size);
} else {
assert!((addr2 - addr1) >= size);
}
}
#[test]
fn read_write() {
let _m = SEQ_TEST_MUTEX.read().unwrap();
let mut b = AlignedBox::<[f32]>::slice_from_value(128, 1024, 3.1415).unwrap();
for i in b.iter() {
assert_eq!(*i, 3.1415);
}
let mut ctr: f32 = 0.0;
for i in b.iter_mut() {
*i = ctr;
ctr += 1.0;
}
ctr = 0.0;
for i in b.iter() {
assert_eq!(*i, ctr);
ctr += 1.0;
}
}
#[test]
fn defaults() {
let _m = SEQ_TEST_MUTEX.read().unwrap();
#[derive(PartialEq, Eq, Debug)]
struct SomeVaryingDefault {
i: i32,
}
static COUNTER: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
impl Default for SomeVaryingDefault {
fn default() -> SomeVaryingDefault {
SomeVaryingDefault {
i: COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
}
}
}
let b = AlignedBox::<[SomeVaryingDefault]>::slice_from_default(128, 1024).unwrap();
assert_eq!(SomeVaryingDefault::default().i, 1024);
let mut ctr = 0;
for i in b.iter() {
assert_eq!(i.i, ctr);
ctr += 1;
}
}
#[test]
fn move_sem() {
let _m = SEQ_TEST_MUTEX.read().unwrap();
let v = vec![1, 2, 3];
let b = AlignedBox::new(128, v).unwrap();
assert_eq!(*b, vec![1, 2, 3]);
}
#[test]
fn copy_sem() {
let _m = SEQ_TEST_MUTEX.read().unwrap();
let v = 17;
let _ = AlignedBox::new(128, v).unwrap();
let _ = AlignedBox::slice_from_value(128, 1024, v).unwrap();
assert_eq!(v, 17);
}
#[test]
fn min_align() {
let _m = SEQ_TEST_MUTEX.read().unwrap();
#[repr(C, align(131072))]
struct LargeAlign {
x: u8,
}
assert_eq!(std::mem::align_of::<LargeAlign>(), 131072);
let a = LargeAlign { x: 28 };
let b = AlignedBox::<LargeAlign>::new(1, a).unwrap();
let (ptr, layout) = AlignedBox::into_raw_parts(b);
assert_eq!((ptr as usize) % std::mem::align_of::<LargeAlign>(), 0);
let _ = unsafe { AlignedBox::from_raw_parts(ptr, layout) };
}
#[test]
fn clone() {
let _m = SEQ_TEST_MUTEX.read().unwrap();
let mut b = AlignedBox::new(128, vec![47, 11]).unwrap();
let mut another_b = b.clone();
assert_eq!(*b, *another_b);
b[0] = 48;
another_b[1] = 12;
assert_eq!(b[0], 48);
assert_eq!(b[1], 11);
assert_eq!(another_b[0], 47);
assert_eq!(another_b[1], 12);
}
}