Skip to main content

osom_lib_alloc/
std_allocator.rs

1//! Holds the implementation of the standard allocator, based on `alloc` crate.
2extern crate alloc;
3
4use core::{alloc::Layout, convert::Infallible, ptr::NonNull};
5
6use osom_lib_reprc::macros::reprc;
7use osom_lib_try_clone::TryClone;
8
9use super::traits::{Allocator, WithMessage};
10
11/// The standard allocator based on `alloc` crate.
12#[derive(Debug, Default, PartialEq, Eq, Clone, Copy)]
13#[reprc]
14pub struct StdAllocator;
15
16impl TryClone for StdAllocator {
17    type Error = Infallible;
18
19    fn try_clone(&self) -> Result<Self, Self::Error> {
20        Ok(*self)
21    }
22}
23
24/// The standard allocator error.
25#[derive(Debug, PartialEq, Eq, Clone, Copy)]
26#[reprc]
27pub enum StdAllocationError {
28    /// A generic allocation error. Likely because of out of memory.
29    AllocationError = 0,
30
31    /// The result is misaligned. Likely because the requested alignment is above
32    /// what malloc supports.
33    MisalignedResult = 1,
34}
35
36impl WithMessage for StdAllocationError {
37    fn message(&self) -> &str {
38        match self {
39            StdAllocationError::AllocationError => "Allocation failed",
40            StdAllocationError::MisalignedResult => "Misaligned result",
41        }
42    }
43}
44
45impl TryClone for StdAllocationError {
46    type Error = Infallible;
47
48    fn try_clone(&self) -> Result<Self, Self::Error> {
49        Ok(*self)
50    }
51}
52
53unsafe impl Allocator for StdAllocator {
54    type SpecificAllocationError = StdAllocationError;
55
56    fn allocate(&mut self, layout: Layout) -> Result<NonNull<u8>, Self::SpecificAllocationError> {
57        let raw_ptr = raw_aligned_malloc(layout);
58        if raw_ptr.is_null() {
59            return Err(StdAllocationError::AllocationError);
60        }
61
62        if raw_ptr.align_offset(layout.align()) != 0 {
63            raw_aligned_free(raw_ptr.cast(), layout);
64            return Err(StdAllocationError::MisalignedResult);
65        }
66
67        Ok(unsafe { NonNull::new_unchecked(raw_ptr.cast()) })
68    }
69
70    #[inline(always)]
71    unsafe fn deallocate(&mut self, ptr: NonNull<u8>, layout: Layout) {
72        raw_aligned_free(ptr.as_ptr().cast(), layout);
73    }
74
75    unsafe fn resize(
76        &mut self,
77        ptr: NonNull<u8>,
78        old_layout: Layout,
79        new_layout: Layout,
80    ) -> Result<NonNull<u8>, Self::SpecificAllocationError> {
81        let new_align = new_layout.align();
82        if old_layout.align() == new_align {
83            let raw_ptr = raw_aligned_realloc(ptr.as_ptr().cast(), old_layout, new_layout.size());
84            if raw_ptr.is_null() {
85                return Err(StdAllocationError::AllocationError);
86            }
87
88            if raw_ptr.align_offset(new_align) != 0 {
89                raw_aligned_free(raw_ptr.cast(), new_layout);
90                return Err(StdAllocationError::MisalignedResult);
91            }
92
93            Ok(unsafe { NonNull::new_unchecked(raw_ptr.cast()) })
94        } else {
95            unsafe {
96                let new_ptr = self.allocate(new_layout)?;
97                let size = core::cmp::min(new_layout.size(), old_layout.size());
98                new_ptr.copy_from_nonoverlapping(ptr, size);
99                self.deallocate(ptr, old_layout);
100                Ok(new_ptr)
101            }
102        }
103    }
104}
105
106#[inline(always)]
107fn raw_aligned_malloc(layout: Layout) -> *mut u8 {
108    unsafe { alloc::alloc::alloc(layout) }
109}
110
111#[inline(always)]
112fn raw_aligned_realloc(ptr: *mut u8, old_layout: Layout, new_size: usize) -> *mut u8 {
113    unsafe { alloc::alloc::realloc(ptr, old_layout, new_size) }
114}
115
116#[inline(always)]
117fn raw_aligned_free(ptr: *mut u8, layout: Layout) {
118    unsafe { alloc::alloc::dealloc(ptr, layout) }
119}