picoc/heap.c

237 lines
8.5 KiB
C
Raw Normal View History

/* stack grows up from the bottom and heap grows down from the top of heap space */
#include "picoc.h"
#define FREELIST_BUCKETS 8 /* freelists for 4, 8, 12 ... 32 byte allocs */
#define SPLIT_MEM_THRESHOLD 16 /* don't split memory which is close in size */
#ifdef SURVEYOR_HOST
#define HEAP_SIZE C_HEAPSIZE
static unsigned char *HeapMemory = (unsigned char *)C_HEAPSTART; /* all memory - stack and heap */
static void *HeapBottom = (void *)C_HEAPSTART + HEAP_SIZE; /* the bottom of the (downward-growing) heap */
static void *StackFrame = (void *)C_HEAPSTART; /* the current stack frame */
static void *HeapStackTop = (void *)C_HEAPSTART; /* the top of the stack */
#else
static unsigned char HeapMemory[HEAP_SIZE]; /* all memory - stack and heap */
static void *HeapBottom = &HeapMemory[HEAP_SIZE]; /* the bottom of the (downward-growing) heap */
static void *StackFrame = &HeapMemory[0]; /* the current stack frame */
void *HeapStackTop = &HeapMemory[0]; /* the top of the stack */
#endif
static struct AllocNode *FreeListBucket[FREELIST_BUCKETS]; /* we keep a pool of freelist buckets to reduce fragmentation */
static struct AllocNode *FreeListBig; /* free memory which doesn't fit in a bucket */
#ifdef DEBUG_HEAP
void ShowBigList()
{
struct AllocNode *LPos;
printf("Heap: bottom=0x%lx 0x%lx-0x%lx, big freelist=", (long)HeapBottom, (long)&HeapMemory[0], (long)&HeapMemory[HEAP_SIZE]);
for (LPos = FreeListBig; LPos != NULL; LPos = LPos->NextFree)
printf("0x%lx:%d ", (long)LPos, LPos->Size);
printf("\n");
}
#endif
/* initialise the stack and heap storage */
void HeapInit()
{
int Count;
StackFrame = &HeapMemory[0];
HeapStackTop = &HeapMemory[0];
*(void **)StackFrame = NULL;
HeapBottom = &HeapMemory[HEAP_SIZE];
FreeListBig = NULL;
for (Count = 0; Count < FREELIST_BUCKETS; Count++)
FreeListBucket[Count] = NULL;
}
/* allocate some space on the stack, in the current stack frame
* clears memory. can return NULL if out of stack space */
void *HeapAllocStack(int Size)
{
void *NewMem = HeapStackTop;
void *NewTop = HeapStackTop + MEM_ALIGN(Size);
if (NewTop > HeapBottom)
return NULL;
HeapStackTop = NewTop;
memset(NewMem, '\0', Size);
return NewMem;
}
/* free some space at the top of the stack */
int HeapPopStack(void *Addr, int Size)
{
int ToLose = MEM_ALIGN(Size);
if (ToLose > (HeapStackTop - (void *)&HeapMemory[0]))
return FALSE;
HeapStackTop -= ToLose;
if (HeapStackTop != Addr)
printf("fail\n");
assert(HeapStackTop == Addr);
return TRUE;
}
/* push a new stack frame on to the stack */
void HeapPushStackFrame()
{
#ifdef DEBUG_HEAP
printf("Adding stack frame at 0x%lx\n", (unsigned long)HeapStackTop);
#endif
*(void **)HeapStackTop = StackFrame;
StackFrame = HeapStackTop;
HeapStackTop += sizeof(void *);
}
/* pop the current stack frame, freeing all memory in the frame. can return NULL */
int HeapPopStackFrame()
{
if (*(void **)StackFrame != NULL)
{
HeapStackTop = StackFrame;
StackFrame = *(void **)StackFrame;
#ifdef DEBUG_HEAP
printf("Popping stack frame back to 0x%lx\n", (unsigned long)HeapStackTop);
#endif
return TRUE;
}
else
return FALSE;
}
/* allocate some dynamically allocated memory. memory is cleared. can return NULL if out of memory */
void *HeapAlloc(int Size)
{
#ifdef USE_MALLOC_HEAP
return calloc(Size, 1);
#else
struct AllocNode *NewMem = NULL;
struct AllocNode **FreeNode;
int AllocSize = MEM_ALIGN(Size) + sizeof(NewMem->Size);
int Bucket;
if (Size == 0)
return NULL;
assert(Size > 0);
/* make sure we have enough space for an AllocNode */
if (AllocSize < sizeof(struct AllocNode))
AllocSize = sizeof(struct AllocNode);
Bucket = AllocSize >> 2;
if (Bucket < FREELIST_BUCKETS && FreeListBucket[Bucket] != NULL)
{ /* try to allocate from a freelist bucket first */
#ifdef DEBUG_HEAP
printf("allocating %d(%d) from bucket", Size, AllocSize);
#endif
NewMem = FreeListBucket[Bucket];
assert((unsigned long)NewMem >= (unsigned long)&HeapMemory[0] && (unsigned char *)NewMem - &HeapMemory[0] < HEAP_SIZE);
FreeListBucket[Bucket] = *(struct AllocNode **)NewMem;
assert(FreeListBucket[Bucket] == NULL || ((unsigned long)FreeListBucket[Bucket] >= (unsigned long)&HeapMemory[0] && (unsigned char *)FreeListBucket[Bucket] - &HeapMemory[0] < HEAP_SIZE));
NewMem->Size = AllocSize;
}
else if (FreeListBig != NULL)
{ /* grab the first item from the "big" freelist we can fit in */
for (FreeNode = &FreeListBig; *FreeNode != NULL && (*FreeNode)->Size < AllocSize; FreeNode = &(*FreeNode)->NextFree)
{}
if (*FreeNode != NULL)
{
assert((unsigned long)*FreeNode >= (unsigned long)&HeapMemory[0] && (unsigned char *)*FreeNode - &HeapMemory[0] < HEAP_SIZE);
assert((*FreeNode)->Size < HEAP_SIZE && (*FreeNode)->Size > 0);
if ((*FreeNode)->Size < Size + SPLIT_MEM_THRESHOLD)
{ /* close in size - reduce fragmentation by not splitting */
#ifdef DEBUG_HEAP
printf("allocating %d(%d) from freelist, no split (%d)", Size, AllocSize, (*FreeNode)->Size);
#endif
NewMem = *FreeNode;
assert((unsigned long)NewMem >= (unsigned long)&HeapMemory[0] && (unsigned char *)NewMem - &HeapMemory[0] < HEAP_SIZE);
*FreeNode = NewMem->NextFree;
}
else
{ /* split this big memory chunk */
#ifdef DEBUG_HEAP
printf("allocating %d(%d) from freelist, split chunk (%d)", Size, AllocSize, (*FreeNode)->Size);
#endif
NewMem = (void *)*FreeNode + (*FreeNode)->Size - AllocSize;
assert((unsigned long)NewMem >= (unsigned long)&HeapMemory[0] && (unsigned char *)NewMem - &HeapMemory[0] < HEAP_SIZE);
(*FreeNode)->Size -= AllocSize;
NewMem->Size = AllocSize;
}
}
}
if (NewMem == NULL)
{ /* couldn't allocate from a freelist - try to increase the size of the heap area */
#ifdef DEBUG_HEAP
printf("allocating %d(%d) at bottom of heap (0x%lx-0x%lx)", Size, AllocSize, (long)(HeapBottom - AllocSize), (long)HeapBottom);
#endif
if (HeapBottom - AllocSize < HeapStackTop)
return NULL;
HeapBottom -= AllocSize;
NewMem = HeapBottom;
NewMem->Size = AllocSize;
}
memset((void *)&NewMem->NextFree, '\0', AllocSize-sizeof(NewMem->Size));
#ifdef DEBUG_HEAP
printf(" = %lx\n", (unsigned long)&NewMem->NextFree);
#endif
return (void *)&NewMem->NextFree;
#endif
}
/* free some dynamically allocated memory */
void HeapFree(void *Mem)
{
#ifdef USE_MALLOC_HEAP
return free(Mem);
#else
struct AllocNode *MemNode = (struct AllocNode *)(Mem-sizeof(int));
int Bucket = MemNode->Size >> 2;
assert((unsigned long)Mem >= (unsigned long)&HeapMemory[0] && (unsigned char *)Mem - &HeapMemory[0] < HEAP_SIZE);
assert(MemNode->Size < HEAP_SIZE && MemNode->Size > 0);
if (Mem == NULL)
return;
if ((void *)MemNode == HeapBottom)
{ /* pop it off the bottom of the heap, reducing the heap size */
#ifdef DEBUG_HEAP
printf("freeing %d from bottom of heap\n", MemNode->Size);
#endif
HeapBottom += MemNode->Size;
#ifdef DEBUG_HEAP
ShowBigList();
#endif
}
else if (Bucket < FREELIST_BUCKETS)
{ /* we can fit it in a bucket */
#ifdef DEBUG_HEAP
printf("freeing %d to bucket\n", MemNode->Size);
#endif
assert(FreeListBucket[Bucket] == NULL || ((unsigned long)FreeListBucket[Bucket] >= (unsigned long)&HeapMemory[0] && (unsigned char *)FreeListBucket[Bucket] - &HeapMemory[0] < HEAP_SIZE));
*(struct AllocNode **)MemNode = FreeListBucket[Bucket];
FreeListBucket[Bucket] = (struct AllocNode *)MemNode;
}
else
{ /* put it in the big memory freelist */
#ifdef DEBUG_HEAP
printf("freeing %lx:%d to freelist\n", (unsigned long)Mem, MemNode->Size);
#endif
assert(FreeListBig == NULL || ((unsigned long)FreeListBig >= (unsigned long)&HeapMemory[0] && (unsigned char *)FreeListBig - &HeapMemory[0] < HEAP_SIZE));
MemNode->NextFree = FreeListBig;
FreeListBig = MemNode;
#ifdef DEBUG_HEAP
ShowBigList();
#endif
}
#endif
}