mm-explicit.c 4.09 KB
Newer Older
Adam Blank's avatar
Adam Blank committed
1
/*
Caleb C. Sander's avatar
Caleb C. Sander committed
2
3
4
 * mm-explicit.c - The best malloc package EVAR!
 *
 * TODO (bug): Uh..this is an implicit list???
Adam Blank's avatar
Adam Blank committed
5
6
7
8
9
 */

#include <stdint.h>

#include "memlib.h"
Caleb C. Sander's avatar
Caleb C. Sander committed
10
#include "mm.h"
Adam Blank's avatar
Adam Blank committed
11

Caleb C. Sander's avatar
Caleb C. Sander committed
12
13
/** The required alignment of heap payloads */
const size_t ALIGNMENT = 2 * sizeof(size_t);
Adam Blank's avatar
Adam Blank committed
14

Caleb C. Sander's avatar
Caleb C. Sander committed
15
/** The layout of each block allocated on the heap */
Adam Blank's avatar
Adam Blank committed
16
typedef struct {
Caleb C. Sander's avatar
Caleb C. Sander committed
17
    /** The size of the block and whether it is allocated (stored in the low bit) */
Adam Blank's avatar
Adam Blank committed
18
    size_t header;
Caleb C. Sander's avatar
Caleb C. Sander committed
19
    /**
Adam Blank's avatar
Adam Blank committed
20
21
22
23
     * 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.
     */
Caleb C. Sander's avatar
Caleb C. Sander committed
24
    uint8_t payload[];
Adam Blank's avatar
Adam Blank committed
25
26
} block_t;

Caleb C. Sander's avatar
Caleb C. Sander committed
27
/** The first and last blocks on the heap */
Adam Blank's avatar
Adam Blank committed
28
29
30
static block_t *mm_heap_first = NULL;
static block_t *mm_heap_last = NULL;

Caleb C. Sander's avatar
Caleb C. Sander committed
31
/** Rounds up `size` to the nearest multiple of `n` */
Adam Blank's avatar
Adam Blank committed
32
static size_t round_up(size_t size, size_t n) {
Caleb C. Sander's avatar
Caleb C. Sander committed
33
    return (size + (n - 1)) / n * n;
Adam Blank's avatar
Adam Blank committed
34
35
}

Caleb C. Sander's avatar
Caleb C. Sander committed
36
/** Set's a block's header with the given size and allocation state */
Adam Blank's avatar
Adam Blank committed
37
38
39
40
static void set_header(block_t *block, size_t size, bool is_allocated) {
    block->header = size | is_allocated;
}

Caleb C. Sander's avatar
Caleb C. Sander committed
41
42
43
44
45
46
/** 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 */
Adam Blank's avatar
Adam Blank committed
47
static bool is_allocated(block_t *block) {
Caleb C. Sander's avatar
Caleb C. Sander committed
48
    return block->header & 1;
Adam Blank's avatar
Adam Blank committed
49
50
}

Caleb C. Sander's avatar
Caleb C. Sander committed
51
52
53
54
55
56
/**
 * 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
Caleb C. Sander's avatar
Caleb C. Sander committed
57
58
    for (block_t *curr = mm_heap_first; mm_heap_last != NULL && curr <= mm_heap_last;
         curr = (void *) curr + get_size(curr)) {
Caleb C. Sander's avatar
Caleb C. Sander committed
59
60
        // If the block is free and large enough for the allocation, return it
        if (!is_allocated(curr) && get_size(curr) >= size) {
Adam Blank's avatar
Adam Blank committed
61
62
63
64
65
66
            return curr;
        }
    }
    return NULL;
}

Caleb C. Sander's avatar
Caleb C. Sander committed
67
68
69
70
71
72
73
74
/** 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
 */
