1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
* mm-explicit.c - The best malloc package EVAR!
*
* TODO (bug): Uh..this is an implicit list???
*/
#include <stdint.h>
#include "memlib.h"
#include "mm.h"
/** 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.
*/
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 (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 & 1;
}
/**
* 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) {
// 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) {
// 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;
}
// Otherwise, a new block needs to be allocated at the end of the heap
block = mem_sbrk(size);
if (block == (void *) -1) {
return NULL;
}
// 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;
// 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) {
// mm_free(NULL) does nothing
if (ptr == NULL) {
return;
}
// 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;
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;
return NULL;
}
/**
* mm_checkheap - So simple, it doesn't need a checker!
*/
void mm_checkheap(void) {
}