ctru/linear.rs
1//! LINEAR memory allocator.
2//!
3//! LINEAR memory is a sector of the 3DS' RAM that binds virtual addresses exactly to the physical address.
4//! As such, it is used for fast and safe memory sharing between different hardware components (such as the GPU and the DSP processor).
5//!
6//! # Additional Resources
7//!
8//! - <https://github.com/devkitPro/libctru/blob/master/libctru/source/allocator/linear.cpp>
9//! - <https://www.3dbrew.org/wiki/Memory_layout>
10
11use std::alloc::{AllocError, Allocator, Layout};
12use std::ptr::NonNull;
13use std::rc::{self, Rc};
14use std::sync::{self, Arc};
15
16// Implementing an `std::alloc::Allocator` type is the best way to handle this case, since it gives
17// us full control over the normal `std` implementations (like `Box`). The only issue is that this is another unstable feature to add.
18// Sadly the linear memory allocator included in `libctru` doesn't implement `linearRealloc` at the time of these additions,
19// but the default fallback of the `std` will take care of that for us.
20
21/// [`Allocator`] struct for LINEAR memory.
22///
23/// To use this struct the main crate must activate the `allocator_api` unstable feature.
24#[derive(Copy, Clone, Default, Debug)]
25pub struct LinearAllocator;
26
27impl LinearAllocator {
28 /// Returns the amount of free space left in the LINEAR memory sector.
29 #[doc(alias = "linearSpaceFree")]
30 pub fn free_space() -> u32 {
31 unsafe { ctru_sys::linearSpaceFree() }
32 }
33}
34
35unsafe impl Allocator for LinearAllocator {
36 #[doc(alias = "linearAlloc", alias = "linearMemAlign")]
37 fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
38 let pointer = unsafe { ctru_sys::linearMemAlign(layout.size(), layout.align()) };
39
40 NonNull::new(pointer.cast())
41 .map(|ptr| NonNull::slice_from_raw_parts(ptr, layout.size()))
42 .ok_or(AllocError)
43 }
44
45 #[doc(alias = "linearFree")]
46 unsafe fn deallocate(&self, ptr: NonNull<u8>, _layout: Layout) {
47 unsafe {
48 ctru_sys::linearFree(ptr.as_ptr().cast());
49 }
50 }
51}
52
53/// Trait indicating a type has been allocated using [`LinearAllocator`].
54/// This can be used to enforce that a given slice was allocated in LINEAR memory.
55///
56/// # Safety
57///
58/// Implementing this trait is a promise that the backing storage was allocated with
59/// [`LinearAllocator`]. If this is not the case, attempting to use the
60/// data with a `LinearAllocation` bound may result in undefined behavior.
61#[diagnostic::on_unimplemented(
62 message = "{Self} is not allocated with `ctru::linear::LinearAllocator`"
63)]
64pub unsafe trait LinearAllocation {}
65
66unsafe impl<T> LinearAllocation for Vec<T, LinearAllocator> {}
67unsafe impl<T: ?Sized> LinearAllocation for Rc<T, LinearAllocator> {}
68unsafe impl<T: ?Sized> LinearAllocation for rc::Weak<T, LinearAllocator> {}
69unsafe impl<T: ?Sized> LinearAllocation for Arc<T, LinearAllocator> {}
70unsafe impl<T: ?Sized> LinearAllocation for sync::Weak<T, LinearAllocator> {}
71unsafe impl<T: ?Sized> LinearAllocation for Box<T, LinearAllocator> {}
72
73// We could also impl for various std::collections types, but it seems unlikely
74// those would ever be used for this purpose in practice, since most of the type
75// we're dereferencing to a &[T]. The workaround would just be to convert to a Vec/Box.