1use core::ops::{Deref, DerefMut};
2use core::ptr::NonNull;
3
4pub(crate) mod block_allocator;
5pub(crate) mod bump_allocator;
6
7use block_allocator::BlockAllocator;
8
9use self::bump_allocator::StartEnd;
10
11struct SendNonNull<T>(NonNull<T>);
12unsafe impl<T> Send for SendNonNull<T> {}
13
14impl<T> Clone for SendNonNull<T> {
15 fn clone(&self) -> Self {
16 *self
17 }
18}
19impl<T> Copy for SendNonNull<T> {}
20
21impl<T> Deref for SendNonNull<T> {
22 type Target = NonNull<T>;
23 fn deref(&self) -> &Self::Target {
24 &self.0
25 }
26}
27
28impl<T> DerefMut for SendNonNull<T> {
29 fn deref_mut(&mut self) -> &mut Self::Target {
30 &mut self.0
31 }
32}
33
34const EWRAM_END: usize = 0x0204_0000;
35const IWRAM_END: usize = 0x0300_8000;
36
37#[global_allocator]
38static GLOBAL_ALLOC: BlockAllocator = unsafe {
39 BlockAllocator::new(StartEnd {
40 start: data_end,
41 end: || EWRAM_END,
42 })
43};
44
45macro_rules! impl_zst_allocator {
46 ($name_of_struct: ty, $name_of_static: ident) => {
47 unsafe impl core::alloc::Allocator for $name_of_struct {
48 fn allocate(
49 &self,
50 layout: core::alloc::Layout,
51 ) -> Result<core::ptr::NonNull<[u8]>, core::alloc::AllocError> {
52 $name_of_static.allocate(layout)
53 }
54
55 unsafe fn deallocate(&self, ptr: core::ptr::NonNull<u8>, layout: core::alloc::Layout) {
56 $name_of_static.deallocate(ptr, layout)
57 }
58 }
59 };
60}
61
62pub(crate) use impl_zst_allocator;
63
64#[derive(Clone)]
87pub struct ExternalAllocator;
88
89impl_zst_allocator!(ExternalAllocator, GLOBAL_ALLOC);
90
91#[derive(Clone)]
112pub struct InternalAllocator;
113
114impl_zst_allocator!(InternalAllocator, __IWRAM_ALLOC);
115
116static __IWRAM_ALLOC: BlockAllocator = unsafe {
117 BlockAllocator::new(StartEnd {
118 start: iwram_data_end,
119 end: || IWRAM_END,
120 })
121};
122
123fn iwram_data_end() -> usize {
124 extern "C" {
125 static __iwram_end: u8;
126 }
127
128 core::ptr::addr_of!(__iwram_end) as usize
131}
132
133fn data_end() -> usize {
134 extern "C" {
135 static __ewram_data_end: u8;
136 }
137
138 core::ptr::addr_of!(__ewram_data_end) as usize
141}
142
143#[cfg(test)]
144mod test {
145 const EWRAM_START: usize = 0x0200_0000;
146
147 use super::*;
148 use alloc::boxed::Box;
149 use alloc::vec;
150 use alloc::vec::Vec;
151
152 #[test_case]
153 fn test_box(_gba: &mut crate::Gba) {
154 let first_box = Box::new(1);
155 let second_box = Box::new(2);
156
157 assert!(&*first_box as *const _ < &*second_box as *const _);
158 assert_eq!(*first_box, 1);
159 assert_eq!(*second_box, 2);
160
161 let address = &*first_box as *const _ as usize;
162 assert!(
163 (EWRAM_START..EWRAM_END).contains(&address),
164 "ewram is located between 0x0200_0000 and 0x0204_0000, address was actually found to be {address:#010X}"
165 );
166 }
167
168 #[test_case]
169 fn test_vec(_gba: &mut crate::Gba) {
170 let mut v = Vec::with_capacity(5);
171
172 for i in 0..100 {
173 v.push(i);
174 }
175
176 for (i, &e) in v.iter().enumerate() {
177 assert_eq!(e, i);
178 }
179 }
180
181 #[test_case]
182 fn test_creating_and_removing_things(_gba: &mut crate::Gba) {
183 let item = Box::new(1);
184 for i in 0..1_000 {
185 let x = Box::new(i);
186 assert_eq!(*x, i);
187 let address = &*x as *const _ as usize;
188 assert!(
189 (EWRAM_START..EWRAM_END).contains(&address),
190 "ewram is located between 0x0200_0000 and 0x0204_0000, address was actually found to be {address:#010X}"
191 );
192 }
193
194 assert_eq!(*item, 1);
195 }
196
197 #[test_case]
198 fn test_adding_to_2_different_vectors(_gba: &mut crate::Gba) {
199 let mut v1 = vec![1, 2, 3];
200 let mut v2 = vec![4, 5, 6];
201
202 for i in 0..100 {
203 v1.push(i + 100);
204 v2.push(i + 1000);
205 }
206
207 assert_eq!(v1[40], 137);
208 assert_eq!(v2[78], 1075);
209 }
210
211 #[test_case]
212 fn should_return_data_end_somewhere_in_ewram(_gba: &mut crate::Gba) {
213 let data_end = data_end();
214
215 assert!(
216 0x0200_0000 <= data_end,
217 "data end should be bigger than 0x0200_0000, got {data_end}"
218 );
219 assert!(
220 0x0204_0000 > data_end,
221 "data end should be smaller than 0x0203_0000"
222 );
223 }
224
225 #[test_case]
226 fn should_return_data_end_somewhere_in_iwram(_gba: &mut crate::Gba) {
227 let data_end = iwram_data_end();
228
229 assert!(
230 (0x0300_0000..0x0300_8000).contains(&data_end),
231 "iwram data end should be in iwram, instead was {data_end}"
232 );
233 }
234
235 #[test_case]
236 fn allocate_to_iwram_works(_gba: &mut crate::Gba) {
237 let a = Box::new_in(1, InternalAllocator);
238 let p = &*a as *const i32;
239 let addr = p as usize;
240 assert!(
241 (0x0300_0000..0x0300_8000).contains(&addr),
242 "address of allocation should be within iwram, instead at {p:?}"
243 );
244 }
245
246 #[test_case]
247 fn benchmark_allocation(_gba: &mut crate::Gba) {
248 let mut stored: Vec<Vec<u8>> = Vec::new();
249
250 let mut rng = crate::rng::RandomNumberGenerator::new();
251
252 const MAX_VEC_LENGTH: usize = 100;
253
254 enum Action {
255 Add { size: usize },
256 Remove { index: usize },
257 }
258
259 let next_action = |rng: &mut crate::rng::RandomNumberGenerator, stored: &[Vec<u8>]| {
260 if stored.len() >= MAX_VEC_LENGTH {
261 Action::Remove {
262 index: (rng.gen() as usize) % stored.len(),
263 }
264 } else if stored.is_empty() || rng.gen() as usize % 4 != 0 {
265 Action::Add {
266 size: rng.gen() as usize % 32,
267 }
268 } else {
269 Action::Remove {
270 index: (rng.gen() as usize) % stored.len(),
271 }
272 }
273 };
274
275 for _ in 0..10000 {
276 match next_action(&mut rng, &stored) {
277 Action::Add { size } => {
278 stored.push(Vec::with_capacity(size));
279 }
280 Action::Remove { index } => {
281 stored.swap_remove(index);
282 }
283 }
284 }
285 }
286
287 #[test_case]
288 fn growth_works(_gba: &mut crate::Gba) {
289 let mut growing_vector = Vec::with_capacity(1);
290
291 for i in 0..1000 {
292 growing_vector.push(i);
293 growing_vector.reserve_exact(i + 2);
294
295 for (idx, elem) in growing_vector.iter().enumerate() {
296 assert_eq!(idx, *elem);
297 }
298 }
299 }
300}