1#[cfg(not(any(feature = "std", feature = "rustc-dep-of-std")))]
6use safa_abi::ffi::{option::OptZero, slice::Slice};
7use safa_abi::mem::MemMapFlags;
8
9use crate::sync::locks::Mutex;
10
11use super::syscalls;
12use core::{alloc::GlobalAlloc, ptr::NonNull};
13
14#[derive(Debug, Default)]
15struct Block {
16 free: bool,
17 next: Option<NonNull<Block>>,
18 data_len: usize,
19}
20
21fn sys_allocate(size_hint: usize) -> Option<(*mut u8, usize)> {
22 let page_count = size_hint.next_multiple_of(4096) / 4096;
23 let (_, s) = syscalls::mem::map(
24 core::ptr::null(),
25 page_count,
26 0,
27 None,
28 None,
29 MemMapFlags::WRITE,
30 )
31 .ok()?;
32
33 Some((s.as_ptr() as *mut u8, s.len()))
34}
35
36impl Block {
37 pub fn create(data_len: usize) -> Option<(NonNull<Self>, Option<NonNull<Block>>)> {
39 let data_len = data_len.next_multiple_of(size_of::<Block>());
40 let size = data_len + size_of::<Block>();
41 let size = size.next_multiple_of(align_of::<Block>());
42 assert!(size <= isize::MAX as usize);
43
44 let (alloc_ptr, alloc_size) = sys_allocate(size)?;
45 assert!(alloc_size >= size);
46
47 let ptr = alloc_ptr as *mut Block;
48
49 unsafe {
50 *ptr = Self {
51 free: true,
52 data_len: size - size_of::<Block>(),
53 ..Default::default()
54 };
55
56 if alloc_size > size {
57 let extra_block_size = alloc_size - size;
58 let extra_block_ptr = alloc_ptr.add(size) as *mut Block;
59 *extra_block_ptr = Self {
60 free: true,
61 data_len: extra_block_size - size_of::<Block>(),
62 ..Default::default()
63 };
64
65 (*ptr).next = Some(NonNull::new_unchecked(extra_block_ptr));
66 }
67 Some((NonNull::new_unchecked(ptr), (*ptr).next))
68 }
69 }
70
71 #[inline(always)]
72 pub unsafe fn block_from_data_ptr(data: NonNull<u8>) -> NonNull<Self> {
75 unsafe { NonNull::new_unchecked((data.as_ptr() as *mut Block).offset(-1)) }
76 }
77
78 #[inline(always)]
79 pub unsafe fn data_from_ptr(ptr: *const Self) -> NonNull<[u8]> {
81 unsafe {
82 let length = (*ptr).data_len;
83 let ptr_to_data = ptr.offset(1) as *const u8 as *mut u8;
84 NonNull::new_unchecked(core::slice::from_raw_parts_mut(ptr_to_data, length) as *mut [u8])
85 }
86 }
87}
88
89pub struct SystemAllocator {
90 head: Option<NonNull<Block>>,
91}
92
93impl SystemAllocator {
94 const fn new() -> Self {
95 Self { head: None }
96 }
97
98 #[inline]
100 fn try_find_block(&self, data_len: usize) -> Option<NonNull<Block>> {
101 let size = data_len + size_of::<Block>();
103 let size = size.next_multiple_of(align_of::<Block>());
104 let data_len = size - size_of::<Block>();
105
106 let mut current = self.head;
107 let mut best_block: Option<(NonNull<Block>, usize)> = None;
108
109 while let Some(block_ptr) = current {
110 let block = unsafe { &*block_ptr.as_ptr() };
111 if !block.free {
112 current = block.next;
113 continue;
114 }
115
116 if block.data_len == data_len {
117 return Some(block_ptr);
118 }
119
120 if block.data_len > data_len
121 && best_block.is_none_or(|(_, bb_len)| bb_len > block.data_len)
122 {
123 best_block = Some((block_ptr, block.data_len));
124 }
125
126 current = block.next;
127 }
128
129 best_block.map(|(ptr, _)| ptr)
130 }
131
132 #[inline]
135 fn find_block(&mut self, data_len: usize) -> Option<NonNull<Block>> {
136 let data_len = data_len.next_multiple_of(size_of::<Block>());
137
138 if let Some(block) = self.try_find_block(data_len) {
139 let block_ptr = block.as_ptr();
140
141 unsafe {
142 let block_len = (*block_ptr).data_len;
143
144 if block_len > data_len && (block_len - data_len) > size_of::<Block>() {
146 let left_over = block_len - data_len;
147 let new_block_len = left_over - size_of::<Block>();
148
149 let new_block = block_ptr.add(1).byte_add(data_len);
150 *new_block = Block {
151 free: true,
152 data_len: new_block_len,
153 next: (*block_ptr).next.take(),
154 };
155
156 (*block_ptr).next = Some(NonNull::new_unchecked(new_block));
157 (*block_ptr).data_len = data_len;
158 }
159 }
160 Some(block)
161 } else {
162 unsafe {
163 let (new_block, new_allocation_tail) = Block::create(data_len)?;
164 let set_next_of = new_allocation_tail.unwrap_or(new_block);
165 let stolen_head = self.head.take();
166
167 (*set_next_of.as_ptr()).next = stolen_head;
168 self.head = Some(new_block);
169
170 Some(new_block)
171 }
172 }
173 }
174
175 fn merge_blocks(&mut self) {
176 let mut current = self.head;
177 while let Some(block_ptr) = current {
178 unsafe {
179 let block = block_ptr.as_ptr();
180 if !(*block).free {
181 current = (*block).next;
182 continue;
183 }
184
185 let Some(next) = (*block).next else {
186 return;
187 };
188
189 let next_ptr = next.as_ptr();
190 if !(*next_ptr).free {
191 current = (*block).next;
192 continue;
193 }
194
195 if block.add(1).byte_add((*block).data_len) == next_ptr {
196 (*block).next = (*next_ptr).next;
198 (*block).data_len += (*next_ptr).data_len + size_of::<Block>();
199 }
200
201 current = (*block).next;
202 }
203 }
204 }
205
206 fn allocate(&mut self, size: usize) -> Option<NonNull<[u8]>> {
207 let block = self.find_block(size)?;
208 unsafe {
209 let ptr = block.as_ptr();
210 (*ptr).free = false;
211 Some(Block::data_from_ptr(ptr))
212 }
213 }
214
215 unsafe fn deallocate(&mut self, block_data: NonNull<u8>) {
216 unsafe {
217 let block_ptr = Block::block_from_data_ptr(block_data).as_ptr();
218 let block = &mut *block_ptr;
219 block.free = true;
220
221 self.merge_blocks();
222 }
223 }
224}
225
226unsafe impl Send for SystemAllocator {}
227unsafe impl Sync for SystemAllocator {}
228
229pub struct GlobalSystemAllocator {
231 inner: Mutex<SystemAllocator>,
232}
233
234impl GlobalSystemAllocator {
235 const fn new() -> Self {
236 Self {
237 inner: Mutex::new(SystemAllocator::new()),
238 }
239 }
240
241 #[inline]
242 pub fn allocate(&self, size: usize) -> Option<NonNull<[u8]>> {
243 self.inner.lock().allocate(size)
244 }
245
246 #[inline]
247 pub unsafe fn deallocate(&self, ptr: NonNull<u8>) {
248 self.inner.lock().deallocate(ptr)
249 }
250
251 }
253
254unsafe impl Sync for GlobalSystemAllocator {}
255unsafe impl Send for GlobalSystemAllocator {}
256
257unsafe impl GlobalAlloc for GlobalSystemAllocator {
258 unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
259 self.allocate(layout.size())
260 .map(|x| x.as_ptr() as *mut u8)
261 .unwrap_or(core::ptr::null_mut())
262 }
263
264 unsafe fn dealloc(&self, ptr: *mut u8, _: core::alloc::Layout) {
265 self.deallocate(NonNull::new_unchecked(ptr));
266 }
267}
268
269#[cfg_attr(
270 not(any(feature = "std", feature = "rustc-dep-of-std")),
271 global_allocator
272)]
273pub static GLOBAL_SYSTEM_ALLOCATOR: GlobalSystemAllocator = GlobalSystemAllocator::new();
276
277#[cfg(not(any(feature = "std", feature = "rustc-dep-of-std")))]
278#[unsafe(no_mangle)]
279pub extern "C" fn syscreate(object_size: usize) -> OptZero<Slice<u8>> {
281 GLOBAL_SYSTEM_ALLOCATOR
282 .allocate(object_size)
283 .map(|mut x| unsafe {
284 let x = x.as_mut();
285 Slice::from_raw_parts(x.as_mut_ptr(), x.len())
286 })
287 .into()
288}
289
290#[cfg(not(any(feature = "std", feature = "rustc-dep-of-std")))]
291#[unsafe(no_mangle)]
292pub unsafe extern "C" fn sysdestroy(object_ptr: *mut u8) {
296 unsafe {
297 match NonNull::new(object_ptr) {
298 Some(ptr) => GLOBAL_SYSTEM_ALLOCATOR.deallocate(ptr),
299 None => (),
300 }
301 }
302}