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