1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#![cfg(not(test))]
//! Global Allocator implementation. Depends on `allocator` feature.


/*
With rust-lang/rust#102318 default_alloc_error_handler has been stabilized,
ie. the default error handler is enabled by default.
Therefore, it's no longer necessary to provide an alloc_error_handler
if the desired error handling is equivalent to a panic.
TODO: think about remove `alloc_error_handler`.
*/


extern crate alloc;

use core::alloc::{GlobalAlloc, Layout};
use core::ffi::c_void;


// #[cfg_attr(feature = "allocator-global", global_allocator)]
#[global_allocator]
#[cfg(feature = "allocator")]
pub static GLOBAL: PlaydateAllocator = PlaydateAllocator;

/// Global handler for an Out Of Memory (OOM) condition
#[alloc_error_handler]
#[cfg(feature = "allocator")]
fn alloc_error(layout: Layout) -> ! { panic!("Out of Memory, requested {}.", layout.size()) }


pub struct PlaydateAllocator;

unsafe impl GlobalAlloc for PlaydateAllocator {
	#[inline]
	unsafe fn alloc(&self, layout: Layout) -> *mut u8 { realloc(core::ptr::null_mut(), layout.size()) as *mut u8 }
	#[inline]
	unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { realloc(ptr as *mut c_void, 0); }
	#[inline]
	unsafe fn realloc(&self, ptr: *mut u8, _layout: Layout, new_size: usize) -> *mut u8 {
		realloc(ptr as *mut c_void, new_size) as *mut u8
	}
}


#[track_caller]
unsafe fn realloc(ptr: *mut c_void, size: usize) -> *mut c_void {
	// Cached pointer to the OS's realloc function.
	static mut REALLOC: Option<unsafe extern "C" fn(ptr: *mut c_void, size: usize) -> *mut c_void> = None;

	let f = REALLOC.get_or_insert_with(|| {
		               if let Some(realloc) = crate::sys::API.as_ref().and_then(|api| (*api.system).realloc) {
			               realloc
		               } else {
			               panic!("Missed API.realloc")
		               }
	               });
	f(ptr, size)
}