Refactor and make the allocator work

This commit is contained in:
Felix Kopp 2020-10-12 15:35:10 +02:00
parent cd833dbbdf
commit fc75cb2014
No known key found for this signature in database
GPG key ID: C478BA0A85F75728

View file

@ -89,10 +89,38 @@ static void memblk_set_size(struct memblk *block, size_t size)
block->size = size;
void *endptr = block;
endptr += MEMBLK_HDR_SIZE_LENGTH;
endptr += ((size >> 1) << 1); /* discard the allocated bit */
endptr += ((size >> 1u) << 1u); /* discard the allocated bit */
*(size_t *)(endptr) = size;
}
/**
* Split a single free memory block up into two individual blocks such that the block
* passed to this function will contain `size` bytes and the newly-created block has
* the rest minus overhead. The new block is inserted into the list of free blocks;
* however, the original block will *not* be re-sorted.
*
* @param blk The block to split up.
* @param size The new (at least by `MEMBLK_OFFSET + n` bytes smaller) size of the block.
* @return The newly created block.
*/
static struct memblk *memblk_split(struct memblk *blk, size_t size)
{
struct memblk *cursor;
struct memblk *newblk = (void *)blk + MEMBLK_OVERHEAD + ((size >> 1u) << 1u);
memblk_set_size(newblk, blk->size - MEMBLK_OVERHEAD - ((size >> 1u) << 1u));
memblk_set_size(blk, size);
list_for_each_entry_reverse(&blk->list, cursor, list) {
if (cursor->size >= newblk->size || &cursor->list == &memblk_free_list) {
list_insert(&cursor->list, &newblk->list);
break;
}
}
return newblk;
}
void malloc_init(void *heap, size_t size)
{
struct memblk *blk = heap;
@ -103,21 +131,22 @@ void malloc_init(void *heap, size_t size)
* dispatching/handling routines, we should do that here.
*/
if (list_is_empty(&memblk_free_list)) {
memblk_set_size(blk, size);
memblk_set_size(blk, size - MEMBLK_OVERHEAD);
list_insert(&memblk_free_list, &blk->list);
}
}
__attribute__((malloc))
void *malloc(size_t size)
{
struct memblk *blk, *newblk, *cursor;
struct memblk *blk;
size_t remaining_blksz;
if (size == 0)
return NULL; /* as per POSIX.1-2008 */
/* round up to the next multiple of `MIN_BLKSZ` */
size = ((size) / MIN_BLKSZ) * MIN_BLKSZ;
size = (size / MIN_BLKSZ) * MIN_BLKSZ;
size += MIN_BLKSZ;
list_for_each_entry(&memblk_free_list, blk, list) {
@ -126,45 +155,28 @@ void *malloc(size_t size)
break;
}
if (blk->size < size)
return NULL; /* ENOMEM */
return NULL; /* TODO: set errno to ENOMEM once we have it */
/*
* If we've made it to here, we have found a sufficiently big block,
* meaning we can't possibly fail anymore. Since that block is likely
* larger than the requested size, we are going to check if it is
* possible to create a new, smaller block right at the end of the
* allocated area.
* allocated area. If it isn't, we just hand out the entire block.
*/
remaining_blksz = blk->size - size;
if (remaining_blksz >= MIN_BLKSZ + MEMBLK_OVERHEAD) {
/* resize the allocated block to fit the requested length exactly */
memblk_set_size(blk, size | 0x1 /* allocated bit */);
if (remaining_blksz >= MIN_BLKSZ + MEMBLK_OVERHEAD)
memblk_split(blk, size | 0x1u /* allocated bit */);
else
memblk_set_size(blk, blk->size | 0x1u /* allocated bit */);
/* create a new smaller block ... */
newblk = (void *)blk + blk->size + MEMBLK_OVERHEAD;
memblk_set_size(newblk, remaining_blksz);
/* ... and insert it into the list */
list_for_each_entry_reverse(&blk->list, cursor, list) {
if (cursor->size < remaining_blksz || &cursor->list == &memblk_free_list) {
list_insert(&cursor->list, &newblk->list);
break;
}
}
list_delete(&blk->list);
} else {
/* hand out the entire block */
memblk_set_size(blk, blk->size | 0x1 /* allocated bit */);
list_delete(&blk->list);
}
memblk_set_size(blk, size | 0x1 /* allocated bit */);
list_delete(&blk->list);
/* Keep the size field intact */
return ((void *)blk) + MEMBLK_HDR_SIZE_LENGTH;
}
__attribute__((malloc))
void *calloc(size_t nmemb, size_t size)
{
void *ptr = malloc(nmemb * size);
@ -178,7 +190,7 @@ void *calloc(size_t nmemb, size_t size)
/** Merge two neighboring free blocks to one big block */
static void memblk_merge(struct memblk *lblk, struct memblk *hblk)
{
size_t *endsz = (void *)hblk + hblk->size + MEMBLK_OVERHEAD;
size_t *endsz = (void *)hblk + hblk->size + MEMBLK_HDR_SIZE_LENGTH;
lblk->size = lblk->size + hblk->size + MEMBLK_OVERHEAD;
*endsz = lblk->size;
}
@ -192,22 +204,22 @@ void free(void *ptr)
if (ptr == NULL)
return; /* as per POSIX.1-2008 */
if ((blk->size & 0x1) == 0)
if ((blk->size & 0x1u) == 0)
return; /* TODO: Raise exception on double-free */
memblk_set_size(blk, (blk->size >> 1) << 1 /* clear allocated bit */);
memblk_set_size(blk, (blk->size >> 1u) << 1u /* clear allocated bit */);
/* check if our higher/right neighbor is allocated and merge if it is not */
neighsz = (void *)blk + MEMBLK_OVERHEAD + blk->size;
if ((*neighsz & 0x1) == 0) {
tmp = (struct memblk *)neighsz;
if ((*neighsz & 0x1u) == 0) {
tmp = container_of(neighsz, struct memblk, size);
memblk_merge(blk, tmp);
list_delete(&tmp->list);
}
/* same thing for the lower/left block */
neighsz = (void *)blk - MEMBLK_HDR_SIZE_LENGTH;
if ((*neighsz & 0x1) == 0) {
if ((*neighsz & 0x1u) == 0) {
tmp = (void *)neighsz - *neighsz - MEMBLK_HDR_SIZE_LENGTH;
memblk_merge(tmp, blk);
list_delete(&tmp->list);
@ -215,11 +227,10 @@ void free(void *ptr)
}
list_for_each_entry(&memblk_free_list, tmp, list) {
if (tmp->size <= blk->size) {
list_insert_before(&tmp->list, &blk->list);
if (tmp->size >= blk->size)
break;
}
}
list_insert_before(&tmp->list, &blk->list);
}
/*