use std::alloc::{Layout, LayoutError};
use crate::{
error::ValidationError,
marker::{MARKER_FREE, MARKER_USED},
AllocationError,
};
#[repr(align(16))]
pub struct AllocationHeader {
marker: [u8; 8],
size: usize,
}
impl AllocationHeader {
#[inline]
pub const fn new(size: usize) -> Self {
Self {
marker: MARKER_USED,
size,
}
}
#[inline]
pub const fn get_size(&self) -> usize {
self.size
}
#[inline]
pub fn is_marked_as_free(&self) -> bool {
self.marker == MARKER_FREE
}
#[inline]
pub fn is_marked_as_used(&self) -> bool {
self.marker == MARKER_USED
}
#[inline]
pub const fn mark_as_free(&mut self) {
self.marker = MARKER_FREE;
}
}
pub const HEADER_SIZE: usize = std::mem::size_of::<AllocationHeader>();
pub(crate) const ALIGN: usize = std::mem::align_of::<AllocationHeader>();
const _: () = assert!(HEADER_SIZE == ALIGN);
pub(crate) fn total_size(size: usize) -> Result<usize, AllocationError> {
size.checked_add(HEADER_SIZE)
.ok_or(AllocationError::ArithmeticError)?
.checked_next_multiple_of(ALIGN)
.ok_or(AllocationError::ArithmeticError)
}
pub(crate) fn layout_of(total: usize) -> Result<Layout, LayoutError> {
Layout::from_size_align(total, ALIGN)
}
pub(crate) unsafe fn initialize_block(base: *mut u8, total: usize) -> *mut u8 {
unsafe {
base.cast::<AllocationHeader>()
.write(AllocationHeader::new(total))
};
unsafe { base.add(HEADER_SIZE) }
}
pub(crate) unsafe fn validate(ptr: *mut u8) -> Result<*mut AllocationHeader, ValidationError> {
let header_ptr: *mut AllocationHeader = unsafe { ptr.sub(HEADER_SIZE) }.cast();
if !header_ptr.is_aligned() {
return Err(ValidationError::ImproperAlignment);
}
if unsafe { &*header_ptr }.is_marked_as_free() {
return Err(ValidationError::MarkerFree);
}
if !unsafe { &*header_ptr }.is_marked_as_used() {
return Err(ValidationError::MarkerCorrupted);
}
Ok(header_ptr)
}