ps_alloc/alloc.rs
1use crate::{
2 header::{initialize_block, layout_of, total_size},
3 AllocationError,
4};
5
6/// A reasonably safe implementation of `alloc`.
7///
8/// Allocates a buffer of `size` bytes and returns a pointer to it. The buffer is
9/// aligned to [`crate::HEADER_SIZE`] (16) bytes and is **not** initialized. `alloc(0)`
10/// succeeds and returns a valid pointer to a zero-sized buffer.
11///
12/// Every returned pointer, including the zero-sized case, must eventually be released
13/// with [`crate::free`] or [`crate::realloc`]; otherwise the allocation leaks.
14///
15/// # Errors
16/// - `Err(ArithmeticError)` on integer overflow.
17/// - `Err(LayoutError)` if the computed layout is invalid.
18/// - `Err(OutOfMemory)` if the underlying allocator returns a null pointer.
19pub fn alloc(size: usize) -> Result<*mut u8, AllocationError> {
20 // the allocation size is header + size, rounded up
21 let size = total_size(size)?;
22
23 // allocations are aligned to [`crate::header::ALIGN`]
24 let layout = layout_of(size)?;
25
26 // SAFETY: `layout` has non-zero size, since `total_size` returns at least
27 // `HEADER_SIZE` bytes.
28 let ptr = unsafe { std::alloc::alloc(layout) };
29
30 // note that [`std::alloc::alloc`] is allowed to abort instead
31 if ptr.is_null() {
32 return Err(AllocationError::OutOfMemory);
33 }
34
35 // the first [`crate::HEADER_SIZE`] bytes of the allocation are reserved for the
36 // header; the user buffer starts directly after it
37 // SAFETY: `ptr` is non-null, sufficiently aligned, and valid for writes of `size`
38 // bytes, which include the header.
39 Ok(unsafe { initialize_block(ptr, size) })
40}