ps_alloc/
free.rs

1use std::alloc::Layout;
2
3use crate::{
4    header::{AllocationHeader, HEADER_SIZE},
5    DeallocationError,
6};
7
8/// This method is the counterpart to [`crate::alloc`].
9///
10/// Every pointer allocated by [`crate::alloc`] must eventually be freed using this function.
11///
12/// # Errors
13///
14/// An error is returned if a safety check fails.
15///
16/// # Safety
17///
18/// Only pointers allocated by [`crate::alloc`] can be freed using this method.
19/// Using this method on any other pointer causes undefined behaviour.
20/// This method provides safety checks, however, these are provided on a **best effort basis** and are an indication undefined behaviour has already occured.
21/// Unless `ptr` is misaligned or null, **this function will dereference it**.
22pub unsafe fn free(ptr: *mut u8) -> Result<(), DeallocationError> {
23    if ptr.is_null() {
24        return Err(DeallocationError::NullPtr);
25    }
26
27    if !(ptr as usize).is_multiple_of(HEADER_SIZE) {
28        return Err(DeallocationError::ImproperAlignment);
29    }
30
31    let header_ptr: *mut AllocationHeader = ptr.sub(HEADER_SIZE).cast();
32
33    if !header_ptr.is_aligned() {
34        return Err(DeallocationError::ImproperAlignment);
35    }
36
37    if (*header_ptr).is_marked_as_free() {
38        return Err(DeallocationError::DoubleFree);
39    }
40
41    if !(*header_ptr).is_marked_as_used() {
42        return Err(DeallocationError::CorruptedMarker);
43    }
44
45    let layout = Layout::from_size_align((*header_ptr).get_size(), HEADER_SIZE)?;
46
47    (*header_ptr).mark_as_free();
48
49    unsafe { std::alloc::dealloc(header_ptr.cast(), layout) }
50
51    Ok(())
52}