|
|
@ -0,0 +1,303 @@ |
|
|
|
#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; |
|
|
|
} |