use crate::prelude::*;
use core::alloc::{GlobalAlloc, Layout};
use core::ptr::NonNull;
use linked_list_allocator::Heap;
const DEFAULT_EXTERNAL_THRESHOLD: usize = 32 * 1024;
const DEFAULT_USE_IRAM: bool = false;
pub static DEFAULT_ALLOCATOR: Allocator = Allocator::new(&DEFAULT_HEAP);
pub static DRAM_ALLOCATOR: Allocator = Allocator::new(&DRAM_HEAP);
pub static IRAM_ALLOCATOR: Allocator = Allocator::new(&IRAM_HEAP);
#[cfg(feature = "external_ram")]
pub static EXTERNAL_ALLOCATOR: Allocator = Allocator::new(&EXTERNAL_HEAP);
extern "C" {
static _heap_start: u8;
static _heap_end: u8;
static _text_heap_start: u8;
static _text_heap_end: u8;
static _external_heap_start: u8;
static _external_heap_end: u8;
}
static DEFAULT_HEAP: GeneralAllocator =
GeneralAllocator::new(DEFAULT_EXTERNAL_THRESHOLD, DEFAULT_USE_IRAM);
#[allow(dead_code)]
static DRAM_HEAP: LockedHeap = unsafe { LockedHeap::new(&|| &_heap_start, &|| &_heap_end) };
#[allow(dead_code)]
static IRAM_HEAP: LockedHeap =
unsafe { LockedHeap::new(&|| &_text_heap_start, &|| &_text_heap_end) };
#[allow(dead_code)]
#[cfg(feature = "external_ram")]
static EXTERNAL_HEAP: LockedHeap = unsafe {
LockedHeap::new(&|| &_external_heap_start, &|| {
core::cmp::min(
&_external_heap_end,
(&_external_heap_start as *const u8).add(crate::external_ram::get_size()),
)
})
};
pub trait AllocatorSize {
fn size(&self) -> usize;
fn used(&self) -> usize;
fn free(&self) -> usize;
}
unsafe trait GlobalAllocSize: GlobalAlloc + AllocatorSize {}
#[derive(Copy, Clone)]
#[doc(hidden)]
pub struct Allocator {
allocator: &'static (dyn GlobalAllocSize + 'static),
}
unsafe impl Sync for Allocator {}
impl Allocator {
const fn new(allocator: &'static dyn GlobalAllocSize) -> Self {
Self { allocator }
}
}
unsafe impl GlobalAllocSize for Allocator {}
impl AllocatorSize for Allocator {
fn size(&self) -> usize {
self.allocator.size()
}
fn used(&self) -> usize {
self.allocator.used()
}
fn free(&self) -> usize {
self.allocator.free()
}
}
unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
self.allocator.alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.allocator.dealloc(ptr, layout)
}
}
extern crate alloc;
use alloc::alloc::{AllocErr, AllocRef};
unsafe impl AllocRef for Allocator {
fn alloc(&mut self, layout: Layout) -> Result<NonNull<[u8]>, AllocErr> {
if layout.size() == 0 {
return Ok(NonNull::slice_from_raw_parts(layout.dangling(), 0));
}
let ptr = unsafe { GlobalAlloc::alloc(self, layout) };
match NonNull::new(ptr) {
Some(ptr) => Ok(NonNull::slice_from_raw_parts(ptr, layout.size())),
None => Err(AllocErr)
}
}
unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
if layout.size() != 0 {
GlobalAlloc::dealloc(self, ptr.as_ptr(), layout);
}
}
}
pub struct GeneralAllocator {
#[cfg(feature = "external_ram")]
external_threshold: usize,
use_iram: bool,
}
unsafe impl Sync for GeneralAllocator {}
impl GeneralAllocator {
pub const fn new(_external_threshold: usize, use_iram: bool) -> Self {
Self {
#[cfg(feature = "external_ram")]
external_threshold: _external_threshold,
use_iram,
}
}
}
unsafe impl GlobalAllocSize for GeneralAllocator {}
impl AllocatorSize for GeneralAllocator {
fn size(&self) -> usize {
#[cfg(not(feature = "external_ram"))]
let res = DRAM_ALLOCATOR.size() + IRAM_ALLOCATOR.size();
#[cfg(feature = "external_ram")]
let res = DRAM_ALLOCATOR.size() + IRAM_ALLOCATOR.size() + EXTERNAL_ALLOCATOR.size();
res
}
fn used(&self) -> usize {
#[cfg(not(feature = "external_ram"))]
let res = DRAM_ALLOCATOR.used() + IRAM_ALLOCATOR.used();
#[cfg(feature = "external_ram")]
let res = DRAM_ALLOCATOR.used() + IRAM_ALLOCATOR.used() + EXTERNAL_ALLOCATOR.used();
res
}
fn free(&self) -> usize {
#[cfg(not(feature = "external_ram"))]
let res = DRAM_ALLOCATOR.free() + IRAM_ALLOCATOR.free();
#[cfg(feature = "external_ram")]
let res = DRAM_ALLOCATOR.free() + IRAM_ALLOCATOR.free() + EXTERNAL_ALLOCATOR.free();
res
}
}
unsafe impl GlobalAlloc for GeneralAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
#[cfg(feature = "external_ram")]
if layout.size() > self.external_threshold {
let res = EXTERNAL_HEAP.alloc(layout);
if res != 0 as *mut u8 {
return res;
}
}
if self.use_iram
&& layout.size() >= core::mem::size_of::<usize>()
&& layout.align() >= core::mem::size_of::<usize>()
{
let res = IRAM_ALLOCATOR.alloc(layout);
if res != 0 as *mut u8 {
return res;
}
}
let res = DRAM_ALLOCATOR.alloc(layout);
if res != 0 as *mut u8 {
return res;
}
#[cfg(feature = "external_ram")]
return EXTERNAL_HEAP.alloc(layout);
#[cfg(not(feature = "external_ram"))]
return res;
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
match ptr as *const _ {
x if DRAM_HEAP.is_in_range(x) => DRAM_HEAP.dealloc(ptr, layout),
x if IRAM_HEAP.is_in_range(x) => IRAM_HEAP.dealloc(ptr, layout),
#[cfg(feature = "external_ram")]
x if EXTERNAL_HEAP.is_in_range(x) => EXTERNAL_HEAP.dealloc(ptr, layout),
_ => (),
}
}
}
struct LockedHeap<'a> {
heap: CriticalSectionSpinLockMutex<Option<Heap>>,
start_addr: &'a dyn Fn() -> *const u8,
end_addr: &'a dyn Fn() -> *const u8,
}
unsafe impl Sync for LockedHeap<'_> {}
unsafe impl GlobalAllocSize for LockedHeap<'_> {}
impl<'a> LockedHeap<'a> {
const fn new(
start_addr: &'a dyn Fn() -> *const u8,
end_addr: &'a dyn Fn() -> *const u8,
) -> Self {
Self {
heap: CriticalSectionSpinLockMutex::new(None),
start_addr,
end_addr,
}
}
fn with_locked_heap<R>(&self, f: impl FnOnce(&mut Heap) -> R) -> R {
(&self.heap).lock(|heap| match heap {
None => {
let start = (self.start_addr)() as usize;
let size = (self.end_addr)() as usize - (self.start_addr)() as usize;
let mut temp_heap = unsafe { Heap::new(start, size) };
let res = f(&mut temp_heap);
*heap = Some(temp_heap);
res
}
Some(heap) => f(heap),
})
}
fn is_in_range(&self, ptr: *const u8) -> bool {
self.with_locked_heap(|heap| (ptr as usize) >= heap.bottom() && (ptr as usize) < heap.top())
}
}
impl AllocatorSize for LockedHeap<'_> {
fn size(&self) -> usize {
self.with_locked_heap(|heap| heap.size())
}
fn used(&self) -> usize {
self.with_locked_heap(|heap| heap.used())
}
fn free(&self) -> usize {
self.with_locked_heap(|heap| heap.free())
}
}
unsafe impl GlobalAlloc for LockedHeap<'_> {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
self.with_locked_heap(|heap| {
heap.allocate_first_fit(layout)
.map_or(0 as *mut u8, |allocation| allocation.as_ptr())
})
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
self.with_locked_heap(|heap| heap.deallocate(NonNull::new_unchecked(ptr), layout));
}
}