mallockit 0.1.0

A framework for building malloc implementations in Rust
Documentation
use crate::util::LayoutUtils;

use crate::util::Address;
use std::alloc::Layout;
use std::mem;
use std::ops::Range;

#[derive(Debug)]
pub struct AllocationArea {
    pub top: Address,
    pub limit: Address,
}

type Header = (u32, u32);

impl AllocationArea {
    pub const HEADER: Layout = Layout::new::<Header>();
    pub const EMPTY: Self = Self {
        top: Address::ZERO,
        limit: Address::ZERO,
    };

    pub const fn align_up(value: usize, align: usize) -> usize {
        let mask = align - 1;
        (value + mask) & !mask
    }

    pub const fn align_allocation(start: Address, align: usize) -> Address {
        start.align_up(align)
    }

    pub const fn refill(&mut self, top: Address, limit: Address) {
        self.top = top;
        self.limit = limit;
    }

    pub fn alloc(&mut self, layout: Layout) -> Option<Address> {
        let top = self.top;
        let start = Self::align_allocation(top, layout.align());
        let end = start + layout.size();
        if usize::from(end) <= usize::from(self.limit) {
            self.top = end;
            Some(start)
        } else {
            None
        }
    }

    fn get_layout_slot(ptr: Address) -> &'static mut Header {
        debug_assert!(mem::size_of::<Header>() == mem::size_of::<usize>());
        unsafe { (ptr - mem::size_of::<usize>()).as_mut::<Header>() }
    }

    pub fn alloc_assume_aligned(&mut self, layout: Layout) -> Option<Address> {
        debug_assert!(layout.align() >= std::mem::size_of::<usize>());
        debug_assert_eq!(self.top, Self::align_allocation(self.top, layout.align()));
        let start = self.top;
        let end = start + layout.size();
        if usize::from(end) <= usize::from(self.limit) {
            self.top = end;
            Some(start)
        } else {
            None
        }
    }

    pub fn alloc_with_layout_assume_aligned(&mut self, layout: Layout) -> Option<Address> {
        debug_assert!(layout.align() >= std::mem::size_of::<usize>());
        let (new_layout, _) = unsafe { Self::HEADER.extend_unchecked(layout) };
        debug_assert_eq!(
            self.top,
            Self::align_allocation(self.top, new_layout.align())
        );
        self.alloc_with_layout(layout)
    }

    pub fn alloc_with_layout(&mut self, layout: Layout) -> Option<Address> {
        debug_assert!(layout.align() >= std::mem::size_of::<usize>());
        let (new_layout, offset) = unsafe { Self::HEADER.extend_unchecked(layout) };
        let top = self.top;
        let start = Self::align_allocation(top, new_layout.align());
        let end = start + new_layout.size();
        if end <= self.limit {
            let data_start = start + offset;
            *Self::get_layout_slot(data_start) = (layout.size() as u32, layout.align() as u32);
            self.top = end;
            Some(data_start)
        } else {
            None
        }
    }

    pub fn load_layout(ptr: Address) -> Layout {
        let (size, align) = *Self::get_layout_slot(ptr);
        unsafe { Layout::from_size_align_unchecked(size as _, align as _) }
    }

    pub fn load_range(ptr: Address) -> Range<Address> {
        let (size, align) = *Self::get_layout_slot(ptr);
        let (new_layout, offset) = unsafe {
            Self::HEADER.extend_unchecked(Layout::from_size_align_unchecked(size as _, align as _))
        };
        let start = ptr - offset;
        let end = start + new_layout.size();
        start..end
    }
}