ps_alloc/
alloc.rs

1use std::alloc::Layout;
2
3use crate::{
4    header::{AllocationHeader, HEADER_SIZE},
5    AllocationError,
6};
7
8/// A reasonably safe implementation of `alloc`.
9///
10/// - Memory allocated by this function must be freed by this crate's `free`.
11/// - Caller guarantees `free` is called before the returned pointer goes out of scope.
12///
13/// # Errors
14/// - `Err(ArithmeticError)` on integer overflow.
15/// - `Err(LayoutError)` if the computed layout is invalid.
16/// - `Err(OutOfMemory)` if `alloc()` returns a `nullptr`.
17///
18/// # Safety
19/// This function is safe to call, as it produces a valid pointer.
20/// The data buffer being pointed to is, however, not initialized.
21pub fn alloc(size: usize) -> Result<*mut u8, AllocationError> {
22    // the allocation size is header + size, rounded up
23    let size = size
24        .checked_add(HEADER_SIZE)
25        .ok_or(AllocationError::ArithmeticError)?
26        .checked_next_multiple_of(HEADER_SIZE)
27        .ok_or(AllocationError::ArithmeticError)?;
28
29    // allocations are aligned to [`HEADER_SIZE`]
30    let layout = Layout::from_size_align(size, HEADER_SIZE)?;
31
32    // since we have a valid layout, the safety requirements of [`std::alloc::alloc`] are satisfied.
33    let ptr = unsafe { std::alloc::alloc(layout) };
34
35    // note that [`std::alloc::alloc`] is allowed to abort instead
36    if ptr.is_null() {
37        return Err(AllocationError::OutOfMemory);
38    }
39
40    // the first [`HEADER_SIZE`] bytes of the allocation are reserved for the header
41    let header: &mut AllocationHeader = unsafe { &mut *ptr.cast() };
42
43    header.mark_as_used();
44    header.set_size(size);
45
46    // the user buffer starts at `ptr + HEADER_SIZE`
47    Ok(unsafe { ptr.add(HEADER_SIZE) })
48}