use core::ffi::c_void;
use core::ptr;
use crate::config::configTOTAL_HEAP_SIZE;
use crate::kernel::tasks::{vTaskSuspendAll, xTaskResumeAll};
use crate::port::portBYTE_ALIGNMENT;
const PORT_BYTE_ALIGNMENT_MASK: usize = portBYTE_ALIGNMENT - 1;
const HEAP_MINIMUM_BLOCK_SIZE: usize = HEAP_STRUCT_SIZE << 1;
const HEAP_BLOCK_ALLOCATED_BITMASK: usize = 1 << (usize::BITS - 1);
const HEAP_STRUCT_SIZE: usize = {
let base = core::mem::size_of::<BlockLink>();
(base + PORT_BYTE_ALIGNMENT_MASK) & !PORT_BYTE_ALIGNMENT_MASK
};
#[repr(C)]
struct BlockLink {
pxNextFreeBlock: *mut BlockLink,
xBlockSize: usize,
}
impl BlockLink {
const fn new() -> Self {
BlockLink {
pxNextFreeBlock: ptr::null_mut(),
xBlockSize: 0,
}
}
}
static mut UC_HEAP: [u8; configTOTAL_HEAP_SIZE] = [0u8; configTOTAL_HEAP_SIZE];
static mut X_START: BlockLink = BlockLink::new();
static mut PX_END: *mut BlockLink = ptr::null_mut();
static mut X_FREE_BYTES_REMAINING: usize = 0;
static mut X_MINIMUM_EVER_FREE_BYTES_REMAINING: usize = 0;
static mut X_NUMBER_OF_SUCCESSFUL_ALLOCATIONS: usize = 0;
static mut X_NUMBER_OF_SUCCESSFUL_FREES: usize = 0;
#[inline(always)]
fn heap_block_size_is_valid(size: usize) -> bool {
(size & HEAP_BLOCK_ALLOCATED_BITMASK) == 0
}
#[inline(always)]
fn heap_block_is_allocated(block: &BlockLink) -> bool {
(block.xBlockSize & HEAP_BLOCK_ALLOCATED_BITMASK) != 0
}
#[inline(always)]
unsafe fn heap_allocate_block(block: *mut BlockLink) {
(*block).xBlockSize |= HEAP_BLOCK_ALLOCATED_BITMASK;
}
#[inline(always)]
unsafe fn heap_free_block(block: *mut BlockLink) {
(*block).xBlockSize &= !HEAP_BLOCK_ALLOCATED_BITMASK;
}
#[inline(always)]
fn heap_get_block_size(block: &BlockLink) -> usize {
block.xBlockSize & !HEAP_BLOCK_ALLOCATED_BITMASK
}
unsafe fn prv_heap_init() {
let mut ux_start_address = UC_HEAP.as_ptr() as usize;
let mut x_total_heap_size = configTOTAL_HEAP_SIZE;
if (ux_start_address & PORT_BYTE_ALIGNMENT_MASK) != 0 {
ux_start_address += PORT_BYTE_ALIGNMENT_MASK;
ux_start_address &= !PORT_BYTE_ALIGNMENT_MASK;
x_total_heap_size -= ux_start_address - (UC_HEAP.as_ptr() as usize);
}
let mut ux_end_address = ux_start_address + x_total_heap_size;
ux_end_address -= HEAP_STRUCT_SIZE;
ux_end_address &= !PORT_BYTE_ALIGNMENT_MASK;
PX_END = ux_end_address as *mut BlockLink;
(*PX_END).xBlockSize = 0;
(*PX_END).pxNextFreeBlock = ptr::null_mut();
X_START.pxNextFreeBlock = ux_start_address as *mut BlockLink;
X_START.xBlockSize = 0;
let px_first_free_block = ux_start_address as *mut BlockLink;
(*px_first_free_block).xBlockSize = ux_end_address - ux_start_address;
(*px_first_free_block).pxNextFreeBlock = PX_END;
X_MINIMUM_EVER_FREE_BYTES_REMAINING = (*px_first_free_block).xBlockSize;
X_FREE_BYTES_REMAINING = (*px_first_free_block).xBlockSize;
}
unsafe fn prv_insert_block_into_free_list(px_block_to_insert: *mut BlockLink) {
let mut px_iterator = &mut X_START as *mut BlockLink;
while (*px_iterator).pxNextFreeBlock < px_block_to_insert {
px_iterator = (*px_iterator).pxNextFreeBlock;
}
let puc_iterator = px_iterator as *mut u8;
let iterator_block_size = heap_get_block_size(&*px_iterator);
if puc_iterator.add(iterator_block_size) == px_block_to_insert as *mut u8 {
(*px_iterator).xBlockSize += heap_get_block_size(&*px_block_to_insert);
let px_block_to_insert = px_iterator;
let puc_block = px_block_to_insert as *mut u8;
let block_size = heap_get_block_size(&*px_block_to_insert);
let px_next = (*px_iterator).pxNextFreeBlock;
if puc_block.add(block_size) == px_next as *mut u8 && px_next != PX_END {
(*px_block_to_insert).xBlockSize += heap_get_block_size(&*px_next);
(*px_block_to_insert).pxNextFreeBlock = (*px_next).pxNextFreeBlock;
}
} else {
let puc_block = px_block_to_insert as *mut u8;
let block_size = heap_get_block_size(&*px_block_to_insert);
let px_next = (*px_iterator).pxNextFreeBlock;
if puc_block.add(block_size) == px_next as *mut u8 && px_next != PX_END {
(*px_block_to_insert).xBlockSize += heap_get_block_size(&*px_next);
(*px_block_to_insert).pxNextFreeBlock = (*px_next).pxNextFreeBlock;
} else {
(*px_block_to_insert).pxNextFreeBlock = px_next;
}
(*px_iterator).pxNextFreeBlock = px_block_to_insert;
}
}
pub unsafe fn pvPortMalloc(x_wanted_size: usize) -> *mut c_void {
let mut pv_return: *mut c_void = ptr::null_mut();
let mut x_wanted_size = x_wanted_size;
if x_wanted_size > 0 {
if let Some(size) = x_wanted_size.checked_add(HEAP_STRUCT_SIZE) {
x_wanted_size = size;
if (x_wanted_size & PORT_BYTE_ALIGNMENT_MASK) != 0 {
let additional = portBYTE_ALIGNMENT - (x_wanted_size & PORT_BYTE_ALIGNMENT_MASK);
if let Some(size) = x_wanted_size.checked_add(additional) {
x_wanted_size = size;
} else {
x_wanted_size = 0; }
}
} else {
x_wanted_size = 0; }
}
vTaskSuspendAll();
{
if PX_END.is_null() {
prv_heap_init();
}
if heap_block_size_is_valid(x_wanted_size)
&& x_wanted_size > 0
&& x_wanted_size <= X_FREE_BYTES_REMAINING
{
let mut px_previous_block = &mut X_START as *mut BlockLink;
let mut px_block = X_START.pxNextFreeBlock;
while heap_get_block_size(&*px_block) < x_wanted_size
&& !(*px_block).pxNextFreeBlock.is_null()
{
px_previous_block = px_block;
px_block = (*px_block).pxNextFreeBlock;
}
if px_block != PX_END {
pv_return = ((*px_previous_block).pxNextFreeBlock as *mut u8).add(HEAP_STRUCT_SIZE)
as *mut c_void;
(*px_previous_block).pxNextFreeBlock = (*px_block).pxNextFreeBlock;
let block_size = heap_get_block_size(&*px_block);
if block_size - x_wanted_size > HEAP_MINIMUM_BLOCK_SIZE {
let px_new_block = (px_block as *mut u8).add(x_wanted_size) as *mut BlockLink;
(*px_new_block).xBlockSize = block_size - x_wanted_size;
(*px_block).xBlockSize = x_wanted_size;
(*px_new_block).pxNextFreeBlock = (*px_previous_block).pxNextFreeBlock;
(*px_previous_block).pxNextFreeBlock = px_new_block;
}
X_FREE_BYTES_REMAINING -= heap_get_block_size(&*px_block);
if X_FREE_BYTES_REMAINING < X_MINIMUM_EVER_FREE_BYTES_REMAINING {
X_MINIMUM_EVER_FREE_BYTES_REMAINING = X_FREE_BYTES_REMAINING;
}
heap_allocate_block(px_block);
(*px_block).pxNextFreeBlock = ptr::null_mut();
X_NUMBER_OF_SUCCESSFUL_ALLOCATIONS += 1;
}
}
}
xTaskResumeAll();
pv_return
}
pub unsafe fn vPortFree(pv: *mut c_void) {
if pv.is_null() {
return;
}
let puc = pv as *mut u8;
let px_link = puc.sub(HEAP_STRUCT_SIZE) as *mut BlockLink;
debug_assert!(
heap_block_is_allocated(&*px_link),
"vPortFree: block not allocated"
);
debug_assert!(
(*px_link).pxNextFreeBlock.is_null(),
"vPortFree: block still in free list"
);
if heap_block_is_allocated(&*px_link) && (*px_link).pxNextFreeBlock.is_null() {
heap_free_block(px_link);
vTaskSuspendAll();
{
X_FREE_BYTES_REMAINING += heap_get_block_size(&*px_link);
prv_insert_block_into_free_list(px_link);
X_NUMBER_OF_SUCCESSFUL_FREES += 1;
}
xTaskResumeAll();
}
}
pub fn xPortGetFreeHeapSize() -> usize {
unsafe { X_FREE_BYTES_REMAINING }
}
pub fn xPortGetMinimumEverFreeHeapSize() -> usize {
unsafe { X_MINIMUM_EVER_FREE_BYTES_REMAINING }
}
pub fn xPortResetHeapMinimumEverFreeHeapSize() {
unsafe {
X_MINIMUM_EVER_FREE_BYTES_REMAINING = X_FREE_BYTES_REMAINING;
}
}
pub unsafe fn pvPortCalloc(x_num: usize, x_size: usize) -> *mut c_void {
let total = match x_num.checked_mul(x_size) {
Some(t) => t,
None => return ptr::null_mut(),
};
let pv = pvPortMalloc(total);
if !pv.is_null() {
ptr::write_bytes(pv as *mut u8, 0, total);
}
pv
}
pub fn vPortGetHeapStats(px_heap_stats: *mut super::HeapStats_t) {
let mut x_blocks: usize = 0;
let mut x_max_size: usize = 0;
let mut x_min_size: usize = usize::MAX;
unsafe {
vTaskSuspendAll();
{
let mut px_block = X_START.pxNextFreeBlock;
if !px_block.is_null() {
while px_block != PX_END {
x_blocks += 1;
let block_size = heap_get_block_size(&*px_block);
if block_size > x_max_size {
x_max_size = block_size;
}
if block_size < x_min_size {
x_min_size = block_size;
}
px_block = (*px_block).pxNextFreeBlock;
}
}
}
xTaskResumeAll();
(*px_heap_stats).xSizeOfLargestFreeBlockInBytes = x_max_size;
(*px_heap_stats).xSizeOfSmallestFreeBlockInBytes =
if x_blocks > 0 { x_min_size } else { 0 };
(*px_heap_stats).xNumberOfFreeBlocks = x_blocks;
(*px_heap_stats).xAvailableHeapSpaceInBytes = X_FREE_BYTES_REMAINING;
(*px_heap_stats).xNumberOfSuccessfulAllocations = X_NUMBER_OF_SUCCESSFUL_ALLOCATIONS;
(*px_heap_stats).xNumberOfSuccessfulFrees = X_NUMBER_OF_SUCCESSFUL_FREES;
(*px_heap_stats).xMinimumEverFreeBytesRemaining = X_MINIMUM_EVER_FREE_BYTES_REMAINING;
}
}
pub fn vPortInitialiseBlocks() {
}
pub fn vPortHeapResetState() {
unsafe {
PX_END = ptr::null_mut();
X_FREE_BYTES_REMAINING = 0;
X_MINIMUM_EVER_FREE_BYTES_REMAINING = 0;
X_NUMBER_OF_SUCCESSFUL_ALLOCATIONS = 0;
X_NUMBER_OF_SUCCESSFUL_FREES = 0;
}
}
use core::alloc::{GlobalAlloc, Layout};
pub struct FreeRtosAllocator;
unsafe impl GlobalAlloc for FreeRtosAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
pvPortMalloc(layout.size()) as *mut u8
}
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
vPortFree(ptr as *mut c_void);
}
unsafe fn realloc(&self, ptr: *mut u8, _layout: Layout, new_size: usize) -> *mut u8 {
let new_ptr = pvPortMalloc(new_size) as *mut u8;
if !new_ptr.is_null() && !ptr.is_null() {
let copy_size = _layout.size().min(new_size);
ptr::copy_nonoverlapping(ptr, new_ptr, copy_size);
vPortFree(ptr as *mut c_void);
}
new_ptr
}
}