playdate_allocator/
global.rs

1use core::alloc::{GlobalAlloc, Layout};
2use core::ffi::c_void;
3
4use crate::System;
5
6
7/// Global Playdate [System] allocator.
8#[global_allocator]
9#[cfg(feature = "global")]
10pub static GLOBAL: System = System;
11
12
13unsafe impl GlobalAlloc for System {
14	#[inline]
15	unsafe fn alloc(&self, layout: Layout) -> *mut u8 { realloc(core::ptr::null_mut(), layout.size()) as *mut u8 }
16	#[inline]
17	unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { dealloc(ptr as *mut c_void); }
18	#[inline]
19	unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
20		let res = realloc(ptr as *mut c_void, new_size) as *mut u8;
21
22		// default mem-copy- behavior if new != old:
23		if !res.is_null() && ptr != res {
24			// SAFETY: the previously allocated block cannot overlap the newly allocated block.
25			// The safety contract for `dealloc` must be upheld by the caller.
26			unsafe {
27				core::ptr::copy_nonoverlapping(ptr, res, core::cmp::min(layout.size(), new_size));
28				self.dealloc(ptr, layout);
29			}
30		}
31
32		res
33	}
34
35	// `alloc_zeroed` is default impl because Playdate's system allocator
36	// as well as Symulator's returns NOT-zeroed memory.
37	// alloc's default impl is fills zeroes after allocation.
38}
39
40
41#[track_caller]
42#[inline(always)]
43pub unsafe fn realloc(ptr: *mut c_void, size: usize) -> *mut c_void { crate::get()(ptr, size) }
44
45
46#[track_caller]
47#[inline(always)]
48pub unsafe fn dealloc(ptr: *mut c_void) { realloc(ptr, 0); }
49
50
51/// Global handler for an Out Of Memory (OOM) condition
52#[track_caller]
53#[alloc_error_handler]
54#[cfg(feature = "global-error-handler")]
55fn alloc_error(layout: Layout) -> ! {
56	type Error = unsafe extern "C" fn(fmt: *const core::ffi::c_char, ...) -> !;
57	unsafe extern "Rust" {
58		#[link_name = "PDERR"]
59		pub static ERROR: core::mem::MaybeUninit<Error>;
60	}
61	if unsafe { ERROR.as_ptr() }.is_null() {
62		use core::intrinsics::is_val_statically_known;
63		if is_val_statically_known(layout.size() != 0) {
64			// const-known, so alloc-less
65			panic!("OoM: {}b", layout.size())
66		} else {
67			// alloc-less panic
68			panic!("OoM")
69		}
70	} else {
71		use numtoa_like::*;
72		// rendes size to ascii inplace:
73		let mut s = [79, 111, 77, 58, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 0];
74		let mut index = s.len() - 3;
75		render(layout.size(), &mut index, &mut s);
76		s[5..].rotate_left(index.wrapping_sub(3));
77
78		// alloc-less panic via pd-err
79		unsafe {
80			let f = ERROR.assume_init();
81			f(s.as_ptr().cast())
82		}
83	}
84}
85
86
87#[cfg(feature = "global-error-handler")]
88mod numtoa_like {
89	// A lookup table to prevent the need for conditional branching
90	// The value of the remainder of each step will be used as the index
91	const LOOKUP: &[u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
92	// A lookup table optimized for decimal lookups. Each two indices represents one possible number.
93	const DEC_LOOKUP: &[u8; 200] = b"0001020304050607080910111213141516171819\
94                                    2021222324252627282930313233343536373839\
95                                    4041424344454647484950515253545556575859\
96                                    6061626364656667686970717273747576777879\
97                                    8081828384858687888990919293949596979899";
98
99	/// Render `value` to string `buf`, left-aligning.
100	/// Modifies `index` pointing to the start of actual string in `buf`.
101	pub fn render(value: usize, index: &mut usize, buf: &mut [u8]) {
102		let mut v = value;
103		// Decode four characters at the same time
104		while v > 9999 {
105			let rem = (v % 10000) as u16;
106			let (frst, scnd) = ((rem / 100) * 2, (rem % 100) * 2);
107			buf[*index - 3..*index - 1].copy_from_slice(&DEC_LOOKUP[frst as usize..frst as usize + 2]);
108			buf[*index - 1..*index + 1].copy_from_slice(&DEC_LOOKUP[scnd as usize..scnd as usize + 2]);
109			*index = index.wrapping_sub(4);
110			v /= 10000;
111		}
112		if v > 999 {
113			let (frst, scnd) = ((v / 100) * 2, (v % 100) * 2);
114			buf[*index - 3..*index - 1].copy_from_slice(&DEC_LOOKUP[frst as usize..frst as usize + 2]);
115			buf[*index - 1..*index + 1].copy_from_slice(&DEC_LOOKUP[scnd as usize..scnd as usize + 2]);
116			*index = index.wrapping_sub(4);
117		} else if v > 99 {
118			let section = (v as u16 / 10) * 2;
119			buf[*index - 2..*index].copy_from_slice(&DEC_LOOKUP[section as usize..section as usize + 2]);
120			buf[*index] = LOOKUP[(v % 10) as usize];
121			*index = index.wrapping_sub(3);
122		} else if v > 9 {
123			v *= 2;
124			buf[*index - 1..*index + 1].copy_from_slice(&DEC_LOOKUP[v as usize..v as usize + 2]);
125			*index = index.wrapping_sub(2);
126		} else {
127			buf[*index] = LOOKUP[v as usize];
128			*index = index.wrapping_sub(1);
129		}
130	}
131}