/*
|
|
* Yet Another Malloc
|
|
* yamalloc.c
|
|
*/
|
|
|
|
/*----------*/
|
|
/* Includes */
|
|
/*----------*/
|
|
|
|
#include "yamalloc.h"
|
|
#include "ya_debug.h"
|
|
#include "ya_block.h"
|
|
#include "ya_freelist.h"
|
|
|
|
/*----------------------*/
|
|
/* Function definitions */
|
|
/*----------------------*/
|
|
|
|
#ifdef YA_DEBUG
|
|
/* Print all blocks in the heap */
|
|
void ya_print_blocks() {
|
|
ya_debug("All blocks:\n");
|
|
block_print_range(heap_start, heap_end);
|
|
ya_debug("Free blocks:\n");
|
|
fl_debug_print();
|
|
}
|
|
#endif
|
|
|
|
/* Allocates enough memory to store at least size bytes.
|
|
* Returns a dword-aligned pointer to the memory or NULL in case of failure. */
|
|
void *malloc(size_t n_bytes) {
|
|
if (n_bytes == 0) {
|
|
return NULL;
|
|
}
|
|
if (heap_start == NULL || heap_end == NULL) {
|
|
if (!heap_init()) {
|
|
return NULL;
|
|
}
|
|
}
|
|
intptr_t size = block_fit(n_bytes);
|
|
intptr_t *block = block_find(size);
|
|
if (!block) {
|
|
block = heap_extend(n_bytes);
|
|
}
|
|
if (block_split(block, size)) {
|
|
// block was indeed split, so splt it in the free list as well
|
|
fl_split(block, size);
|
|
}
|
|
block_alloc(block);
|
|
fl_alloc(block);
|
|
return block;
|
|
}
|
|
|
|
/* Frees the memory block pointed to by ptr, which must have been allocated
|
|
* through a call to malloc, calloc or realloc before. Otherwise, undefined
|
|
* behavior occurs. */
|
|
void free(void *ptr) {
|
|
intptr_t *block = ptr;
|
|
if (block < heap_start || block > heap_end || !block_is_alloc(block)) {
|
|
return; // TODO: provoke segfault
|
|
}
|
|
block_free(block);
|
|
fl_free(block);
|
|
fl_join(block);
|
|
block_join(block);
|
|
}
|
|
|
|
/* Allocates enough memory to store an array of nmemb elements,
|
|
* each size bytes large, and clears the memory.
|
|
* Returns the pointer to the allocated memory or NULL in case of failure. */
|
|
void *calloc(size_t nmemb, size_t n_bytes) {
|
|
intptr_t *block = malloc(n_bytes * nmemb);
|
|
intptr_t size = block_size(block);
|
|
block_clear(block);
|
|
return block;
|
|
}
|
|
|
|
/* Resizes the previously allocated memory pointed to by ptr to size.
|
|
* If ptr is NULL, it is equivalent to malloc(size).
|
|
* If called with size 0, it is equivalent to free(ptr).
|
|
* If ptr does not point to memory previously allocated by malloc, calloc or
|
|
* realloc, undefined behavior occurs.
|
|
* */
|
|
void *realloc(void *ptr, size_t n_bytes) {
|
|
if (!ptr) {
|
|
return malloc(n_bytes);
|
|
}
|
|
if (n_bytes == 0) {
|
|
free(ptr);
|
|
return NULL;
|
|
}
|
|
intptr_t *block = ptr;
|
|
if (block < heap_start) {
|
|
return NULL; // TODO: provoke segfault
|
|
}
|
|
intptr_t new_size = block_fit(n_bytes);
|
|
intptr_t size = block_size(block); // segfault if ptr after heap end
|
|
if (new_size == size) {
|
|
return ptr; // don't change anything
|
|
}
|
|
if (new_size < size) {
|
|
intptr_t *next = block_split(block, new_size);
|
|
if (next) {
|
|
fl_free(next); // add the next from the free list
|
|
fl_join_next(next);
|
|
block_join_next(next); // coalesce the leftovers
|
|
}
|
|
block_alloc(block);
|
|
// no need to remove it from the free list, it wasn't in it
|
|
return block;
|
|
}
|
|
intptr_t *next = block + size;
|
|
if (next == heap_end ||
|
|
(!block_is_alloc(next) && next + block_size(next) == heap_end)) {
|
|
// grow the heap and extend block
|
|
next = heap_extend(n_bytes);
|
|
fl_free(block);
|
|
fl_join_next(block);
|
|
block_join_next(block);
|
|
if (block_split(block, new_size)) {
|
|
fl_split(block, new_size);
|
|
}
|
|
block_alloc(block);
|
|
fl_alloc(block);
|
|
return block;
|
|
}
|
|
// try to use next free block
|
|
if (next < heap_end) {
|
|
intptr_t next_size = block_size(next);
|
|
if (!block_is_alloc(next) && new_size <= size + next_size) {
|
|
// try to split the next block at the right size
|
|
if (block_split(next, new_size - size)) {
|
|
// split successful, must split in the free list too
|
|
fl_split(block, new_size);
|
|
}
|
|
fl_alloc(next); // remove the next block from the free list
|
|
fl_join_next(block);
|
|
block_join_next(block); // coalesce
|
|
block_alloc(block); // mark block as allocated
|
|
return block;
|
|
}
|
|
}
|
|
// resizing failed, so allocate a whole new block and copy
|
|
intptr_t *new_block = malloc(n_bytes);
|
|
for (int i = 0; i < size; i++) {
|
|
new_block[i] = block[i];
|
|
}
|
|
free(block);
|
|
return new_block;
|
|
}
|