realloc 0.1.0

A re-implementation of various ::alloc features
Documentation
use core::cell::UnsafeCell;
use core::mem::MaybeUninit;
use core::ptr::NonNull;

use crate::lock::Mutex;
use crate::{Align, Alloc, Allocator, Brand, Error, Layout};

/// An allocator that hands out allocations from a statically-sized chunk of bytes.
///
/// If the memory is exhausted, the only way to obtain more is to free all
/// existing allocations. Alternatively, you can layer another allocator on top
/// of this one that uses a more sophisticated allocation strategy.
pub struct Array<const N: usize> {
    buf: UnsafeCell<[MaybeUninit<u8>; N]>,
    inner: Mutex<ArrayInner>,
    brand: Brand,
}

// SAFETY: all the real concurrent access is done via `Mutex`
unsafe impl<const N: usize> Sync for Array<N> {}

struct ArrayInner {
    next: usize,
    count: usize,
}

impl<const N: usize> Array<N> {
    /// Create a new, empty array allocator.
    ///
    /// Note that the internal buffer is uninitialized. This does not matter,
    /// since memory allocated via the [`Allocator`] interface has no guarantees
    /// about initialization.
    pub fn new() -> Self {
        Self {
            buf: UnsafeCell::new([MaybeUninit::uninit(); N]),
            inner: Mutex::new(ArrayInner { next: 0, count: 0 }),
            brand: Brand::new(),
        }
    }
}

unsafe impl<const N: usize> Allocator for Array<N> {
    fn brand(&self) -> &Brand {
        &self.brand
    }

    fn alloc_bytes(&self, layout: Layout) -> Result<Alloc<'_, u8>, Error> {
        let mut this_lock = self.inner.lock();
        let this = &mut *this_lock;

        if layout.size > N - this.next {
            return Err(Error::OutOfMemory);
        }

        let buf = self.buf.get().cast::<u8>();

        let align = layout.align.align();
        // SAFETY: no isizes are overflowed, nor bounds overreached
        let start = unsafe { buf.add(this.next) };
        let start = start.map_addr(|addr| addr.div_ceil(align) * align);
        let end = start as usize + layout.size;

        debug_assert!(start as usize % align == 0);
        if end > buf as usize + N {
            return Err(Error::OutOfMemory);
        }

        this.next = end - buf as usize;
        this.count += 1;

        drop(this_lock);

        let ptr = NonNull::new(start).unwrap();
        // SAFETY:
        // - `ptr` was just allocated in this allocator, and thus not already
        //   deallocated.
        // - `layout` is the layout used to allocate `ptr`.
        // - This is not being used to double-drop an allocation.
        Ok(unsafe { Alloc::new(ptr, layout, self) })
    }

    fn dealloc_bytes<'this>(&'this self, alloc: Alloc<'this, u8>) {
        if alloc.allocator().brand() != &self.brand {
            return;
        }

        let mut this = self.inner.lock();

        this.count -= 1;
        if this.count == 0 {
            this.next = 0;
        }
    }

    fn dangling(&self, align: Align) -> Alloc<'_, u8> {
        let layout = Layout { size: 0, align };
        unsafe {
            Alloc::new(
                NonNull::new(align.align() as *mut u8).unwrap(),
                layout,
                self,
            )
        }
    }

    fn total_size(&self) -> Option<usize> {
        Some(N)
    }

    fn remaining_size(&self) -> Option<usize> {
        Some(N - self.inner.lock().next)
    }
}

impl<const N: usize> Default for Array<N> {
    fn default() -> Self {
        Self::new()
    }
}