malloclab/mm.c
2022-11-12 08:47:17 -05:00

384 lines
10 KiB
C

#include <string.h>
#include <stdlib.h>
#include <assert.h>
#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);
/* 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;
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, 1) == 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_back(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);
/* Remove block from free list */
list_remove(&blck->elem);
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);
}
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);
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;
}
team_t team = {
"Micah Moore Felicia Seo",
"Micah Moore",
"micahmoore@vt.edu",
"Felicia Seo",
"seofelicia@vt.edu",
};