safa_api/
alloc.rs

1use crate::raw::{NonNullSlice, Optional};
2
3use super::syscalls;
4use core::{cell::UnsafeCell, ptr::NonNull};
5
6#[derive(Debug, Default)]
7struct Block {
8    free: bool,
9    next: Option<NonNull<Block>>,
10    data_len: usize,
11}
12
13impl Block {
14    /// Asks the system for a new memory Block with a size big enough to hold `data_len` bytes
15    pub fn create(data_len: usize) -> Option<NonNull<Self>> {
16        let size = data_len + size_of::<Block>();
17        let size = size.next_multiple_of(align_of::<Block>());
18        assert!(size <= isize::MAX as usize);
19
20        let ptr = get_data_break() as *mut Block;
21        syscalls::sbrk(size as isize).ok()?;
22        unsafe {
23            *ptr = Self {
24                free: true,
25                data_len: size - size_of::<Block>(),
26                ..Default::default()
27            };
28            Some(NonNull::new_unchecked(ptr))
29        }
30    }
31
32    #[inline(always)]
33    /// Gets the Block metadata of a data ptr,
34    /// unsafe because the pointer had to be made by calling `[Block::data_from_ptr]` on a vaild pointer, otherwise the returned value is invaild
35    pub unsafe fn block_from_data_ptr(data: NonNull<u8>) -> NonNull<Self> {
36        unsafe { NonNull::new_unchecked((data.as_ptr() as *mut Block).offset(-1)) }
37    }
38
39    #[inline(always)]
40    /// Gets the data ptr from a pointer to Block
41    pub unsafe fn data_from_ptr(ptr: *const Self) -> NonNull<[u8]> {
42        unsafe {
43            let length = (*ptr).data_len;
44            let ptr_to_data = ptr.offset(1) as *const u8 as *mut u8;
45            NonNull::new_unchecked(core::slice::from_raw_parts_mut(ptr_to_data, length) as *mut [u8])
46        }
47    }
48}
49
50pub struct SystemAllocator {
51    head: Option<NonNull<Block>>,
52}
53
54fn get_data_break() -> *mut u8 {
55    // Should never fail
56    unsafe { syscalls::sbrk(0).unwrap_unchecked() }
57}
58
59impl SystemAllocator {
60    const fn new() -> Self {
61        Self { head: None }
62    }
63
64    /// tries to find a block with enough space for `data_len` bytes
65    #[inline]
66    fn try_find_block(&self, data_len: usize) -> Option<NonNull<Block>> {
67        // To optimize the search for exact size we have to manipulate the data_len a bit
68        let size = data_len + size_of::<Block>();
69        let size = size.next_multiple_of(align_of::<Block>());
70        let data_len = size - size_of::<Block>();
71
72        let mut current = self.head;
73        let mut best_block: Option<(NonNull<Block>, usize)> = None;
74
75        while let Some(block_ptr) = current {
76            let block = unsafe { &*block_ptr.as_ptr() };
77            if !block.free {
78                current = block.next;
79                continue;
80            }
81
82            if block.data_len == data_len {
83                return Some(block_ptr);
84            }
85
86            if block.data_len > data_len
87                && best_block.is_none_or(|(_, bb_len)| bb_len > block.data_len)
88            {
89                best_block = Some((block_ptr, block.data_len));
90            }
91
92            current = block.next;
93        }
94
95        best_block.map(|(ptr, _)| ptr)
96    }
97
98    /// finds a block with enough space for `data_len` bytes
99    /// or creates a new one if there is no enough space
100    #[inline]
101    fn find_block(&mut self, data_len: usize) -> Option<NonNull<Block>> {
102        if let Some(block) = self.try_find_block(data_len) {
103            Some(block)
104        } else {
105            unsafe {
106                let new_block = Block::create(data_len)?;
107                let stolen_head = self.head.take();
108
109                (*new_block.as_ptr()).next = stolen_head;
110                self.head = Some(new_block);
111
112                Some(new_block)
113            }
114        }
115    }
116
117    fn allocate(&mut self, size: usize) -> Option<NonNull<[u8]>> {
118        let block = self.find_block(size)?;
119        unsafe {
120            let ptr = block.as_ptr();
121            (*ptr).free = false;
122            Some(Block::data_from_ptr(ptr))
123        }
124    }
125
126    unsafe fn deallocate(&mut self, block_data: NonNull<u8>) {
127        unsafe {
128            let block_ptr = Block::block_from_data_ptr(block_data).as_ptr();
129            let block = &mut *block_ptr;
130            block.free = true;
131        }
132    }
133}
134
135unsafe impl Send for SystemAllocator {}
136unsafe impl Sync for SystemAllocator {}
137
138// FIXME: implement locks before multithreading
139pub struct GlobalSystemAllocator {
140    inner: UnsafeCell<SystemAllocator>,
141}
142
143impl GlobalSystemAllocator {
144    const fn new() -> Self {
145        Self {
146            inner: UnsafeCell::new(SystemAllocator::new()),
147        }
148    }
149    #[inline]
150    pub fn allocate(&self, size: usize) -> Option<NonNull<[u8]>> {
151        unsafe { (*self.inner.get()).allocate(size) }
152    }
153    #[inline]
154    pub unsafe fn deallocate(&self, ptr: NonNull<u8>) {
155        unsafe { (*self.inner.get()).deallocate(ptr) }
156    }
157
158    // TODO: implement grow and shrink
159}
160
161unsafe impl Sync for GlobalSystemAllocator {}
162unsafe impl Send for GlobalSystemAllocator {}
163
164pub static GLOBAL_SYSTEM_ALLOCATOR: GlobalSystemAllocator = GlobalSystemAllocator::new();
165
166#[cfg(not(any(feature = "std", feature = "rustc-dep-of-std")))]
167#[unsafe(no_mangle)]
168/// Allocates an object sized `object_size` using [`GLOBAL_SYSTEM_ALLOCATOR`]
169extern "C" fn syscreate(object_size: usize) -> Optional<NonNullSlice<u8>> {
170    GLOBAL_SYSTEM_ALLOCATOR
171        .allocate(object_size)
172        .map(|mut x| unsafe {
173            let x = x.as_mut();
174            let len = x.len();
175            let ptr = NonNull::new_unchecked(x.as_mut_ptr());
176            NonNullSlice::from_raw_parts(ptr, len)
177        })
178        .into()
179}
180
181#[cfg(not(any(feature = "std", feature = "rustc-dep-of-std")))]
182#[unsafe(no_mangle)]
183unsafe extern "C" fn sysdestroy(object_ptr: *mut u8) {
184    unsafe {
185        match NonNull::new(object_ptr) {
186            Some(ptr) => GLOBAL_SYSTEM_ALLOCATOR.deallocate(ptr),
187            None => (),
188        }
189    }
190}