playdate_allocator/
lib.rs1#![no_std]
2#![cfg_attr(feature = "allocator-api", feature(allocator_api, slice_ptr_get))]
3#![cfg_attr(feature = "global-error-handler",
4 feature(alloc_error_handler, core_intrinsics),
5 allow(internal_features))]
6#![cfg_attr(any(test, debug_assertions, not(feature = "static-link")),
7 feature(fn_ptr_trait))]
8
9
10extern crate alloc;
11
12
13#[cfg(feature = "allocator-api")]
14pub(crate) mod local;
15pub(crate) mod global;
16
17
18pub struct System;
25
26
27type Realloc = unsafe extern "C" fn(ptr: *mut c_void, size: usize) -> *mut c_void;
28
29
30use core::ffi::c_void;
31
32#[cfg(not(feature = "static-link"))]
34static mut REALLOC: Realloc = fake;
35
36
37#[cold]
40#[cfg(not(feature = "static-link"))]
41unsafe extern "C" fn fake(_: *mut c_void, _: usize) -> *mut c_void { core::ptr::null_mut() }
42
43
44unsafe extern "C" {
45 #[cfg(feature = "static-link")]
47 fn pdrealloc(ptr: *mut c_void, size: usize) -> *mut c_void;
48}
49
50
51#[inline(always)]
52#[cfg(debug_assertions)]
53pub fn init(realloc: Realloc) {
54 use core::marker::FnPtr;
55
56 debug_assert!(!realloc.addr().is_null());
57 init_realloc(realloc)
58}
59
60
61#[inline(always)]
62#[cfg(not(debug_assertions))]
63pub const fn init(realloc: Realloc) { init_realloc(realloc) }
64
65
66#[inline(always)]
67#[cfg_attr(feature = "static-link",
68 doc = "\n no-op because [`realloc`] is linked statically")]
69const fn init_realloc(#[cfg_attr(feature = "static-link", allow(unused_variables))] realloc: Realloc) {
70 #[cfg(not(feature = "static-link"))]
71 unsafe {
72 REALLOC = realloc
73 }
74}
75
76#[inline(always)]
77#[cfg(feature = "static-link")]
78pub const fn is_inited() -> bool { true }
79
80
81#[cfg(not(feature = "static-link"))]
82pub fn is_inited() -> bool {
83 use core::ptr::fn_addr_eq;
84 unsafe { !fn_addr_eq(REALLOC, fake as Realloc) }
85}
86
87
88#[inline(always)]
89#[cfg(debug_assertions)]
90fn get() -> Realloc {
91 let realloc = get_unchecked();
92
93 #[cfg(not(feature = "static-link"))]
94 debug_assert!(!core::marker::FnPtr::addr(realloc).is_null(), "missed realloc");
95
96 realloc
97}
98
99#[inline(always)]
100#[cfg(not(debug_assertions))]
101const fn get() -> Realloc { get_unchecked() }
102
103
104#[inline(always)]
105const fn get_unchecked() -> Realloc {
106 #[cfg(feature = "static-link")]
107 {
108 pdrealloc
109 }
110 #[cfg(not(feature = "static-link"))]
111 {
112 unsafe { REALLOC }
113 }
114}
115
116
117#[cfg(test)]
118#[cfg(not(feature = "global"))]
119mod tests {
120 #![allow(unexpected_cfgs)] use core::ptr::null_mut;
126 use super::*;
127
128
129 #[test]
130 #[cfg_attr(feature = "static-link", ignore = "for static-mut only")]
131 fn not_inited() {
132 #[cfg(not(feature = "static-link"))]
133 unsafe {
134 REALLOC = fake
135 }
136 assert!(!is_inited());
137 }
138
139 #[test]
140 fn inited() {
141 #[cfg(not(feature = "static-link"))]
142 {
143 unsafe { REALLOC = fake }
144 assert!(!is_inited());
145 }
146
147 init_fake();
148
149 assert!(is_inited());
150
151 #[cfg(feature = "static-link")]
152 assert!(!core::marker::FnPtr::addr(pdrealloc as Realloc).is_null());
153 }
154
155
156 #[test]
157 #[cfg_attr(not(fake_alloc), ignore = "set RUSTFLAGS='--cfg=fake_alloc' to enable.")]
158 fn get_alloc_fake() {
159 init_fake();
160
161 let realloc = get();
162 let p = unsafe { realloc(null_mut(), 64) };
163
164 assert!(p.is_null());
165 }
166
167
168 pub(crate) fn init_fake() {
169 #[cfg(not(feature = "static-link"))]
170 {
171 unsafe extern "C" fn fake(_: *mut c_void, _: usize) -> *mut c_void { null_mut() }
173 unsafe { REALLOC = fake }
174 }
175 }
176
177
178 #[no_mangle]
179 #[cfg(fake_alloc)]
180 #[cfg(feature = "static-link")]
181 extern "C" fn pdrealloc(_: *mut c_void, _: usize) -> *mut c_void { null_mut() }
182}