ps_alloc/
lib.rs

1mod error;
2
3use std::alloc::Layout;
4
5pub use error::*;
6
7pub const ALIGNMENT: usize = std::mem::size_of::<Allocation>();
8pub const MARKER_FREE: [u8; 8] = *b"Fr33Mmry";
9pub const MARKER_USED: [u8; 8] = *b"U53dMmry";
10
11#[repr(align(16))]
12struct Allocation {
13    marker: [u8; 8],
14    size: usize,
15}
16
17/// - A reasonably safe implementation of `alloc`.
18/// - Memory allocated by this function must be freed by this crate's `free`.
19/// - Caller guarantees `free` is called before the returned pointer goes out of scope.
20pub fn alloc(size: usize) -> Result<*mut u8, AllocationError> {
21    use AllocationError::*;
22
23    let size = size
24        .div_ceil(ALIGNMENT)
25        .checked_add(1)
26        .ok_or(ArithmeticError)?
27        .checked_mul(ALIGNMENT)
28        .ok_or(ArithmeticError)?;
29
30    let layout = Layout::from_size_align(size, ALIGNMENT)?;
31
32    let ptr = unsafe { std::alloc::alloc(layout) };
33
34    if ptr.is_null() {
35        Err(OutOfMemory)?
36    }
37
38    let allocation = unsafe { &mut *(ptr.cast::<Allocation>()) };
39
40    allocation.marker = MARKER_USED;
41    allocation.size = size;
42
43    let ptr = unsafe { ptr.add(ALIGNMENT) };
44
45    Ok(ptr)
46}
47
48/// - A reasonably safe implementation of `free`.
49/// - This function will free a pointer allocated by `alloc`.
50/// - Caller guarantees that the provided pointer was allocated by this crate's `alloc` function.
51/// - Providing `NULL` is safe and will return `Err(DeallocationError::NullPtr)`.
52/// - Providing any other pointer is undefined behaviour.
53pub fn free<T>(ptr: *mut T) -> Result<(), DeallocationError> {
54    use DeallocationError::*;
55
56    if ptr.is_null() {
57        Err(NullPtr)?
58    }
59
60    let ptr = ptr.cast::<Allocation>();
61
62    if !ptr.is_aligned() {
63        Err(ImproperAlignment)?
64    }
65
66    let ptr = unsafe { ptr.sub(1) };
67    let allocation = unsafe { &mut *ptr };
68
69    if allocation.marker == MARKER_FREE {
70        Err(DoubleFree)?
71    } else if allocation.marker != MARKER_USED {
72        Err(InvalidAllocation)?
73    }
74
75    let layout = Layout::from_size_align(allocation.size, ALIGNMENT)?;
76
77    allocation.marker = MARKER_FREE;
78
79    unsafe { std::alloc::dealloc(ptr.cast(), layout) }
80
81    Ok(())
82}