#include #include #include #include "mm.h" #include "memlib.h" #include "list.h" #include "config.h" struct boundary_tag { int inuse:1; int size:31; }; /* FENCE is used for heap prologue/epilogue. */ const struct boundary_tag FENCE = { .inuse = 1, .size = 0 }; struct block { struct boundary_tag header; char payload[0]; struct list_elem elem; }; static struct block *heap_listp = 0; /* Basic constants and macros */ #define WSIZE sizeof(struct boundary_tag) /* Word and header/footer size (bytes) */ #define DSIZE 2*WSIZE #define MIN_BLOCK_SIZE_WORDS 8 /* Minimum block size in words */ #define CHUNKSIZE (1<<10) /* Extend heap by this amount (words) */ #define NUM_LISTS 8 /* Number of free lists */ static inline size_t max(size_t x, size_t y) { return x > y ? x : y; } static size_t align(size_t size) { return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1); } static struct list *free_lists[NUM_LISTS]; static size_t list_sizes[NUM_LISTS]; /* Function prototypes for internal helper routines */ static struct block *extend_heap(size_t words, int realloc); static void place(struct block *blck, size_t asize); static struct list* find_list(int not_empty, size_t size); static struct block *find_fit(size_t num_words); //~ static void coalesce_free_lists(); static struct block *coalesce(struct block *bp); //~ static void print_lists(); /* Given a block, obtain previous's block footer. Works for left-most block also. */ static struct boundary_tag * prev_blk_footer(struct block *blk) { return &blk->header - 1; } /* Return if block is free */ static bool blk_free(struct block *blk) { return !blk->header.inuse; } /* Return size of block is free */ static size_t blk_size(struct block *blk) { return blk->header.size; } /* Given a block, obtain pointer to previous block. Not meaningful for left-most block. */ static struct block *prev_blk(struct block *blk) { struct boundary_tag *prevfooter = prev_blk_footer(blk); assert(prevfooter->size != 0); return (struct block *)((void *)blk - WSIZE * prevfooter->size); } /* Given a block, obtain pointer to next block. Not meaningful for right-most block. */ static struct block *next_blk(struct block *blk) { assert(blk_size(blk) != 0); return (struct block *)((void *)blk + WSIZE * blk->header.size); } /* Given a block, obtain its footer boundary tag */ static struct boundary_tag * get_footer(struct block *blk) { return ((void *)blk + WSIZE * blk->header.size) - sizeof(struct boundary_tag); } /* Set a block's size and inuse bit in header and footer */ static void set_header_and_footer(struct block *blk, int size, int inuse) { blk->header.inuse = inuse; blk->header.size = size; * get_footer(blk) = blk->header; /* Copy header to footer */ } /* Mark a block as used and set its size. */ static void set_block_used(struct block *blk, int size) { set_header_and_footer(blk, size, 1); } /* Mark a block as free and set its size. */ static void set_block_free(struct block *blk, int size) { set_header_and_footer(blk, size, 0); } int mm_init(void) { assert (offsetof(struct block, payload) == 4); assert (sizeof(struct boundary_tag) == 4); /* Create the initial empty heap */ struct boundary_tag * initial = mem_sbrk(4 * sizeof(struct boundary_tag)); if (initial == NULL) return -1; /* We use a slightly different strategy than suggested in the book. * Rather than placing a min-sized prologue block at the beginning * of the heap, we simply place two fences. * The consequence is that coalesce() must call prev_blk_footer() * and not prev_blk() because prev_blk() cannot be called on the * left-most block. */ initial[2] = FENCE; /* Prologue footer */ heap_listp = (struct block *)&initial[3]; initial[3] = FENCE; /* Epilogue header */ for (int i = 0; i < NUM_LISTS; i++) { struct list *current_list = malloc(sizeof(struct list)); list_init(current_list); free_lists[i] = current_list; } for (int i = 0; i < NUM_LISTS; i++) list_sizes[i] = MIN_BLOCK_SIZE_WORDS * (i + 1); /* Extend the empty heap with a free block of CHUNKSIZE bytes */ if (extend_heap(CHUNKSIZE, 0) == NULL) return -1; return 0; } /* * The mm malloc routine returns a pointer to an allocated block payload of at * least size bytes. The entire allocated block should lie within the heap region and should * not overlap with any other allocated chunk. */ void *mm_malloc(size_t size) { struct block *blck; if (size == 0) { return NULL; } size_t words = max(MIN_BLOCK_SIZE_WORDS, align(size + DSIZE) / WSIZE); /* Find fit for size */ if ((blck = find_fit(words)) != NULL) { place(blck, words); return blck->payload; } //~ coalesce_free_lists(); //~ if ((blck = find_fit(words)) != NULL) { //~ place(blck, words); //~ return blck->payload; //~ } if ((blck = extend_heap(words, 0)) == NULL){ return NULL; } place(blck, words); return blck->payload; } void mm_free(void *bp) { assert (heap_listp != 0); // assert that mm_init was called if (bp == 0) return; /* Find block from user pointer */ struct block *blk = bp - offsetof(struct block, payload); size_t size = blk_size(blk); set_block_free(blk, size); list_push_front(find_list(0, size), &blk->elem); coalesce(blk); } //~ static void coalesce_free_lists() //~ { //~ for (int i = 0; i < NUM_LISTS; i++) { //~ struct list* current_list = &free_lists[i]; //~ for (struct list_elem* e = list_begin(current_list); e != list_end(current_list); e = list_next(e)) { //~ struct block* bp = list_entry(e, struct block, elem); //~ coalesce(bp); //~ } //~ } //~ } void *mm_realloc(void *ptr, size_t size) { void *new_ptr = NULL; if (ptr == NULL) { return mm_malloc(size); } if (size == 0) { mm_free(ptr); return NULL; } struct block *oldblock = ptr - offsetof(struct block, payload); size_t oldwords = blk_size(oldblock); size_t oldsize = oldwords * WSIZE; if (!(new_ptr = mm_malloc(size))) { return NULL; } if (size < oldsize) { oldsize = size; } memcpy(new_ptr, ptr, oldsize); mm_free(ptr); return new_ptr; } static struct block *coalesce(struct block *bp) { bool prev_alloc = prev_blk_footer(bp)->inuse; /* is previous block allocated? */ bool next_alloc = ! blk_free(next_blk(bp)); /* is next block allocated? */ size_t size = blk_size(bp); if (prev_alloc && next_alloc) { /* Case 1 */ // both are allocated, nothing to coalesce return bp; } else if (prev_alloc && !next_alloc) { /* Case 2 */ // combine this block and next block by extending it list_remove(&bp->elem); list_remove(&next_blk(bp)->elem); size_t new_size = size + blk_size(next_blk(bp)); set_block_free(bp, new_size); list_push_front(find_list(0, new_size), &bp->elem); } else if (!prev_alloc && next_alloc) { /* Case 3 */ // combine previous and this block by extending previous list_remove(&prev_blk(bp)->elem); list_remove(&bp->elem); size_t new_size = blk_size(prev_blk(bp)) + size; bp = prev_blk(bp); set_block_free(bp, new_size); list_push_front(find_list(0, new_size), &bp->elem); } else { /* Case 4 */ // combine all previous, this, and next block into one list_remove(&prev_blk(bp)->elem); list_remove(&bp->elem); list_remove(&next_blk(bp)->elem); size_t new_size = blk_size(prev_blk(bp)) + size + blk_size(next_blk(bp)); bp = prev_blk(bp); set_block_free(bp, new_size); list_push_front(find_list(0, new_size), &bp->elem); } return bp; } /* * Extend heap with free block and return its block pointer * Does not add new block to free list if called by realloc */ static struct block *extend_heap(size_t words, int realloc) { void *blck = mem_sbrk(words * WSIZE); if (blck == NULL) return NULL; /* Initialize free block header, footer and epilogue header. */ struct block * blk = blck - sizeof(FENCE); set_block_free(blk, words); next_blk(blk)->header = FENCE; if (realloc) { return blk; } list_push_front(find_list(0, words), &blk->elem); return coalesce(blk); } /* * Allocate block of memory at address blck */ static void place(struct block *blck, size_t asize) { size_t csize = blk_size(blck); //~ print_lists(); list_remove(&blck->elem); //~ print_lists(); size_t remaining_size = csize - asize; if (remaining_size >= MIN_BLOCK_SIZE_WORDS) { set_block_used(blck, asize); blck = next_blk(blck); set_block_free(blck, remaining_size); /* Add remaining free block back into list */ list_push_front(find_list(0, remaining_size), &blck->elem); //~ print_lists(); } else { set_block_used(blck, csize); } } /** * Find free list with blocks of given size * * not_empty == 0, find "any" list * not_empty == 1, find a not empty list */ static struct list* find_list(int not_empty, size_t n_words) { for (int i = 0; i < NUM_LISTS; i++) { if ((n_words < list_sizes[i]) || (i == (NUM_LISTS - 1))) { if (not_empty && list_empty(free_lists[i])) { continue; } return free_lists[i]; } } return NULL; } /* * Find a fit for a block with num_words */ static struct block *find_fit(size_t num_words) { struct list* free_list = find_list(1, num_words); //~ print_lists(); if (free_list == NULL) { return NULL; } /* Search for block from selected free list*/ for (struct list_elem* e = list_back(free_list); e != list_head(free_list); e = list_prev(e)) { struct block* bp = list_entry(e, struct block, elem); if (num_words <= blk_size(bp)) { return bp; } } return NULL; } //~ static void print_lists() //~ { //~ for (int i = 0; i < NUM_LISTS; i++) //~ { //~ struct list *lp = free_lists[i]; //~ printf("Free List %d: ", i); //~ for (struct list_elem *e = list_begin(lp); e != list_end(lp); e = list_next(e)) //~ { //~ struct block *bp = list_entry(e, struct block, elem); //~ printf("{inuse: %d, size: %d}\t", bp->header.inuse, bp->header.size); //~ } //~ printf("\n"); //~ } //~ } team_t team = { "Micah Moore Felicia Seo", "Micah Moore", "micahmoore@vt.edu", "Felicia Seo", "seofelicia@vt.edu", };