From 6c942fb0c6af268ce8b6b07b00dfe46b0b370a14 Mon Sep 17 00:00:00 2001 From: "csander@caltech.edu" <csander@caltech.edu> Date: Tue, 27 Oct 2020 19:35:53 +0000 Subject: [PATCH] Clean up starter code --- Makefile | 6 +-- include/memlib.h | 1 + include/mm.h | 14 ++--- src/mm-explicit.c | 134 +++++++++++++++++++++++++++++----------------- src/mm-implicit.c | 134 +++++++++++++++++++++++++++++----------------- 5 files changed, 183 insertions(+), 106 deletions(-) diff --git a/Makefile b/Makefile index 07e0c4a..c018d53 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # Makefile for the malloc lab driver # CC = clang -CFLAGS = -Werror -Wall -Wextra -O3 -g -DDRIVER +CFLAGS = -Werror -Wall -Wextra -O3 -g all: bin/mdriver-implicit bin/mdriver-explicit @@ -15,7 +15,7 @@ bin/mdriver-explicit: out/mdriver-explicit.o out/mm-explicit.o out/memlib.o out/ out/mdriver-implicit.o: driver/mdriver.c $(CC) $(CFLAGS) -c -DSTAGE0 $^ -o $@ -out/mdriver-explicit.o: driver/mdriver.c +out/mdriver-explicit.o: driver/mdriver.c $(CC) $(CFLAGS) -c -DSTAGE1 $^ -o $@ out/%.o: src/%.c @@ -25,4 +25,4 @@ out/%.o: driver/%.c $(CC) $(CFLAGS) -c $^ -o $@ clean: - rm -f *~ out/*.o bin/* + rm -f out/* bin/* diff --git a/include/memlib.h b/include/memlib.h index 5321091..f87c2e8 100644 --- a/include/memlib.h +++ b/include/memlib.h @@ -1,4 +1,5 @@ #include <stddef.h> +#include <sys/types.h> void mem_init(void); void mem_deinit(void); diff --git a/include/mm.h b/include/mm.h index 5e06116..e406261 100644 --- a/include/mm.h +++ b/include/mm.h @@ -1,14 +1,14 @@ #ifndef MM_H #define MM_H -#include <stdio.h> #include <stdbool.h> +#include <stddef.h> -extern bool mm_init(void); -extern void *mm_malloc(size_t size); -extern void mm_free(void *ptr); -extern void *mm_realloc(void *ptr, size_t size); -extern void *mm_calloc(size_t nmemb, size_t size); -extern void mm_checkheap(); +bool mm_init(void); +void *mm_malloc(size_t size); +void mm_free(void *ptr); +void *mm_realloc(void *ptr, size_t size); +void *mm_calloc(size_t nmemb, size_t size); +void mm_checkheap(void); #endif /* MM_H */ diff --git a/src/mm-explicit.c b/src/mm-explicit.c index d586f0b..82010aa 100644 --- a/src/mm-explicit.c +++ b/src/mm-explicit.c @@ -4,120 +4,158 @@ * TODO (bug): Uh..this is an implicit list??? */ -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdbool.h> #include <stdint.h> -#include <stddef.h> -#include <unistd.h> #include "mm.h" #include "memlib.h" -typedef uint64_t word_t; -const size_t ALIGNMENT = 2 * sizeof(word_t); +/** The required alignment of heap payloads */ +const size_t ALIGNMENT = 2 * sizeof(size_t); +/** The layout of each block allocated on the heap */ typedef struct { + /** The size of the block and whether it is allocated (stored in the low bit) */ size_t header; - /* + /** * We don't know what the size of the payload will be, so we will * declare it as a zero-length array. This allow us to obtain a * pointer to the start of the payload. */ - word_t payload[]; + uint8_t payload[]; } block_t; +/** The first and last blocks on the heap */ static block_t *mm_heap_first = NULL; static block_t *mm_heap_last = NULL; +/** Rounds up `size` to the nearest multiple of `n` */ static size_t round_up(size_t size, size_t n) { - return n * ((size + (n-1)) / n); -} - -static size_t get_size(block_t *block) { - return block->header & ~0x7; + return (size + (n - 1)) / n * n; } +/** Set's a block's header with the given size and allocation state */ static void set_header(block_t *block, size_t size, bool is_allocated) { block->header = size | is_allocated; } +/** Extracts a block's size from its header */ +static size_t get_size(block_t *block) { + return block->header & ~1; +} + +/** Extracts a block's allocation state from its header */ static bool is_allocated(block_t *block) { - return block->header & 0x1; + return block->header & 1; } -block_t *find_fit(size_t size) { - for (block_t *curr = mm_heap_first; mm_heap_last && curr <= mm_heap_last; curr = ((void *)curr + get_size(curr))) { - size_t curr_size = get_size(curr); - if (!is_allocated(curr) && curr_size >= size) { +/** + * Finds the first free block in the heap with at least the given size. + * If no block is large enough, returns NULL. + */ +static block_t *find_fit(size_t size) { + // Traverse the blocks in the heap using the implicit list + for ( + block_t *curr = mm_heap_first; + mm_heap_last != NULL && curr <= mm_heap_last; + curr = (void *) curr + get_size(curr) + ) { + // If the block is free and large enough for the allocation, return it + if (!is_allocated(curr) && get_size(curr) >= size) { return curr; } } return NULL; } +/** Gets the header corresponding to a given payload pointer */ +static block_t *block_from_payload(void *ptr) { + return ptr - offsetof(block_t, payload); +} + +/** + * mm_init - Initializes the allocator state + */ bool mm_init(void) { - mm_heap_first = mem_sbrk(8); - set_header(mm_heap_first, 8, true); + // We want the first payload to start at ALIGNMENT bytes from the start of the heap + void *padding = mem_sbrk(ALIGNMENT - sizeof(block_t)); + if (padding == (void *) -1) { + return false; + } + + // Initialize the heap with no blocks + mm_heap_first = NULL; mm_heap_last = NULL; return true; } +/** + * mm_malloc - Allocates a block with the given size + */ void *mm_malloc(size_t size) { - size_t asize = round_up(size + sizeof(word_t), ALIGNMENT); + // The block must have enough space for a header and be 16-byte aligned + size = round_up(sizeof(block_t) + size, ALIGNMENT); + + // If there is a large enough free block, use it + block_t *block = find_fit(size); + if (block != NULL) { + set_header(block, get_size(block), true); + return block->payload; + } - block_t *block = find_fit(asize); + // Otherwise, a new block needs to be allocated at the end of the heap + block = mem_sbrk(size); + if (block == (void *) -1) { + return NULL; + } - if (!block) { - void * ptr = mem_sbrk(asize); - if (ptr == (void *)-1) { - return NULL; - } - mm_heap_last = (block_t *)ptr; - block = mm_heap_last; - set_header(block, asize, true); + // Update mm_heap_first and mm_heap_last since we extended the heap + if (mm_heap_first == NULL) { + mm_heap_first = block; } + mm_heap_last = block; - set_header(block, get_size(block), true); + // Initialize the block with the allocated size + set_header(block, size, true); return block->payload; } +/** + * mm_free - Releases a block to be reused for future allocations + */ void mm_free(void *ptr) { - if (!ptr) { + // mm_free(NULL) does nothing + if (ptr == NULL) { return; } - block_t *block = (block_t *)(ptr - offsetof(block_t, payload)); - size_t block_size = get_size(block); - set_header(block, block_size, false); + // Mark the block as unallocated + block_t *block = block_from_payload(ptr); + set_header(block, get_size(block), false); } -/* +/** * mm_realloc - Change the size of the block by mm_mallocing a new block, * copying its data, and mm_freeing the old block. - **/ + */ void *mm_realloc(void *old_ptr, size_t size) { - (void)old_ptr; - (void)size; + (void) old_ptr; + (void) size; return NULL; } - -/* +/** * mm_calloc - Allocate the block and set it to zero. */ void *mm_calloc(size_t nmemb, size_t size) { - (void)nmemb; - (void)size; + (void) nmemb; + (void) size; return NULL; } -/* +/** * mm_checkheap - So simple, it doesn't need a checker! */ -void mm_checkheap() { +void mm_checkheap(void) { } diff --git a/src/mm-implicit.c b/src/mm-implicit.c index fc84bd2..cdd7763 100644 --- a/src/mm-implicit.c +++ b/src/mm-implicit.c @@ -5,120 +5,158 @@ * TODO (bug): The allocator doesn't re-use space very well... */ -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdbool.h> #include <stdint.h> -#include <stddef.h> -#include <unistd.h> #include "mm.h" #include "memlib.h" -typedef uint64_t word_t; -const size_t ALIGNMENT = 2 * sizeof(word_t); +/** The required alignment of heap payloads */ +const size_t ALIGNMENT = 2 * sizeof(size_t); +/** The layout of each block allocated on the heap */ typedef struct { + /** The size of the block and whether it is allocated (stored in the low bit) */ size_t header; - /* + /** * We don't know what the size of the payload will be, so we will * declare it as a zero-length array. This allow us to obtain a * pointer to the start of the payload. */ - word_t payload[]; + uint8_t payload[]; } block_t; +/** The first and last blocks on the heap */ static block_t *mm_heap_first = NULL; static block_t *mm_heap_last = NULL; +/** Rounds up `size` to the nearest multiple of `n` */ static size_t round_up(size_t size, size_t n) { - return n * ((size + (n-1)) / n); -} - -static size_t get_size(block_t *block) { - return block->header & ~0x7; + return (size + (n - 1)) / n * n; } +/** Set's a block's header with the given size and allocation state */ static void set_header(block_t *block, size_t size, bool is_allocated) { block->header = size | is_allocated; } +/** Extracts a block's size from its header */ +static size_t get_size(block_t *block) { + return block->header & ~1; +} + +/** Extracts a block's allocation state from its header */ static bool is_allocated(block_t *block) { - return block->header & 0x1; + return block->header & 1; } -block_t *find_fit(size_t size) { - for (block_t *curr = mm_heap_first; mm_heap_last && curr <= mm_heap_last; curr = ((void *)curr + get_size(curr))) { - size_t curr_size = get_size(curr); - if (!is_allocated(curr) && curr_size >= size) { +/** + * Finds the first free block in the heap with at least the given size. + * If no block is large enough, returns NULL. + */ +static block_t *find_fit(size_t size) { + // Traverse the blocks in the heap using the implicit list + for ( + block_t *curr = mm_heap_first; + mm_heap_last != NULL && curr <= mm_heap_last; + curr = (void *) curr + get_size(curr) + ) { + // If the block is free and large enough for the allocation, return it + if (!is_allocated(curr) && get_size(curr) >= size) { return curr; } } return NULL; } +/** Gets the header corresponding to a given payload pointer */ +static block_t *block_from_payload(void *ptr) { + return ptr - offsetof(block_t, payload); +} + +/** + * mm_init - Initializes the allocator state + */ bool mm_init(void) { - mm_heap_first = mem_sbrk(8); - set_header(mm_heap_first, 8, true); + // We want the first payload to start at ALIGNMENT bytes from the start of the heap + void *padding = mem_sbrk(ALIGNMENT - sizeof(block_t)); + if (padding == (void *) -1) { + return false; + } + + // Initialize the heap with no blocks + mm_heap_first = NULL; mm_heap_last = NULL; return true; } +/** + * mm_malloc - Allocates a block with the given size + */ void *mm_malloc(size_t size) { - size_t asize = round_up(size + sizeof(word_t), ALIGNMENT); + // The block must have enough space for a header and be 16-byte aligned + size = round_up(sizeof(block_t) + size, ALIGNMENT); + + // If there is a large enough free block, use it + block_t *block = find_fit(size); + if (block != NULL) { + set_header(block, get_size(block), true); + return block->payload; + } - block_t *block = find_fit(asize); + // Otherwise, a new block needs to be allocated at the end of the heap + block = mem_sbrk(size); + if (block == (void *) -1) { + return NULL; + } - if (!block) { - void * ptr = mem_sbrk(asize); - if (ptr == (void *)-1) { - return NULL; - } - mm_heap_last = (block_t *)ptr; - block = mm_heap_last; - set_header(block, asize, true); + // Update mm_heap_first and mm_heap_last since we extended the heap + if (mm_heap_first == NULL) { + mm_heap_first = block; } + mm_heap_last = block; - set_header(block, get_size(block), true); + // Initialize the block with the allocated size + set_header(block, size, true); return block->payload; } +/** + * mm_free - Releases a block to be reused for future allocations + */ void mm_free(void *ptr) { - if (!ptr) { + // mm_free(NULL) does nothing + if (ptr == NULL) { return; } - block_t *block = (block_t *)(ptr - offsetof(block_t, payload)); - size_t block_size = get_size(block); - set_header(block, block_size, false); + // Mark the block as unallocated + block_t *block = block_from_payload(ptr); + set_header(block, get_size(block), false); } -/* +/** * mm_realloc - Change the size of the block by mm_mallocing a new block, * copying its data, and mm_freeing the old block. - **/ + */ void *mm_realloc(void *old_ptr, size_t size) { - (void)old_ptr; - (void)size; + (void) old_ptr; + (void) size; return NULL; } - -/* +/** * mm_calloc - Allocate the block and set it to zero. */ void *mm_calloc(size_t nmemb, size_t size) { - (void)nmemb; - (void)size; + (void) nmemb; + (void) size; return NULL; } -/* +/** * mm_checkheap - So simple, it doesn't need a checker! */ -void mm_checkheap() { +void mm_checkheap(void) { } -- GitLab