alloc-align 1.0.0

Allocator wrapper that forces a given alignment (nightly only)
Documentation
#![feature(allocator_api)]
#![cfg_attr(test, feature(pointer_is_aligned_to))]

use std::{
    alloc::{AllocError, Allocator, Global, Layout},
    ptr::NonNull,
};

/// Allocator that forces all allocations to be aligned
/// to at least `ALIGN` bytes.
pub struct Align<const ALIGN: usize, A = Global> {
    inner: A,
}

impl<const ALIGN: usize> Align<ALIGN, Global> {
    pub const fn new() -> Self {
        Self::wrap(Global)
    }
}

impl<const ALIGN: usize, A> Align<ALIGN, A> {
    pub const fn wrap(inner: A) -> Self {
        Self { inner }
    }
}

impl<const ALIGN: usize, A> Default for Align<ALIGN, A>
where
    A: Default,
{
    fn default() -> Self {
        Self::wrap(A::default())
    }
}

impl<const ALIGN: usize, A> Align<ALIGN, A> {
    fn align_layout(layout: Layout) -> Layout {
        layout.align_to(ALIGN).unwrap()
    }
}

unsafe impl<const ALIGN: usize, A> Allocator for Align<ALIGN, A>
where
    A: Allocator,
{
    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        self.inner.allocate(Self::align_layout(layout))
    }

    fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        self.inner.allocate_zeroed(Self::align_layout(layout))
    }

    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
        unsafe { self.inner.deallocate(ptr, Self::align_layout(layout)) }
    }

    unsafe fn grow(
        &self,
        ptr: NonNull<u8>,
        old_layout: Layout,
        new_layout: Layout,
    ) -> Result<NonNull<[u8]>, AllocError> {
        unsafe {
            self.inner.grow(
                ptr,
                Self::align_layout(old_layout),
                Self::align_layout(new_layout),
            )
        }
    }

    unsafe fn grow_zeroed(
        &self,
        ptr: NonNull<u8>,
        old_layout: Layout,
        new_layout: Layout,
    ) -> Result<NonNull<[u8]>, AllocError> {
        unsafe {
            self.inner.grow_zeroed(
                ptr,
                Self::align_layout(old_layout),
                Self::align_layout(new_layout),
            )
        }
    }

    unsafe fn shrink(
        &self,
        ptr: NonNull<u8>,
        old_layout: Layout,
        new_layout: Layout,
    ) -> Result<NonNull<[u8]>, AllocError> {
        unsafe {
            self.inner.shrink(
                ptr,
                Self::align_layout(old_layout),
                Self::align_layout(new_layout),
            )
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn aligned_vec() {
        let mut vec = Vec::<u32, Align<4096, Global>>::new_in(Align::default());
        vec.push(1);
        assert!(vec.as_ptr().is_aligned_to(4096));
    }
}