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}