#include "memory.h"
|
|
#include <stdlib.h> /* for malloc */
|
|
#include <stdio.h> /* for printf (for debugging) */
|
|
#include <string.h> /* for memcpy */
|
|
#include <inttypes.h>
|
|
|
|
const size_t g_totalMemorySize = 1024; /* bytes */
|
|
unsigned char *g_heapsBase = NULL;
|
|
unsigned char *g_heapsEnd = NULL;
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
#define BOOLEAN int
|
|
|
|
#define ALIGN(size) ((size + 7) & ~0x7)
|
|
|
|
typedef struct block * BlockPtr;
|
|
struct block
|
|
{
|
|
BOOLEAN isFree;
|
|
size_t size;
|
|
BlockPtr next;
|
|
BlockPtr previous;
|
|
char data[1]; /* marks the start of the actual data segment */
|
|
};
|
|
const HEADER_SIZE = offsetof(struct block, data);
|
|
|
|
static int _assert(BOOLEAN condition, int line); /* for line, pass in __LINE__ */
|
|
static BlockPtr _findBlock( /*BlockPtr last, */ size_t size);
|
|
static void _splitBlock(BlockPtr b, size_t size);
|
|
static void _initHeap();
|
|
static BOOLEAN _isValidAddress(void *ptr);
|
|
|
|
void memoryDump()
|
|
{
|
|
BlockPtr b = NULL;
|
|
unsigned char * p = NULL;
|
|
int col = 0;
|
|
unsigned char * snapshot = (unsigned char *)malloc(g_totalMemorySize);
|
|
|
|
printf("\n----------- heap metadata --------------\n");
|
|
printf("Our heap's total size: %zd\n", g_totalMemorySize);
|
|
printf("HEADER_SIZE: %d\n", HEADER_SIZE);
|
|
printf("So max total net available memory is: %zd\n\n", g_totalMemorySize - HEADER_SIZE);
|
|
memcpy(snapshot, g_heapsBase, g_totalMemorySize);
|
|
|
|
/*
|
|
* Make headers human-readable
|
|
*/
|
|
int blockCounter = 0;
|
|
for (b = (BlockPtr) g_heapsBase; b != NULL; b = b->next)
|
|
{
|
|
/*
|
|
* Pad the number with spaces so the whole message is the size of the header:
|
|
* 32 bytes on a 64-bit machine (should be 28 bytes but 32 because of alignment? or am I miscounting?)
|
|
* 16 bytes on a 32-bit machine.
|
|
*/
|
|
char msg[100];
|
|
sprintf(msg, "[%16zd bytes: ]", b->size);
|
|
|
|
size_t offset = (unsigned char*)b - (unsigned char*)g_heapsBase;
|
|
_assert(offset >= 0 && offset < g_totalMemorySize, __LINE__);
|
|
|
|
memcpy((unsigned char*)((size_t)snapshot + offset), msg, HEADER_SIZE);
|
|
|
|
size_t withHeader = b->size + HEADER_SIZE;
|
|
printf("Block %d, %3zd B (%3zd B) at %p (after header), is %s\n", /* replace the size_t prints %d by %zd on Ubuntu */
|
|
++blockCounter,
|
|
b->size,
|
|
withHeader,
|
|
b->data,
|
|
b->isFree ? "FREE" : "OCCUPIED");
|
|
}
|
|
printf("\n");
|
|
|
|
/*
|
|
* Print the heap snapshot
|
|
*/
|
|
printf("------------ heap content: beginning memory dump ---------------\n");
|
|
for (p = snapshot; p < (snapshot + g_totalMemorySize); p++)
|
|
{
|
|
if (*p && *p != '\n')
|
|
{
|
|
/* print the symbol at that location as a character */
|
|
putchar(*p);
|
|
}
|
|
else
|
|
{
|
|
putchar(178);
|
|
}
|
|
|
|
/* insert periodic line breaks */
|
|
col++;
|
|
if (col % 64 == 0)
|
|
{
|
|
putchar('\n');
|
|
}
|
|
}
|
|
|
|
printf("------------- ending memory dump :heap content -----------------\n\n");
|
|
}
|
|
|
|
static void _initHeap()
|
|
{
|
|
g_heapsBase = malloc(g_totalMemorySize);
|
|
g_heapsEnd = g_heapsBase + g_totalMemorySize;
|
|
|
|
/* initially, the heap is one giant block (which gets divvied up and fragmented over time as people make dcmalloc requests) */
|
|
BlockPtr newBlock;
|
|
newBlock = (BlockPtr)(g_heapsBase);
|
|
newBlock->size = g_totalMemorySize - HEADER_SIZE;
|
|
newBlock->isFree = TRUE;
|
|
newBlock->next = NULL;
|
|
newBlock->previous = NULL;
|
|
}
|
|
|
|
/**
|
|
* assuming we still have memory left
|
|
* TO DO: add an _assert()
|
|
*/
|
|
void * dcmalloc(size_t size)
|
|
{
|
|
BlockPtr b = NULL;
|
|
|
|
size = ALIGN(size);
|
|
|
|
if (!g_heapsBase)
|
|
{
|
|
_initHeap();
|
|
}
|
|
|
|
b = _findBlock(size);
|
|
if (!b)
|
|
{
|
|
printf("Sorry, not enough memory left to allocate %zd bytes.\n", size);
|
|
return NULL;
|
|
}
|
|
b->isFree = FALSE;
|
|
_splitBlock(b, size);
|
|
|
|
printf("allocating at %p, %zd bytes\n", b, size);
|
|
return b->data;
|
|
}
|
|
|
|
void _merge(BlockPtr b, BlockPtr nextBlock)
|
|
{
|
|
/* assume b1 and b2 are neighbors, both are free, and b1 is right before b2 */
|
|
_assert(b->isFree && nextBlock->isFree, __LINE__); /* both blocks think they are free */
|
|
_assert(b->next == nextBlock, __LINE__); /* b1 thinks that b2 is after her */
|
|
_assert(b == nextBlock->previous, __LINE__); /* b2 thinks that b1 is before him */
|
|
_assert(b < nextBlock, __LINE__); /* b1 is before b2 */
|
|
|
|
b->size += (HEADER_SIZE + nextBlock->size);
|
|
b->next = nextBlock->next; /* skip next block */
|
|
if (nextBlock->next)
|
|
{
|
|
nextBlock->next->previous = b;
|
|
}
|
|
|
|
memset(b->data, '-', b->size);
|
|
}
|
|
|
|
/*
|
|
* Important: if ptr is NULL, this will return TRUE.
|
|
*
|
|
* But if ptr is not NULL (i.e. has no excuse) and is
|
|
* not within the bounds of our heap, then this will return FALSE
|
|
*/
|
|
BOOLEAN _isValidAddress(void *ptr)
|
|
{
|
|
unsigned char *p = (unsigned char *)ptr;
|
|
|
|
BOOLEAN isValid = TRUE;
|
|
|
|
if (p)
|
|
{
|
|
isValid =
|
|
p <= g_heapsEnd &&
|
|
p >= g_heapsBase;
|
|
}
|
|
return isValid;
|
|
}
|
|
|
|
void dcfree(void *ptr)
|
|
{
|
|
BlockPtr b = (BlockPtr) (ptr - HEADER_SIZE);
|
|
|
|
printf("freeing %p\n", ptr);
|
|
_assert(b && _isValidAddress(b), __LINE__);
|
|
_assert(_isValidAddress(b->next), __LINE__);
|
|
_assert(_isValidAddress(b->previous), __LINE__);
|
|
|
|
b->isFree = TRUE;
|
|
dcmemset(b->data, '-', b->size);
|
|
|
|
/* fuse with the next block if it's also free */
|
|
if (b->next)
|
|
{
|
|
if (b->next->isFree)
|
|
{
|
|
_merge(b, b->next);
|
|
}
|
|
}
|
|
|
|
if (b->previous && b->previous->isFree)
|
|
{
|
|
_merge(b->previous, b);
|
|
}
|
|
}
|
|
|
|
|
|
void * dcmemset(void * ptr, int value, size_t num)
|
|
{
|
|
int c = value;
|
|
size_t i = 0;
|
|
unsigned char *p = (unsigned char *)ptr;
|
|
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
*p = (unsigned char)c;
|
|
p++;
|
|
}
|
|
|
|
return ptr;
|
|
}
|
|
|
|
static BlockPtr _findBlock( size_t size )
|
|
{
|
|
BlockPtr b = (BlockPtr) g_heapsBase;
|
|
_assert(b != NULL, __LINE__);
|
|
while (b && !(b->isFree && size <= b->size))
|
|
{
|
|
b = b->next;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
/*
|
|
* Arguments:
|
|
* size - size without the header; i.e. how much data, at a minimum, do you need this block to hold?
|
|
* b - block to split
|
|
*
|
|
* The simple picture:
|
|
* Before: |------------------ block b ------------------|
|
|
* After: |--- block b ---|--------- new block ---------|
|
|
*
|
|
* Or, to be more precise, with headers (marked with an 'H'):
|
|
* Before: |H----------------- block b ------------------|
|
|
* After: |H-- block b ---|H-------- new block ---------|
|
|
*/
|
|
static void _splitBlock(BlockPtr b, size_t size)
|
|
{
|
|
if (size >= b->size)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* create a second block */
|
|
BlockPtr newBlock;
|
|
unsigned char *p = b->data + size;
|
|
newBlock = (BlockPtr)p;
|
|
newBlock->size = b->size - size - HEADER_SIZE;
|
|
newBlock->next = b->next;
|
|
newBlock->previous = b;
|
|
newBlock->isFree = TRUE;
|
|
|
|
/* update the original block */
|
|
b->size = size;
|
|
b->next = newBlock;
|
|
|
|
/* debug */
|
|
_assert((unsigned char*) newBlock->next < g_heapsEnd, __LINE__);
|
|
if ((unsigned char *) newBlock->next >= g_heapsEnd)
|
|
{
|
|
printf("splitBlock: invalid address!");
|
|
getchar();
|
|
return;
|
|
}
|
|
|
|
_assert((unsigned char *) b->next < g_heapsEnd, __LINE__);
|
|
if ((unsigned char *) b->next >= g_heapsEnd)
|
|
{
|
|
printf("splitBlock: invalid address 2!");
|
|
getchar();
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static int _assert(BOOLEAN condition, int line)
|
|
{
|
|
if (!condition)
|
|
{
|
|
fprintf(stderr,
|
|
"_assertION FAILURE: at %s, line %d\n",
|
|
__FILE__,
|
|
line);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|