forked from ltganesan/Malloc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mm.c
619 lines (545 loc) · 18.7 KB
/
mm.c
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
/*mm-explicit.c - Explicit free list implementation
* Name: Lalitha Ganesan
* Andrew ID: ltg
*
* SPECS:
* Doubly Linked Explicit Free List
* First Fit Scanning Algorithm
*
* MALLOCING:
* In this approach, a block is allocated by traversing the free list
* and deleting the first free block found that has a size >= the
* requested size in the param of malloc.
* If a free block is too big, split the block if the remaining part is
* big enough to be a free block, delete the first part (as indicated
* by the size in the malloc param), and change the header and
* footer of the new (smaller) free block (if necessary).
*
* FREEING:
* When free(ptr) is called, find the block and change its header and
* footer info to include a 0 allocation bit.
* Newly freed blocks are placed at the beginning of the free list.
* When a newly freed block is adjacent to at least one other free block,
* we must coalesce the blocks right after freeing.
*
* REALLOCING:
* If decreasing size, shrink the block => change the header and footer
* info.
* If increasing size, call malloc with new size, copy old data, then \
* free old block.
* If size is same, keep same block.
*
* CALLOCING:
* Malloc the block, then set the data to zero.
*
* ANATOMY OF BLOCKS:
* This is how a free block looks: HEADER | PREV FREE | NEXT FREE | OLD DATA | FOOTER
* This is how an allocated block looks: HEADER |--------------DATA----------------| FOOTER
*
* In the header and footer: 8 bit word containing size of block and
* allocation flag bit
*
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "mm.h"
#include "memlib.h"
/* If you want debugging output, use the following macro. When you hand
* in, remove the #define DEBUG line. */
#ifdef DEBUG
# define dbg_printf(...) printf(__VA_ARGS__)
# define dbg_mm_checkheap(...) mm_checkheap(__VA_ARGS__)
#else
# define dbg_printf(...)
# define dbg_mm_checkheap(...)
#endif
/* do not change the following! */
#ifdef DRIVER
/* create aliases for driver tests */
#define malloc mm_malloc
#define free mm_free
#define realloc mm_realloc
#define calloc mm_calloc
#endif /* def DRIVER */
/* single word (4) or double word (8) alignment */
#define ALIGNMENT 8
/* rounds up to the nearest multiple of ALIGNMENT */
#define ALIGN(p) (((size_t)(p) + (ALIGNMENT-1)) & ~0x7)
/* Basic constants and macros */
#define WSIZE 4 /* word size (bytes) */
#define DSIZE 8 /* doubleword size (bytes) */
#define CHUNKSIZE 16 /* initial heap size (bytes) */
#define MINIMUM 24 /* minimum block size */
#define MAX(x, y) ((x) > (y) ? (x) : (y))
/* Pack a size and allocated bit into a word */
#define PACK(size, alloc) ((size) | (alloc))
/* Read and write a word at address p */
#define GET(p) (*(int *)(p))
#define PUT(p, val) (*(int *)(p) = (val))
/* Read the size and allocated fields from address p */
#define GET_SIZE(p) (GET(p) & ~0x7)
#define GET_ALLOC(p) (GET(p) & 0x1)
/* Given block ptr bp, compute address of its header and footer */
#define HDRP(bp) ((void *)(bp) - WSIZE)
#define FTRP(bp) ((void *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)
/* Given block ptr bp, compute address of next and previous blocks */
#define NEXT_BLKP(bp) ((void *)(bp) + GET_SIZE(HDRP(bp)))
#define PREV_BLKP(bp) ((void *)(bp) - GET_SIZE(HDRP(bp) - WSIZE))
/* Given block ptr bp, compute address of next and previous free blocks */
#define NEXT_FREEP(bp)(*(void **)(bp + DSIZE))
#define PREV_FREEP(bp)(*(void **)(bp))
static char *heap_listp = 0; /* Pointer to the first block */
static char *free_listp = 0;/* Pointer to the first free block */
/* Function prototypes for internal helper routines */
static void *extendHeap(size_t words);
static void place(void *bp, size_t asize);
static void *findFit(size_t asize);
static void *coalesce(void *bp);
static void printBlock(void *bp);
static void checkBlock(void *bp);
static void insertAtFront(void *bp); /* Linked list function */
static void removeBlock(void *bp); /* Linked list function */
/*
* mm_init - Initialize the memory manager
* This function gets heap space and takes care of alignment by adding
* padding and creating a dummy block to make sure the payload is aligned on a
* 4-byte boundary.
* The initial heap looks like this: |PADDING|DUMMY HEADER|DUMMY FOOTER|TAIL BLOCK|
* PADDING = 4 bytes, DUMMY HEADER = DUMMY FOOTER = 8 bytes, TAIL BLOCK = 4 bytes
* TAIL BLOCK signals the end of the heap => It signals the end of the free list.
*
* This function should return 0 if successful, -1 if failure.
*/
int mm_init(void)
{
/*return -1 if unable to get heap space*/
if ((heap_listp = mem_sbrk(2*MINIMUM)) == NULL)
return -1;
PUT(heap_listp, 0); //Alignment padding
/*initialize dummy block header*/
PUT(heap_listp + WSIZE, PACK(MINIMUM, 1)); //WSIZE = padding
PUT(heap_listp + DSIZE, 0); //PREV pointer
PUT(heap_listp + DSIZE+WSIZE, 0); //NEXT pointer
/*initialize dummy block footer*/
PUT(heap_listp + MINIMUM, PACK(MINIMUM, 1));
/*initialize dummy tail block*/
PUT(heap_listp+WSIZE + MINIMUM, PACK(0, 1));
/*initialize the free list pointer to the tail block*/
free_listp = heap_listp + DSIZE;
/*return -1 if unable to get heap space*/
if (extendHeap(CHUNKSIZE/WSIZE) == NULL)
return -1;
return 0;
}
/*
* mm_malloc - Allocate a block with at least size bytes of payload
* This function takes into account alignment and how the heap space
* is organized during any given malloc call.
*
* The adjusted block size is calculated by taking the max of the
* minimum size (24 bytes) and the requested size (aligned size + 8).
*
* It then searches the free list until it finds a place to put the block.
* Using this block pointer, it places the block in this spot.
*
* If no space was found, we must ask for more memory and then place at
* the block at the start of the new heap memory.
*
* This function takes a payload size as a parameter and returns
* a pointer to the start of the alocated block.
*/
void *mm_malloc(size_t size)
{
size_t asize; /* adjusted block size */
size_t extendsize; /* amount to extend heap if no fit */
char *bp;
/* Ignore spurious requests */
if (size <= 0)
return NULL;
/* Adjust block size to include overhead and alignment reqs */
asize = MAX(ALIGN(size) + DSIZE, MINIMUM);
/* Search the free list for a fit */
if ((bp = findFit(asize)))
{
place(bp, asize);
return bp;
}
/* No fit found. Get more memory and place the block */
extendsize = MAX(asize, CHUNKSIZE);
//return NULL if unable to get heap space
if ((bp = extendHeap(extendsize/WSIZE)) == NULL)
return NULL;
place(bp, asize);
return bp;
}
/*
* mm_free - Free a block
* This function adds a block to the free list.
* Using block pointer, set the allocation bits to 0 in
* both the header and footer of the block.
* Then, coalesce with adjacent blocks, if applicable.
* This function takes a block pointer as a parameter.
*/
void mm_free(void *bp)
{
if(!bp) return; //return if the pointer is NULL
size_t size = GET_SIZE(HDRP(bp));
//set header and footer to unallocated
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
coalesce(bp); //coalesce and add the block to the free list
}
/*
* mm_realloc - Reallocate a block
* This function extends or shrinks an allocated block.
* If the new size is <= 0, then just free the block.
* If the new size is the same as the old size,just return the old ptr.
* If the new size is less than the old size, shrink the block
* by changing the size in the header and footer of the block.
* Then, if the remaining is at least teh minimum block size, create a free
* block.
* If the new size is greater than the old size, call malloc using the new size,
* copy all of the old data, then call free to the original block.
*
* This function takes a block pointer and a new size as parameters and
* returns a block pointer to the newly allocated block.
*/
void *mm_realloc(void *ptr, size_t size)
{
size_t oldsize;
void *newptr;
size_t asize = MAX(ALIGN(size) + DSIZE, MINIMUM);
/* If size <= 0 then this is just free, and we return NULL. */
if(size <= 0) {
free(ptr);
return 0;
}
/* If oldptr is NULL, then this is just malloc. */
if(ptr == NULL) {
return malloc(size);
}
/* Get the size of the original block */
oldsize = GET_SIZE(HDRP(ptr));
/* If the size doesn't need to be changed, return orig pointer */
if (asize == oldsize)
return ptr;
/* If the size needs to be decreased, shrink the block and
* return the same pointer */
if(asize <= oldsize)
{
size = asize;
/* If a new block couldn't fit in the remaining space,
* return the pointer */
if(oldsize - size <= MINIMUM)
return ptr;
PUT(HDRP(ptr), PACK(size, 1));
PUT(FTRP(ptr), PACK(size, 1));
PUT(HDRP(NEXT_BLKP(ptr)), PACK(oldsize-size, 1));
free(NEXT_BLKP(ptr));
return ptr;
}
newptr = malloc(size);
/* If realloc() fails the original block is left untouched */
if(!newptr) {
return 0;
}
/* Copy the old data. */
if(size < oldsize) oldsize = size;
memcpy(newptr, ptr, oldsize);
/* Free the old block. */
free(ptr);
return newptr;
}
/*
* calloc - Allocate the block and set it to zero.
* This function allocates a block of given size and sets it to 0.
* First, malloc the size payload desired. Then, set memory to 0.
*
* This function takes 2 parameters: number f elements in an array and
* the size of each element.
* It returns the block pointer to the newly allocated block.
*/
void *calloc (size_t nmemb, size_t size)
{
size_t bytes = nmemb * size;
void *newptr;
newptr = malloc(bytes);
memset(newptr, 0, bytes);
return newptr;
}
/*
* mm_checkheap - Check the heap for consistency
* Checks the epilogue and prologue blocks for size and allocation bit.
* Checks the 8-byte address alignment for each block in the free list.
* Checks each free block to see if its next and previous pointers are
* within heap bounds.
* Checks the consistency of header and footer size and allocation bits
* for each free block.
*/
void mm_checkheap(int verbose)
{
void *bp = heap_listp; //Points to the first block in the heap
if (verbose)
printf("Heap (%p):\n", heap_listp);
/* If first block's header's size or allocation bit is wrong,
* the prologue header is wrong
*/
if ((GET_SIZE(HDRP(heap_listp)) != MINIMUM) ||
!GET_ALLOC(HDRP(heap_listp)))
printf("Bad prologue header\n");
checkBlock(heap_listp); //
/* Print the stats of every free block in the free list */
for (bp = free_listp; GET_ALLOC(HDRP(bp))==0; bp = NEXT_FREEP(bp))
{
if (verbose)
printBlock(bp);
checkBlock(bp);
}
/* Print the stats of the last block in the heap */
if (verbose)
printBlock(bp);
/* If last block's header's size or allocation bit is wrong,
* the epilogue's header is wrong
*/
if ((GET_SIZE(HDRP(bp)) != 0) || !(GET_ALLOC(HDRP(bp))))
printf("Bad epilogue header\n");
}
/* The remaining routines are internal helper routines */
/*
* extendHeap - Extend heap with free block and return its block pointer
* This function maintains alignment by only allocating an even number of
* words to extend the heap by.
*
* We overwrite the epilogue of the previously aquired heap space with
* the header of the first new block.
* Then, after the new space is added onto the heap, we add an epilogue
* header in the beginning of the newly aquired heap space.
*
* This function takes a size to extend the heap by as a parameter and
* returns a block pointer to the first block in the newly acquired heap
* space.
*/
static void *extendHeap(size_t words)
{
char *bp;
size_t size;
/* Allocate an even number of words to maintain alignment */
size = (words % 2) ? (words+1) * WSIZE : words * WSIZE;
if (size < MINIMUM)
size = MINIMUM;
if ((long)(bp = mem_sbrk(size)) == -1)
return NULL;
/* Initialize free block header/footer and the epilogue header */
PUT(HDRP(bp), PACK(size, 0)); /* free block header */
PUT(FTRP(bp), PACK(size, 0)); /* free block footer */
PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1)); /* new epilogue header */
/* Coalesce if the previous block was free and add the block to
* the free list */
return coalesce(bp);
}
/*
* place - Place block of asize bytes at start of free block bp
*
* This function places the block by comparing asize with the total
* block size, csize. If the difference is at least the minimum block
* size, we can split the block into an allocated block and a free block.
* If not, we declare the whole block as allocated to avoid excessive
* external fragmentation.
*
* This function takes a block pointer to a free block and the size of the
* block we wish to place there.
*/
static void place(void *bp, size_t asize)
{
/* Gets the size of the whole free block */
size_t csize = GET_SIZE(HDRP(bp));
/* If the difference is at least 24 bytes, change the header and footer
* info for the allocated block (size = asize, allocated = 1) and
* remove the block from the free list.
* Then, split the block by:
* (1) Changing the header and footer info for the free block created from the
* remaining space (size = csize-asize, allocated = 0)
* (2) Coalescing the new free block with adjacent free blocks
*/
if ((csize - asize) >= MINIMUM) {
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1));
removeBlock(bp);
bp = NEXT_BLKP(bp);
PUT(HDRP(bp), PACK(csize-asize, 0));
PUT(FTRP(bp), PACK(csize-asize, 0));
coalesce(bp);
}
/* If the remaining space is not enough for a free block, don't split the block */
else {
PUT(HDRP(bp), PACK(csize, 1));
PUT(FTRP(bp), PACK(csize, 1));
removeBlock(bp);
}
}
/*
* findFit - Find a fit for a block with asize bytes
* This function iterates through the free list and uses a first-fit search
* to find the first free block with size greater than or equal to the requested block size
*
* This function takes the requested block size, asize, as a parameter
* and returns a pointer to the free block we wish to use for allocation.
*/
static void *findFit(size_t asize)
{
void *bp;
/* First fit search */
/* Iterates through free list to find a free block large enough to
* accomodate the size we want to allocate.
*/
for (bp = free_listp; GET_ALLOC(HDRP(bp)) == 0; bp = NEXT_FREEP(bp))
{
if (asize <= (size_t)GET_SIZE(HDRP(bp)))
return bp;
}
return NULL; // No fit
}
/*
* coalesce - boundary tag coalescing.
* This function joins adjecent free blocks together by
* finding the size (newsize) of the new (bigger) free block, removing the
* free block(s) from the free list, and changing the header
* and footer information to the newly coalesced free block
* (size = newsize, allocated = 0)
* Then, we add the new free block to the front of the free list.
*
* This function takes a block pointer to the newly freed block (around which
* we must coalesce) and returns the block pointer to the coalesced free
* block.
* Return ptr to coalesced block
*/
static void *coalesce(void *bp)
{
size_t prev_alloc;
prev_alloc = GET_ALLOC(FTRP(PREV_BLKP(bp))) || PREV_BLKP(bp) == bp;
size_t next_alloc = GET_ALLOC(HDRP(NEXT_BLKP(bp)));
size_t size = GET_SIZE(HDRP(bp));
/* Case 1, extend the block leftward */
if (prev_alloc && !next_alloc)
{
size += GET_SIZE(HDRP(NEXT_BLKP(bp)));
removeBlock(NEXT_BLKP(bp));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
}
/* Case 2, extend the block rightward */
else if (!prev_alloc && next_alloc)
{
size += GET_SIZE(HDRP(PREV_BLKP(bp)));
bp = PREV_BLKP(bp);
removeBlock(bp);
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
}
/* Case 3, extend the block in both directions */
else if (!prev_alloc && !next_alloc)
{
size += GET_SIZE(HDRP(PREV_BLKP(bp))) +
GET_SIZE(HDRP(NEXT_BLKP(bp)));
removeBlock(PREV_BLKP(bp));
removeBlock(NEXT_BLKP(bp));
bp = PREV_BLKP(bp);
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
}
insertAtFront(bp);
return bp;
}
/*
* printBlock - Prints the details of a block in the list.
* This function displays previous and next pointers if the block
* is marked as free.
*
* This function takes a block pointer (to a block for examination) as a
* parameter.
*/
static void printBlock(void *bp)
{
int hsize, halloc, fsize, falloc;
/* Basic header and footer information */
hsize = GET_SIZE(HDRP(bp));
halloc = GET_ALLOC(HDRP(bp));
fsize = GET_SIZE(FTRP(bp));
falloc = GET_ALLOC(FTRP(bp));
if (hsize == 0)
{
printf("%p: EOL\n", bp);
return;
}
/* Prints out header and footer info if it's an allocated block.
* Prints out header and footer info and next and prev info
* if it's a free block.
*/
if (halloc)
printf("%p: header:[%d:%c] footer:[%d:%c]\n", bp,
hsize, (halloc ? 'a' : 'f'),
fsize, (falloc ? 'a' : 'f'));
else
printf("%p:header:[%d:%c] prev:%p next:%p footer:[%d:%c]\n",
bp, hsize, (halloc ? 'a' : 'f'), PREV_FREEP(bp),
NEXT_FREEP(bp), fsize, (falloc ? 'a' : 'f'));
}
/*
* checkBlock - Checks a block for consistency
* Checks prev and next pointers to see if they are within heap boundaries.
* Checks for 8-byte alignment.
* Checks header and footer for consistency.
*
* This function takes a block pointer (to a block for examinination) as a
* parameter.
*/
static void checkBlock(void *bp)
{
// Reports if the next and prev pointers are within heap bounds
if (NEXT_FREEP(bp)< mem_heap_lo() || NEXT_FREEP(bp) > mem_heap_hi())
printf("Error: next pointer %p is not within heap bounds \n"
, NEXT_FREEP(bp));
if (PREV_FREEP(bp)< mem_heap_lo() || PREV_FREEP(bp) > mem_heap_hi())
printf("Error: prev pointer %p is not within heap bounds \n"
, PREV_FREEP(bp));
/* Reports if there isn't 8-byte alignment by checking if the block pointer
* is divisible by 8.
*/
if ((size_t)bp % 8)
printf("Error: %p is not doubleword aligned\n", bp);
// Reports if the header information does not match the footer information
if (GET(HDRP(bp)) != GET(FTRP(bp)))
printf("Error: header does not match footer\n");
}
/*
* insertAtFront - Inserts a block at the front of the free list
* This function takes a block pointer of the block to add to the
* free list as a parameter.
*/
static void insertAtFront(void *bp)
{
NEXT_FREEP(bp) = free_listp; //Sets next ptr to start of free list
PREV_FREEP(free_listp) = bp; //Sets current's prev to new block
PREV_FREEP(bp) = NULL; // Sets prev pointer to NULL
free_listp = bp; // Sets start of free list as new block
}
/*
* removeBlock - Removes a block from the free list
* This function takes a block pointer of the block to remove as a
* parameter.
*/
static void removeBlock(void *bp)
{
/* If there's a previous block, set its next pointer to the
* next block.
* If not, set the block's previous pointer to the prev block.
*/
if (PREV_FREEP(bp))
NEXT_FREEP(PREV_FREEP(bp)) = NEXT_FREEP(bp);
else
free_listp = NEXT_FREEP(bp);
PREV_FREEP(NEXT_FREEP(bp)) = PREV_FREEP(bp);
}