ps-alloc 0.1.0-8

a reasonably safe allocator
Documentation
use std::alloc::Layout;

use crate::{
    header::{AllocationHeader, HEADER_SIZE},
    DeallocationError,
};

/// This method is the counterpart to [`crate::alloc`].
///
/// Every pointer allocated by [`crate::alloc`] must eventually be freed using this function.
///
/// # Errors
///
/// An error is returned if a safety check fails.
///
/// # Safety
///
/// Only pointers allocated by [`crate::alloc`] can be freed using this method.
/// Using this method on any other pointer causes undefined behaviour.
/// This method provides safety checks, however, these are provided on a **best effort basis** and are an indication undefined behaviour has already occured.
/// Unless `ptr` is misaligned or null, **this function will dereference it**.
pub unsafe fn free(ptr: *mut u8) -> Result<(), DeallocationError> {
    if ptr.is_null() {
        return Err(DeallocationError::NullPtr);
    }

    if !(ptr as usize).is_multiple_of(HEADER_SIZE) {
        return Err(DeallocationError::ImproperAlignment);
    }

    let header_ptr: *mut AllocationHeader = ptr.sub(HEADER_SIZE).cast();

    if !header_ptr.is_aligned() {
        return Err(DeallocationError::ImproperAlignment);
    }

    if (*header_ptr).is_marked_as_free() {
        return Err(DeallocationError::DoubleFree);
    }

    if !(*header_ptr).is_marked_as_used() {
        return Err(DeallocationError::CorruptedMarker);
    }

    let layout = Layout::from_size_align((*header_ptr).get_size(), HEADER_SIZE)?;

    (*header_ptr).mark_as_free();

    unsafe { std::alloc::dealloc(header_ptr.cast(), layout) }

    Ok(())
}