Adam Blank's avatar
Adam Blank committed
75
bool mm_init(void) {
Caleb C. Sander's avatar
Caleb C. Sander committed
76
77
78
79
80
81
82
83
    // 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;
Adam Blank's avatar
Adam Blank committed
84
85
86
87
    mm_heap_last = NULL;
    return true;
}

Caleb C. Sander's avatar
Caleb C. Sander committed
88
89
90
/**
 * mm_malloc - Allocates a block with the given size
 */
Adam Blank's avatar
Adam Blank committed
91
void *mm_malloc(size_t size) {
Caleb C. Sander's avatar
Caleb C. Sander committed
92
93
94
95
96
97
98
99
100
    // 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;
    }
Adam Blank's avatar
Adam Blank committed
101

Caleb C. Sander's avatar
Caleb C. Sander committed
102
103
104
105
106
    // Otherwise, a new block needs to be allocated at the end of the heap
    block = mem_sbrk(size);
    if (block == (void *) -1) {
        return NULL;
    }
Adam Blank's avatar
Adam Blank committed
107

Caleb C. Sander's avatar
Caleb C. Sander committed
108
109
110
    // Update mm_heap_first and mm_heap_last since we extended the heap
    if (mm_heap_first == NULL) {
        mm_heap_first = block;
Adam Blank's avatar
Adam Blank committed
111
    }
Caleb C. Sander's avatar
Caleb C. Sander committed
112
    mm_heap_last = block;
Adam Blank's avatar
Adam Blank committed
113

Caleb C. Sander's avatar
Caleb C. Sander committed
114
115
    // Initialize the block with the allocated size
    set_header(block, size, true);
Adam Blank's avatar
Adam Blank committed
116
117
118
    return block->payload;
}

Caleb C. Sander's avatar
Caleb C. Sander committed
119
120
121
/**
 * mm_free - Releases a block to be reused for future allocations
 */
Adam Blank's avatar
Adam Blank committed
122
void mm_free(void *ptr) {
Caleb C. Sander's avatar
Caleb C. Sander committed
123
124
    // mm_free(NULL) does nothing
    if (ptr == NULL) {
Adam Blank's avatar
Adam Blank committed
125
126
127
        return;
    }

Caleb C. Sander's avatar
Caleb C. Sander committed
128
129
130
    // Mark the block as unallocated
    block_t *block = block_from_payload(ptr);
    set_header(block, get_size(block), false);
Adam Blank's avatar
Adam Blank committed
131
132
}

Caleb C. Sander's avatar
Caleb C. Sander committed
133
/**
Adam Blank's avatar
Adam Blank committed
134
135
 * mm_realloc - Change the size of the block by mm_mallocing a new block,
 *      copying its data, and mm_freeing the old block.
Caleb C. Sander's avatar
Caleb C. Sander committed
136
 */
Adam Blank's avatar
Adam Blank committed
137
void *mm_realloc(void *old_ptr, size_t size) {
Caleb C. Sander's avatar
Caleb C. Sander committed
138
139
    (void) old_ptr;
    (void) size;
Adam Blank's avatar
Adam Blank committed
140
141
142
    return NULL;
}

Caleb C. Sander's avatar
Caleb C. Sander committed
143
/**
Adam Blank's avatar
Adam Blank committed
144
145
146
 * mm_calloc - Allocate the block and set it to zero.
 */
void *mm_calloc(size_t nmemb, size_t size) {
Caleb C. Sander's avatar
Caleb C. Sander committed
147
148
    (void) nmemb;
    (void) size;
Adam Blank's avatar
Adam Blank committed
149
150
151
    return NULL;
}

Caleb C. Sander's avatar
Caleb C. Sander committed
152
/**
Adam Blank's avatar
Adam Blank committed
153
154
 * mm_checkheap - So simple, it doesn't need a checker!
 */
Caleb C. Sander's avatar
Caleb C. Sander committed
155
void mm_checkheap(void) {
Adam Blank's avatar
Adam Blank committed
156
}