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.
20/// # Errors
21/// - `Err(ArithmeticError)` is returned on integer overflow, which shouldn't happen.
22/// - `Err(LayoutError)` is returned if `sizeof(([u8; 8], usize))` isn't a power of 2.
23/// - `Err(OutOfMemory)` is returned if `alloc()` returned a `nullptr`.
24#[allow(clippy::cast_ptr_alignment)]
25pub fn alloc(size: usize) -> Result<*mut u8, AllocationError> {
26    use AllocationError::{ArithmeticError, OutOfMemory};
27
28    let size = size
29        .div_ceil(ALIGNMENT)
30        .checked_add(1)
31        .ok_or(ArithmeticError)?
32        .checked_mul(ALIGNMENT)
33        .ok_or(ArithmeticError)?;
34
35    let layout = Layout::from_size_align(size, ALIGNMENT)?;
36
37    let ptr = unsafe { std::alloc::alloc(layout) };
38
39    if ptr.is_null() {
40        Err(OutOfMemory)?;
41    }
42
43    let allocation = unsafe { &mut *(ptr.cast::<Allocation>()) };
44
45    allocation.marker = MARKER_USED;
46    allocation.size = size;
47
48    let ptr = unsafe { ptr.add(ALIGNMENT) };
49
50    Ok(ptr)
51}
52
53/// - A reasonably safe implementation of `free`.
54/// - This function will free a pointer allocated by `alloc`.
55/// - Caller guarantees that the provided pointer was allocated by this crate's `alloc` function.
56/// - Providing `NULL` is safe and will return `Err(DeallocationError::NullPtr)`.
57/// - Providing any other pointer is undefined behaviour.
58/// # Errors
59/// - Returns `Err(DeallocationError)` if a safety check fails.
60pub fn free<T>(ptr: *mut T) -> Result<(), DeallocationError> {
61    use DeallocationError::{DoubleFree, ImproperAlignment, InvalidAllocation, NullPtr};
62
63    if ptr.is_null() {
64        Err(NullPtr)?;
65    }
66
67    let ptr = ptr.cast::<Allocation>();
68
69    if !ptr.is_aligned() {
70        Err(ImproperAlignment)?;
71    }
72
73    let ptr = unsafe { ptr.sub(1) };
74    let allocation = unsafe { &mut *ptr };
75
76    if allocation.marker == MARKER_FREE {
77        Err(DoubleFree)?;
78    } else if allocation.marker != MARKER_USED {
79        Err(InvalidAllocation)?;
80    }
81
82    let layout = Layout::from_size_align(allocation.size, ALIGNMENT)?;
83
84    allocation.marker = MARKER_FREE;
85
86    unsafe { std::alloc::dealloc(ptr.cast(), layout) }
87
88    Ok(())
89}