agb/agb_alloc/
mod.rs

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/// This is the allocator for the External Working Ram.
65///
66/// This is currently equivalent to the Global Allocator (where things are allocated if no allocator is provided).
67/// This implements the allocator trait, so is meant to be used in specifying where certain structures should be
68/// allocated.
69///
70/// ```rust,no_run
71/// #![feature(allocator_api)]
72/// # #![no_std]
73/// # #![no_main]
74/// # use agb::ExternalAllocator;
75/// # extern crate alloc;
76/// # use alloc::vec::Vec;
77/// # fn foo(gba: &mut agb::Gba) {
78/// let mut v = Vec::new_in(ExternalAllocator);
79/// v.push("hello, world");
80/// assert!(
81///     (0x0200_0000..0x0204_0000).contains(&(v.as_ptr() as usize)),
82///     "the address of the vector is inside ewram"
83/// );
84/// # }
85/// ```
86#[derive(Clone)]
87pub struct ExternalAllocator;
88
89impl_zst_allocator!(ExternalAllocator, GLOBAL_ALLOC);
90
91/// This is the allocator for the Internal Working Ram. This implements the
92/// allocator trait, so is meant to be used in specifying where certain
93/// structures should be allocated.
94///
95/// ```rust,no_run
96/// #![feature(allocator_api)]
97/// # #![no_std]
98/// # #![no_main]
99/// # use agb::InternalAllocator;
100/// # extern crate alloc;
101/// # use alloc::vec::Vec;
102/// # fn foo(gba: &mut agb::Gba) {
103/// let mut v = Vec::new_in(InternalAllocator);
104/// v.push("hello, world");
105/// assert!(
106///     (0x0300_0000..0x0300_8000).contains(&(v.as_ptr() as usize)),
107///     "the address of the vector is inside iwram"
108/// );
109/// # }
110/// ```
111#[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    // Symbols defined in the linker have an address *but no data or value*.
129    // As strange as this looks, they are only useful to take the address of.
130    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    // Symbols defined in the linker have an address *but no data or value*.
139    // As strange as this looks, they are only useful to take the address of.
140    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}