99 lines
2.4 KiB
C
99 lines
2.4 KiB
C
#include "cgdoom-alloc.h"
|
|
#include "z_zone.h"
|
|
#include <stdint.h>
|
|
|
|
/* We use -fstrict-volatile-bitfields to enforce the 32-bit access size. */
|
|
struct node;
|
|
typedef volatile struct node node_t;
|
|
|
|
struct node {
|
|
/* Neighbors, or NULL at the head and tail of the list */
|
|
node_t *prev, *next;
|
|
/* Size of the block, in bytes */
|
|
uint32_t size :24;
|
|
/* Whether the block is free */
|
|
uint32_t free :8;
|
|
};
|
|
|
|
/* First node of the list. */
|
|
static node_t *arena = NULL;
|
|
/* Bounds of the arena, used to find whether data has been allocated here. */
|
|
static void *arena_start, *arena_end;
|
|
|
|
/* Split a free node into two (if there's enough space for a second one). */
|
|
static void split(node_t *node, int size)
|
|
{
|
|
int remainder = node->size - size;
|
|
if(remainder < 32) return;
|
|
|
|
node_t *right = (void *)node + sizeof(node_t) + size;
|
|
right->prev = node;
|
|
right->next = node->next;
|
|
right->size = remainder - sizeof(node_t);
|
|
right->free = 1;
|
|
|
|
node->size = size;
|
|
node->next = right;
|
|
if(right->next) right->next->prev = right;
|
|
}
|
|
|
|
/* Merge this free node with the next one (also needs to be free). */
|
|
static void merge_with_next(node_t *node)
|
|
{
|
|
if(!node->next) return;
|
|
node->size += sizeof(node_t) + node->next->size;
|
|
node->next = node->next->next;
|
|
if(node->next) node->next->prev = node;
|
|
}
|
|
|
|
void CGD_PRAM_Init(void *start, void *end)
|
|
{
|
|
arena = NULL;
|
|
if(end - start < 256) return;
|
|
|
|
arena = start;
|
|
arena->prev = NULL;
|
|
arena->next = NULL;
|
|
arena->size = (end - start) - sizeof(node_t);
|
|
arena->free = 1;
|
|
|
|
arena_start = start;
|
|
arena_end = end;
|
|
}
|
|
|
|
void *CGD_PRAM_Malloc(size_t size)
|
|
{
|
|
node_t *candidate;
|
|
size = (size + 3) & -4;
|
|
|
|
/* Find a free block in the list */
|
|
for(candidate = arena; candidate; candidate = candidate->next) {
|
|
if(candidate->free && candidate->size >= size) break;
|
|
}
|
|
if(!candidate) return Z_Malloc(size, PU_STATIC, 0);
|
|
|
|
/* Prepare and return that block */
|
|
split(candidate, size);
|
|
candidate->free = 0;
|
|
return (void *)candidate + sizeof(node_t);
|
|
}
|
|
|
|
void CGD_PRAM_Free(void *ptr)
|
|
{
|
|
if(!ptr) return;
|
|
if(ptr < arena_start || ptr >= arena_end) return Z_Free(ptr);
|
|
|
|
node_t *node = (void *)ptr - sizeof(node_t);
|
|
node->free = 1;
|
|
if(node->next && node->next->free) merge_with_next(node);
|
|
if(node->prev && node->prev->free) merge_with_next(node->prev);
|
|
}
|
|
|
|
void *CGD_PRAM_Zalloc(size_t size)
|
|
{
|
|
uint32_t volatile *ptr = CGD_PRAM_Malloc(size);
|
|
if(!ptr) return NULL;
|
|
for(int i = 0; i < size / 4; i++) ptr[i] = 0;
|
|
return (void *)ptr;
|
|
}
|