From fc75cb2014db054e9df850ffc34a6c1f61d10e32 Mon Sep 17 00:00:00 2001 From: Felix Kopp Date: Mon, 12 Oct 2020 15:35:10 +0200 Subject: [PATCH] Refactor and make the allocator work --- lib/malloc.c | 85 +++++++++++++++++++++++++++++----------------------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/lib/malloc.c b/lib/malloc.c index 6fa240e..b2dc3be 100644 --- a/lib/malloc.c +++ b/lib/malloc.c @@ -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); } /*