#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "umm_malloc_cfg.h"
#include "umm_malloc.h"
#define DBGLOG_LEVEL 0
#ifdef DBGLOG_ENABLE
#include "dbglog/dbglog.h"
#endif
extern void *UMM_MALLOC_CFG_HEAP_ADDR;
extern uint32_t UMM_MALLOC_CFG_HEAP_SIZE;
UMM_H_ATTPACKPRE typedef struct umm_ptr_t {
uint16_t next;
uint16_t prev;
} UMM_H_ATTPACKSUF umm_ptr;
UMM_H_ATTPACKPRE typedef struct umm_block_t {
union {
umm_ptr used;
} header;
union {
umm_ptr free;
uint8_t data[UMM_BLOCK_BODY_SIZE - sizeof(struct umm_ptr_t)];
} body;
} UMM_H_ATTPACKSUF umm_block;
#define UMM_FREELIST_MASK ((uint16_t)(0x8000))
#define UMM_BLOCKNO_MASK ((uint16_t)(0x7FFF))
struct umm_heap_config {
umm_block *pheap;
size_t heap_size;
uint16_t numblocks;
};
struct umm_heap_config umm_heap_current;
#define UMM_HEAP (umm_heap_current.pheap)
#define UMM_HEAPSIZE (umm_heap_current.heap_size)
#define UMM_NUMBLOCKS (umm_heap_current.numblocks)
#define UMM_BLOCKSIZE (sizeof(umm_block))
#define UMM_BLOCK_LAST (UMM_NUMBLOCKS - 1)
#define UMM_BLOCK(b) (UMM_HEAP[b])
#define UMM_DATA(b) (UMM_BLOCK(b).body.data)
#define UMM_NBLOCK(b) (UMM_BLOCK(b).header.used.next)
#define UMM_PBLOCK(b) (UMM_BLOCK(b).header.used.prev)
#define UMM_NFREE(b) (UMM_BLOCK(b).body.free.next)
#define UMM_PFREE(b) (UMM_BLOCK(b).body.free.prev)
#include "umm_integrity.c"
#include "umm_poison.c"
#include "umm_info.c"
static uint16_t umm_blocks(size_t size) {
if (size <= (sizeof(((umm_block *)0)->body))) {
return 1;
}
size -= (sizeof(((umm_block *)0)->body));
size_t blocks = (2 + ((size-1) / (UMM_BLOCKSIZE)));
if (blocks > (INT16_MAX)) {
blocks = INT16_MAX;
}
return (uint16_t)blocks;
}
static void umm_split_block(uint16_t c,
uint16_t blocks,
uint16_t new_freemask) {
UMM_NBLOCK(c + blocks) = (UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) | new_freemask;
UMM_PBLOCK(c + blocks) = c;
UMM_PBLOCK(UMM_NBLOCK(c) & UMM_BLOCKNO_MASK) = (c + blocks);
UMM_NBLOCK(c) = (c + blocks);
}
static void umm_disconnect_from_free_list(uint16_t c) {
UMM_NFREE(UMM_PFREE(c)) = UMM_NFREE(c);
UMM_PFREE(UMM_NFREE(c)) = UMM_PFREE(c);
UMM_NBLOCK(c) &= (~UMM_FREELIST_MASK);
}
static void umm_assimilate_up(uint16_t c) {
if (UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_FREELIST_MASK) {
UMM_FRAGMENTATION_METRIC_REMOVE(UMM_NBLOCK(c));
DBGLOG_DEBUG("Assimilate up to next block, which is FREE\n");
umm_disconnect_from_free_list(UMM_NBLOCK(c));
UMM_PBLOCK(UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK) = c;
UMM_NBLOCK(c) = UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK;
}
}
static uint16_t umm_assimilate_down(uint16_t c, uint16_t freemask) {
UMM_FRAGMENTATION_METRIC_REMOVE(UMM_PBLOCK(c));
UMM_NBLOCK(UMM_PBLOCK(c)) = UMM_NBLOCK(c) | freemask;
UMM_PBLOCK(UMM_NBLOCK(c)) = UMM_PBLOCK(c);
if (freemask) {
UMM_FRAGMENTATION_METRIC_ADD(UMM_PBLOCK(c));
}
return UMM_PBLOCK(c);
}
void umm_init_heap(void *ptr, size_t size)
{
UMM_HEAP = (umm_block *)ptr;
UMM_HEAPSIZE = size;
UMM_NUMBLOCKS = (UMM_HEAPSIZE / UMM_BLOCKSIZE);
memset(UMM_HEAP, 0x00, UMM_HEAPSIZE);
UMM_FRAGMENTATION_METRIC_INIT();
UMM_NBLOCK(0) = 1;
UMM_NFREE(0) = 1;
UMM_PFREE(0) = 1;
UMM_NBLOCK(1) = UMM_BLOCK_LAST | UMM_FREELIST_MASK;
UMM_PBLOCK(UMM_BLOCK_LAST) = 1;
}
void umm_init(void) {
umm_init_heap(UMM_MALLOC_CFG_HEAP_ADDR, UMM_MALLOC_CFG_HEAP_SIZE);
}
static void umm_free_core(void *ptr) {
uint16_t c;
c = (((uint8_t *)ptr) - (uint8_t *)(&(UMM_HEAP[0]))) / UMM_BLOCKSIZE;
DBGLOG_DEBUG("Freeing block %6i\n", c);
umm_assimilate_up(c);
if (UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK) {
DBGLOG_DEBUG("Assimilate down to previous block, which is FREE\n");
c = umm_assimilate_down(c, UMM_FREELIST_MASK);
} else {
UMM_FRAGMENTATION_METRIC_ADD(c);
DBGLOG_DEBUG("Just add to head of free list\n");
UMM_PFREE(UMM_NFREE(0)) = c;
UMM_NFREE(c) = UMM_NFREE(0);
UMM_PFREE(c) = 0;
UMM_NFREE(0) = c;
UMM_NBLOCK(c) |= UMM_FREELIST_MASK;
}
}
void umm_free(void *ptr) {
UMM_CRITICAL_DECL(id_free);
UMM_CHECK_INITIALIZED();
if ((void *)0 == ptr) {
DBGLOG_DEBUG("free a null pointer -> do nothing\n");
return;
}
UMM_CRITICAL_ENTRY(id_free);
umm_free_core(ptr);
UMM_CRITICAL_EXIT(id_free);
}
static void *umm_malloc_core(size_t size) {
uint16_t blocks;
uint16_t blockSize = 0;
uint16_t bestSize;
uint16_t bestBlock;
uint16_t cf;
blocks = umm_blocks(size);
cf = UMM_NFREE(0);
bestBlock = UMM_NFREE(0);
bestSize = 0x7FFF;
while (cf) {
blockSize = (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK) - cf;
DBGLOG_TRACE("Looking at block %6i size %6i\n", cf, blockSize);
#if defined UMM_BEST_FIT
if ((blockSize >= blocks) && (blockSize < bestSize)) {
bestBlock = cf;
bestSize = blockSize;
}
#elif defined UMM_FIRST_FIT
if ((blockSize >= blocks)) {
break;
}
#else
#error "No UMM_*_FIT is defined - check umm_malloc_cfg.h"
#endif
cf = UMM_NFREE(cf);
}
if (0x7FFF != bestSize) {
cf = bestBlock;
blockSize = bestSize;
}
if (UMM_NBLOCK(cf) & UMM_BLOCKNO_MASK && blockSize >= blocks) {
UMM_FRAGMENTATION_METRIC_REMOVE(cf);
if (blockSize == blocks) {
DBGLOG_DEBUG("Allocating %6i blocks starting at %6i - exact\n", blocks, cf);
umm_disconnect_from_free_list(cf);
} else {
DBGLOG_DEBUG("Allocating %6i blocks starting at %6i - existing\n", blocks, cf);
umm_split_block(cf, blocks, UMM_FREELIST_MASK );
UMM_FRAGMENTATION_METRIC_ADD(UMM_NBLOCK(cf));
UMM_NFREE(UMM_PFREE(cf)) = cf + blocks;
UMM_PFREE(cf + blocks) = UMM_PFREE(cf);
UMM_PFREE(UMM_NFREE(cf)) = cf + blocks;
UMM_NFREE(cf + blocks) = UMM_NFREE(cf);
}
} else {
DBGLOG_DEBUG("Can't allocate %5i blocks\n", blocks);
return (void *)NULL;
}
return (void *)&UMM_DATA(cf);
}
void *umm_malloc(size_t size) {
UMM_CRITICAL_DECL(id_malloc);
void *ptr = NULL;
UMM_CHECK_INITIALIZED();
if (0 == size) {
DBGLOG_DEBUG("malloc a block of 0 bytes -> do nothing\n");
return ptr;
}
UMM_CRITICAL_ENTRY(id_malloc);
ptr = umm_malloc_core(size);
UMM_CRITICAL_EXIT(id_malloc);
return ptr;
}
void *umm_realloc(void *ptr, size_t size) {
UMM_CRITICAL_DECL(id_realloc);
uint16_t blocks;
uint16_t blockSize;
uint16_t prevBlockSize = 0;
uint16_t nextBlockSize = 0;
uint16_t c;
size_t curSize;
UMM_CHECK_INITIALIZED();
if (((void *)NULL == ptr)) {
DBGLOG_DEBUG("realloc the NULL pointer - call malloc()\n");
return umm_malloc(size);
}
if (0 == size) {
DBGLOG_DEBUG("realloc to 0 size, just free the block\n");
umm_free(ptr);
return (void *)NULL;
}
blocks = umm_blocks(size);
c = (((uint8_t *)ptr) - (uint8_t *)(&(UMM_HEAP[0]))) / UMM_BLOCKSIZE;
blockSize = (UMM_NBLOCK(c) - c);
curSize = (blockSize * UMM_BLOCKSIZE) - (sizeof(((umm_block *)0)->header));
UMM_CRITICAL_ENTRY(id_realloc);
if ((UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_FREELIST_MASK)) {
nextBlockSize = (UMM_NBLOCK(UMM_NBLOCK(c)) & UMM_BLOCKNO_MASK) - UMM_NBLOCK(c);
}
if ((UMM_NBLOCK(UMM_PBLOCK(c)) & UMM_FREELIST_MASK)) {
prevBlockSize = (c - UMM_PBLOCK(c));
}
DBGLOG_DEBUG("realloc blocks %i blockSize %i nextBlockSize %i prevBlockSize %i\n", blocks, blockSize, nextBlockSize, prevBlockSize);
if (blockSize >= blocks) {
DBGLOG_DEBUG("realloc the same or smaller size block - %i, do nothing\n", blocks);
} else if ((blockSize + nextBlockSize) == blocks) {
DBGLOG_DEBUG("exact realloc using next block - %i\n", blocks);
umm_assimilate_up(c);
blockSize += nextBlockSize;
} else if ((0 == prevBlockSize) && (blockSize + nextBlockSize) >= blocks) {
DBGLOG_DEBUG("realloc using next block - %i\n", blocks);
umm_assimilate_up(c);
blockSize += nextBlockSize;
} else if ((prevBlockSize + blockSize) >= blocks) {
DBGLOG_DEBUG("realloc using prev block - %i\n", blocks);
umm_disconnect_from_free_list(UMM_PBLOCK(c));
c = umm_assimilate_down(c, 0);
memmove((void *)&UMM_DATA(c), ptr, curSize);
ptr = (void *)&UMM_DATA(c);
blockSize += prevBlockSize;
} else if ((prevBlockSize + blockSize + nextBlockSize) >= blocks) {
DBGLOG_DEBUG("realloc using prev and next block - %i\n", blocks);
umm_assimilate_up(c);
umm_disconnect_from_free_list(UMM_PBLOCK(c));
c = umm_assimilate_down(c, 0);
memmove((void *)&UMM_DATA(c), ptr, curSize);
ptr = (void *)&UMM_DATA(c);
blockSize += (prevBlockSize + nextBlockSize);
} else {
DBGLOG_DEBUG("realloc a completely new block %i\n", blocks);
void *oldptr = ptr;
if ((ptr = umm_malloc_core(size))) {
DBGLOG_DEBUG("realloc %i to a bigger block %i, copy, and free the old\n", blockSize, blocks);
memcpy(ptr, oldptr, curSize);
umm_free_core(oldptr);
} else {
DBGLOG_DEBUG("realloc %i to a bigger block %i failed - return NULL and leave the old block!\n", blockSize, blocks);
}
blockSize = blocks;
}
if (blockSize > blocks) {
DBGLOG_DEBUG("split and free %i blocks from %i\n", blocks, blockSize);
umm_split_block(c, blocks, 0);
umm_free_core((void *)&UMM_DATA(c + blocks));
}
UMM_CRITICAL_EXIT(id_realloc);
return ptr;
}
void *umm_calloc(size_t num, size_t item_size) {
void *ret;
ret = umm_malloc((size_t)(item_size * num));
if (ret) {
memset(ret, 0x00, (size_t)(item_size * num));
}
return ret;
}