您的位置:首页 > 移动开发

CSAPP: Malloc Lab 7

2016-01-16 10:47 411 查看
csappmallocoptimizationlabics

本次Lab真是CSAPP系列Lab中最恶心的Lab了!

这是《深入理解计算机系统》第二版配套Lab中的第7个Lab,对应本书的第9章:虚拟存储器。

下载地址:http://csapp.cs.cmu.edu/im/labs/malloclab.tar

Lab的要求是自己实现类似GNU Libc的malloc和free函数,也就是实现一个动态内存分配器,让你亲手管理一个程序的堆内存分配。最后会从吞吐量(单位时间可执行次数)和空间利用率两个方面进行评估。

本书中介绍了不少空闲链表的形式、分配策略,以及每个堆块的布局,诸如:

隐式空闲链表,只记录每个块的大小,分配时遍历整个堆寻找大小适合的块
显式空闲链表,在空闲块中额外记录前一个、后一个空闲块的位置,可以节省遍历时间
分离空闲链表,维护多个链表,将不同大小类的块分到同一个链表中

首次适配:选择第一个合适的块
下次适配:每次搜索从上次结束的地方开始
最佳适配:选择大小合适的最小块

其他实现与堆块的布局在此就不赘述了~书上都有所介绍
这个Lab我整整做了4天多,前三天一直在Debug,直到最后一天才能正常运行,然后又花了几个小时优化才达到满分……

我的思路是空闲链表+BST:

使用一小撮空闲链表来维护较小的空闲块
使用二叉搜索树来维护大的空闲块链表
所有的链表只存一种大小的空闲块
具体如图所示,二叉搜索树的每个节点都是一个链表的头部





优化策略:

1、已分配的块可以省略脚部(Footer)。
那么相邻空闲块合并时从何得知前一块是不是空闲呢?

我们已经知道,块是按8字节对齐的,也就是说块头部中存储的块的大小是8的倍数,二进制表示中最后三位都是0。而在头部中,最低一位是存储当前块的分配状况的,那么就拿第二位来存储前一个块的分配情况好了。

这样我们可以在合并时检查自己头部来得到前一块是不是被分配了,如果没有被分配,就与之合并。

2、块指针可以用4字节保存。

虽然在64位机器中,指针是64位的,不过由于本次Lab的writeup中指出

Because we are running on 64-bit machines, your allocator must be coded accordingly, with one exception: the size of the heap will never be greater than or equal to 2^32 bytes. This does not imply anything about the location of the heap, but there is a neat
optimization that can be done using this information. However, be very, very careful if you decide to take advantage of this fact.

因而我们可以只记录每个地址相对于堆起始地址的偏移量,这个偏移量是32位的,这个改进可以使得块大小最小达到16字节!

嗯……我也只想到了这么多,既然满分了就不再多想了……

下附完整代码~

里面有些宏函数的定义,显示起来可能略乱……

/*
* mm.c
*
* Name: LegendaryPaladin
* 姓名:轩辕奇侠
*
* Brief introduction:
*       This solution used an algorithm with segregated free list
*   and a binary search tree combined to maximize both space utilization
*   and throughput. Please note that each free list is a unique size class,
*   which means it only contains blocks with EXACTLY THE SAME SIZE.
*
* Details of binary search tree:
*       The BST is used to store free blocks sized larger than a specific
*   threshold (I set it to 40 bytes), of which each node is a header of a
*   free list of a same size, When searching for a free block in BST,
*   I applied the best-fit policy which selects the free block with
*   approximately minimum size among all the blocks sized larger than
*   requested.
*
* Details of segregated free list:
*       I set up a small array to store the headers of segregated free lists.
*   Each free list, as described above, has blocks with a same size. Blocks
*   which is not large enough to be stored in BST will be stored here.
*
* Block layout:
*       Allocated block:
*
*           [Header: <size><prev_alloc><1>]
*           [Payload...]
*
*       Large free block:
*
*           [Header: <size><prev_alloc><0>]
*           [4-byte pointer to next block with same size]
*           [4-byte pointer to previous block with same size]
*           [Pointer to its left child in BST]
*           [Pointer to its right child in BST]
*           [Pointer to the pointer to itself in its parent block in BST]
*           [...]
*           [Footer: <size><0>]
*
*       Small free block:
*
*           [Header: <size><prev_alloc><0>]
*           [4-byte pointer to next block with same size]
*           [4-byte pointer to previous block with same size]
*           [...]
*           [Footer: <size><0>]
*
*           Note that I omitted the footer of allocated block, instead, I
*       stored its allocation info in the header of next block.
*
*
* 概述:
*       本次作业,我通过分离空闲链表 + 二叉搜索树来实现空间利用率和效率的
*   最大化。注意我的每个链表都只存储一种大小的空闲块。
*
* 二叉搜索树:
*       二叉搜索树用于存大于某个特定阈值的块(我设为40字节),每个节点是
*   具有该大小的空闲块链表的表头。在树中查找结点的时候,我会使用“最优适配”
*   策略,选择树中大于请求大小的块中近似最小的那个。
*
* 分离空闲链表:
*       我给分离空闲链表的表头开了个小数组,每个空闲链表同样也只存一种大小的块。
*   大小不够放到树里的块会放到这里。
*
* 块布局:
*       已分配块:
*
*           [头部: <大小><上一块分配状况><1>]
*           [有效载荷...]
*
*       大的空闲块:
*
*           [头部: <大小><上一块分配状况><0>]
*           [指向下一个相同大小块的4字节指针]
*           [指向上一个相同大小块的4字节指针]
*           [指向左儿子的指针]
*           [指向右儿子的指针]
*           [指向它父亲指向它的指针的指针……]
*           [...]
*           [脚部: <大小><0>]
*
*       小的空闲块:
*
*           [头部: <大小><上一块分配状况><0>]
*           [指向下一个相同大小块的4字节指针]
*           [指向上一个相同大小块的4字节指针]
*           [...]
*           [脚部: <大小><0>]
*
*           注意我把已分配块的脚部省略了,把它的分配状态信息
*       存到了下一个块的头部。
*
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/* Other helper headers */
#include <linux/kernel.h>
#include <linux/stddef.h>

#include "mm.h"
#include "memlib.h"

/* 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 */

typedef void *block_ptr;

/* Global var and data structure pointers */
static char *heap_listp = 0;        /* Pointer to first block */
static block_ptr larger_bin_root;   /* Root of the BST which contains larger blocks */
static block_ptr *bins;             /* Array of the headers of segregated free lists */
static const unsigned int fixed_bin_count = 5;  /* Number of segregated free lists */
#ifdef DEBUG
static int operid;
#endif

/* Function prototypes */
static block_ptr coalesce(block_ptr bp);
static block_ptr extend_heap(size_t words);
static void place(block_ptr bp, size_t asize);
static void insert_free_block(block_ptr bp, size_t blocksize);
static void printblock(block_ptr bp);
static void checkblock(block_ptr bp);
static block_ptr find_fit(size_t asize);
#ifdef DEBUG
static void printtree(block_ptr node, int depth);
#endif

/* Macros and utility inline functions */

/* Single word (4) or double word (8) alignment */
#define ALIGNMENT   8

/* Round size up to ALIGNMENT */
#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~0x7)

/* Basic constants */
#define WSIZE       4           /* Word and header/footer size (bytes) */
#define DSIZE       8           /* Doubleword size (bytes) */
#define BLOCKSIZE   (1 << 6)  /* Extend heap by this amount (bytes) */

#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)              (*(unsigned int *)(p))
#define PUTTRUNC(p, val)    (GET(p) = (val))

/* Write header info at address p without modifying prev_alloc */
#define PUT(p, val)         (GET(p) = (GET(p) & 0x2) | (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)            ((char *)(bp) - WSIZE)
#define FTRP(bp)            ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)

/* Given block ptr bp, compute address of next and previous blocks */
#define NEXT_BLKP(bp)       ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp)       ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))

/* Given block ptr bp, set next block's prev_alloc */
#define SET_ALLOC(bp)       (GET(HDRP(NEXT_BLKP(bp))) |= 0x2)
#define SET_UNALLOC(bp)     (GET(HDRP(NEXT_BLKP(bp))) &= ~0x2)

/* Given block ptr bp, get prev_alloc */
#define GET_PREV_ALLOC(bp)  (GET(HDRP(bp)) & 0x2)

/* Given a freed block ptr bp, compute 'address' of next and previous blocks of same size */
#define NEXT_SAMESZ_BLKP(bp)    (*(unsigned int *)(bp))
#define PREV_SAMESZ_BLKP(bp)    (*(unsigned int *)((char *)(bp) + WSIZE))

#define WNULL 0U

/* Convert 4-byte address to 8-byte address */
static inline block_ptr word_to_ptr(unsigned int w)
{
return ((w) == WNULL ? NULL : (block_ptr)((unsigned int)(w) + 0x800000000UL));
}

/* Convert 8-byte address to 4-byte address */
static inline unsigned int ptr_to_word(block_ptr p)
{
return ((p) == NULL ? WNULL : (unsigned int)((unsigned long)(p) - 0x800000000UL));
}

/* The following are macros for BST node blocks */

/* Check if this block is large enough to be a BST node */
#define IS_OVER_BST_SIZE(size)  ((size) > DSIZE * fixed_bin_count)
#define IS_BST_NODE(bp)         (IS_OVER_BST_SIZE(GET_SIZE(HDRP(bp))))

/* Given BST block ptr bp, compute address of its left child or right child */
#define LCHLD_BLKPREF(bp)       ((block_ptr *)((char *)(bp) + DSIZE))
#define RCHLD_BLKPREF(bp)       ((block_ptr *)((char *)(bp) + DSIZE * 2))

/* Crap-like triple pointer > < */
#define PARENT_CHLDSLOTPREF(bp) ((block_ptr **)((char *)(bp) + DSIZE * 3))

#define LCHLD_BLKP(bp) (*LCHLD_BLKPREF(bp))
#define RCHLD_BLKP(bp) (*RCHLD_BLKPREF(bp))
#define PARENT_CHLDSLOTP(bp) (*PARENT_CHLDSLOTPREF(bp))

/* Reset the fields of a free block bp */
#define reset_block(bp)                                                     \
{                                                                           \
NEXT_SAMESZ_BLKP(bp) = WNULL;                                           \
PREV_SAMESZ_BLKP(bp) = WNULL;                                           \
if (IS_BST_NODE(bp))                                                    \
{                                                                       \
LCHLD_BLKP(bp) = NULL;                                              \
RCHLD_BLKP(bp) = NULL;                                              \
PARENT_CHLDSLOTP(bp) = NULL;                                        \
}                                                                       \
}

/* Remove bp from its free list */
#define remove_linked_freed_block(bp)                                               \
{                                                                                   \
if (PREV_SAMESZ_BLKP(bp))                                                       \
NEXT_SAMESZ_BLKP(word_to_ptr(PREV_SAMESZ_BLKP(bp))) = NEXT_SAMESZ_BLKP(bp); \
if (NEXT_SAMESZ_BLKP(bp))                                                       \
PREV_SAMESZ_BLKP(word_to_ptr(NEXT_SAMESZ_BLKP(bp))) = PREV_SAMESZ_BLKP(bp); \
}

static inline void remove_freed_block(block_ptr bp)
{
if (IS_BST_NODE(bp) && PARENT_CHLDSLOTP(bp))
{
/* I hate deleting node. */
block_ptr l = LCHLD_BLKP(bp), r = RCHLD_BLKP(bp), cur;
if ((cur = word_to_ptr(NEXT_SAMESZ_BLKP(bp))))
{
PARENT_CHLDSLOTP(cur) = PARENT_CHLDSLOTP(bp);
*PARENT_CHLDSLOTP(bp) = cur;
LCHLD_BLKP(cur) = l;
if (l)
PARENT_CHLDSLOTP(l) = LCHLD_BLKPREF(cur);
RCHLD_BLKP(cur) = r;
if (r)
PARENT_CHLDSLOTP(r) = RCHLD_BLKPREF(cur);
}
else if (l && r)
{
/* Find left-most node in right branch to replace curr */
if (!(cur = LCHLD_BLKP(r)))
{
/* Right child doesn't have lchild */
LCHLD_BLKP(r) = l;
PARENT_CHLDSLOTP(r) = PARENT_CHLDSLOTP(bp);
*PARENT_CHLDSLOTP(bp) = r;
PARENT_CHLDSLOTP(l) = LCHLD_BLKPREF(r);
}
else
{
while (LCHLD_BLKP(cur))
cur = LCHLD_BLKP(cur);
*PARENT_CHLDSLOTP(bp) = cur;
*PARENT_CHLDSLOTP(cur) = RCHLD_BLKP(cur);
if (RCHLD_BLKP(cur))
PARENT_CHLDSLOTP(RCHLD_BLKP(cur)) = PARENT_CHLDSLOTP(cur);
PARENT_CHLDSLOTP(cur) = PARENT_CHLDSLOTP(bp);
LCHLD_BLKP(cur) = l;
RCHLD_BLKP(cur) = r;
PARENT_CHLDSLOTP(l) = LCHLD_BLKPREF(cur);
PARENT_CHLDSLOTP(r) = RCHLD_BLKPREF(cur);
}
}
else if (r)
{
*PARENT_CHLDSLOTP(bp) = r;
PARENT_CHLDSLOTP(r) = PARENT_CHLDSLOTP(bp);
}
else if (l)
{
*PARENT_CHLDSLOTP(bp) = l;
PARENT_CHLDSLOTP(l) = PARENT_CHLDSLOTP(bp);
}
else
*PARENT_CHLDSLOTP(bp) = NULL;
}
else if (!PREV_SAMESZ_BLKP(bp))
/* Remove a free list header from the array of headers */
bins[GET_SIZE(HDRP(bp)) / DSIZE - 1] = word_to_ptr(NEXT_SAMESZ_BLKP(bp));
remove_linked_freed_block(bp);
}

/* Function definitions */

/*
* bestfit_search - search for a block with requested size or larger in BST.
*/
block_ptr *bestfit_search(block_ptr *node, size_t size, int specific)
{
block_ptr *candidate, curr = *node;
size_t curr_size;

if (curr == NULL)
return NULL;

curr_size = GET_SIZE(HDRP(curr));

if (size < curr_size)
{
if (specific)
return bestfit_search(LCHLD_BLKPREF(curr), size, specific);
if ((candidate = bestfit_search(LCHLD_BLKPREF(curr), size, specific)))
return candidate;
return node;
}
else if (size > curr_size)
return bestfit_search(RCHLD_BLKPREF(curr), size, specific);
else
return node;
}

/*
* Return whether the pointer is in the heap.
* May be useful for debugging.
*/
static inline int in_heap(const block_ptr p)
{
return p <= mem_heap_hi() && p >= mem_heap_lo();
}

/*
* Return whether the pointer is aligned.
* May be useful for debugging.
*/
static inline int aligned(const block_ptr p)
{
return ((unsigned long)p % ALIGNMENT) == 0;
}

/*
* Initialize: return -1 on error, 0 on success.
*/
int mm_init(void)
{
/* Create the initial empty heap */
if ((bins = mem_sbrk(
ALIGN(fixed_bin_count * sizeof(block_ptr)) +
4 * WSIZE)) == (block_ptr)-1)
return -1;
memset(bins, 0, fixed_bin_count * sizeof(block_ptr));
heap_listp = (char *)ALIGN((unsigned long)(bins + fixed_bin_count));
larger_bin_root = NULL;
PUTTRUNC(heap_listp, 0);                            /* Alignment padding */
PUTTRUNC(heap_listp + (1 * WSIZE), PACK(DSIZE, 1)); /* Prologue header */
PUTTRUNC(heap_listp + (2 * WSIZE), PACK(DSIZE, 1)); /* Prologue footer */
PUTTRUNC(heap_listp + (3 * WSIZE), PACK(0, 1));     /* Epilogue header */
heap_listp += (2 * WSIZE);
SET_ALLOC(heap_listp);
#ifdef DEBUG
{
printblock(heap_listp);
checkblock(heap_listp);
}
operid = 0;
#endif
return 0;
}

/*
* malloc
*/
block_ptr malloc(size_t size)
{
size_t asize;      /* Adjusted block size */
size_t extendsize; /* Amount to extend heap if no fit */
char *bp;

if (heap_listp == 0)
{
mm_init();
}

/* Ignore spurious requests */
if (size == 0)
return NULL;

/* Adjust block size to include overhead and alignment reqs. */
if (size <= DSIZE)
asize = 2 * DSIZE;
else
asize = DSIZE * ((size + (WSIZE) + (DSIZE - 1)) / DSIZE);

#ifdef DEBUG
printf("\nMalloc request: size = %zu, rounded to %zu \033[41;37m[ID:%d]\033[0m\n", size, asize, operid++);
#endif

/* Search the free list for a fit */
if ((bp = find_fit(asize)) != NULL)
{
#ifdef DEBUG
{
puts("Found fit!");
checkblock(bp);
printblock(bp);
}
#endif
place(bp, asize);
return bp;
}

/* No fit found. Get more memory and place the block */
extendsize = MAX(asize, BLOCKSIZE);
if ((bp = extend_heap(extendsize / WSIZE)) == NULL)
return NULL;
place(bp, asize);
return bp;
}

/*
* free
*/
void free(block_ptr ptr)
{
block_ptr tmp;
size_t size;
if (!ptr || !in_heap(ptr) || !aligned(ptr))
return;

#ifdef DEBUG
{
printf("\nFree request: ptr = %p \033[41;37m[ID:%d]\033[0m\n", ptr, operid++);
printblock(ptr);
}
#endif

size = GET_SIZE(HDRP(ptr));
PUT(HDRP(ptr), PACK(size, 0));
PUT(FTRP(ptr), PACK(size, 0));
SET_UNALLOC(ptr);
reset_block(ptr);
tmp = coalesce(ptr);
insert_free_block(tmp, GET_SIZE(HDRP(tmp)));
}

/*
* realloc - I don't want to look at mm-naive.c
*/
block_ptr realloc(block_ptr oldptr, size_t size)
{
size_t oldsize;
block_ptr newptr;

/* If size == 0 then this is just free, and we return NULL. */
if (size == 0)
{
free(oldptr);
return 0;
}

/* If oldptr is NULL, then this is just malloc. */
if (oldptr == NULL)
{
return malloc(size);
}

newptr = malloc(size);

/* If realloc() fails the original block is left untouched  */
if (!newptr)
{
return 0;
}

/* Copy the old data. */
oldsize = GET_SIZE(HDRP(oldptr));
if (size < oldsize)
oldsize = size;
memcpy(newptr, oldptr, oldsize);

/* Free the old block. */
free(oldptr);

return newptr;
}

/*
* calloc - I don't want to look at mm-naive.c
* This function is not tested by mdriver, but it is
* needed to run the traces.
*/
block_ptr calloc(size_t nmemb, size_t size)
{
size_t bytes = nmemb * size;
block_ptr newptr;

newptr = malloc(bytes);
memset(newptr, 0, bytes);

return newptr;
}

/*
* coalesce - Boundary tag coalescing. Return ptr to coalesced block
*/
static block_ptr coalesce(block_ptr bp)
{
/*
* TODO Here is the bug: Do update the bins while doing this.
* Tried to fix. Not sure what will happen.
*/
block_ptr prev, next = NEXT_BLKP(bp);

/* Use GET_PREV_ALLOC to judge if prev block is allocated */
size_t prev_alloc = GET_PREV_ALLOC(bp);
size_t next_alloc = GET_ALLOC(HDRP(next));
size_t size = GET_SIZE(HDRP(bp));

if (prev_alloc && next_alloc)            /* Case 1 */
{
return bp;
}

else if (prev_alloc && !next_alloc)      /* Case 2 */
{
remove_freed_block(next);
size += GET_SIZE(HDRP(next));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
}

else if (!prev_alloc && next_alloc)       /* Case 3 */
{
prev = PREV_BLKP(bp);
remove_freed_block(prev);
size += GET_SIZE(HDRP(prev));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(prev), PACK(size, 0));
bp = prev;
}

else                                      /* Case 4 */
{
prev = PREV_BLKP(bp);
remove_freed_block(next);
remove_freed_block(prev);
size += GET_SIZE(HDRP(prev)) + GET_SIZE(FTRP(next));
PUT(HDRP(prev), PACK(size, 0));
PUT(FTRP(next), PACK(size, 0));
bp = prev;
}
reset_block(bp);
// insert_free_block(bp, size);
return bp;
}

/*
* extend_heap - Extend heap with free block and return its block pointer
*/
static block_ptr extend_heap(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 ((long)(bp = mem_sbrk(size)) == -1)
return NULL;

#ifdef DEBUG
printf("\nExtended the heap by %zu words.\n", words);
#endif

/* 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 */
reset_block(bp);
PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1));   /* New epilogue header */

/* Coalesce if the previous block was free */
return coalesce(bp);
}

/*
* place - Place block of asize bytes at start of free block bp
*       and split if remainder would be at least minimum block size
*/
static void place(block_ptr bp, size_t asize)
{
size_t csize = GET_SIZE(HDRP(bp)), delta = csize - asize;

if (delta >= (2 * DSIZE))
{
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1));
SET_ALLOC(bp);
bp = NEXT_BLKP(bp);
PUT(HDRP(bp), PACK(delta, 0));
PUT(FTRP(bp), PACK(delta, 0));
SET_UNALLOC(bp);
reset_block(bp);
insert_free_block(bp, delta);
#ifdef DEBUG
{
printf("Block with size %zu remains a block:\n", asize);
printblock(bp);
}
#endif
}
else
{
PUT(HDRP(bp), PACK(csize, 1));
PUT(FTRP(bp), PACK(csize, 1));
SET_ALLOC(bp);
}
}

/*
* insert_free_block - insert a block into BST or segregated free list
* BLOCKSIZE should be duplicate of double word
*/
static void insert_free_block(block_ptr bp, size_t blocksize)
{
block_ptr *new = &larger_bin_root, parent = NULL;

if (!IS_OVER_BST_SIZE(blocksize))
{
/* Insert into segregated free list */
size_t dcount = blocksize / DSIZE;
if (bins[dcount - 1])
{
/* Connect it before the existing block as a new header */
NEXT_SAMESZ_BLKP(bp) = ptr_to_word(bins[dcount - 1]);
PREV_SAMESZ_BLKP(bins[dcount - 1]) = ptr_to_word(bp);
}
PREV_SAMESZ_BLKP(bp) = WNULL;
bins[dcount - 1] = bp;
return;
}

/* Figure out where to put the new node in BST */
while (*new)
{
size_t curr_size = GET_SIZE(HDRP(parent = *new));

if (blocksize < curr_size)
new = LCHLD_BLKPREF(parent);
else if (blocksize > curr_size)
new = RCHLD_BLKPREF(parent);
else
{
/* MWHAHAHAHAHA */
block_ptr next = word_to_ptr(NEXT_SAMESZ_BLKP(bp) = NEXT_SAMESZ_BLKP(parent));
if (next)
/* Connect it before the existing block as a new header */
PREV_SAMESZ_BLKP(next) = ptr_to_word(bp);
NEXT_SAMESZ_BLKP(parent) = ptr_to_word(bp);
PREV_SAMESZ_BLKP(bp) = ptr_to_word(parent);
return;
}
}

/* Connect this node as a child */
*new = bp;
PARENT_CHLDSLOTP(bp) = new;
#ifdef DEBUG
{
printf("Inserting a block: ");
printblock(bp);
}
#endif
}

/*
* find_fit - Find a fit for a block with asize bytes
* asize should be duplicate of double word
*/
static block_ptr find_fit(size_t asize)
{
block_ptr curr, *blocks;
size_t dcount = asize / DSIZE;

if (!IS_OVER_BST_SIZE(asize))
{
if (bins[dcount - 1])
{
/* Found a free list of this size! */
curr = bins[dcount - 1];
bins[dcount - 1] = word_to_ptr(NEXT_SAMESZ_BLKP(curr));
remove_freed_block(curr);
return curr;
}
/* ...not found? Proceed to BST */
}

if ((blocks = bestfit_search(&larger_bin_root, asize, 0)) == NULL)
/* No best-fit found...T T */
return NULL;

curr = *blocks;

/* Found a best-fit block! LOL */
#ifdef DEBUG
if ((*blocks = word_to_ptr(NEXT_SAMESZ_BLKP(curr))) == NULL)
{
printf("** All blocks with size %u (request: %zu) deleted.\n", GET_SIZE(HDRP(curr)), asize);
}
#else
/* Set the node to the next same size block if it has */
*blocks = word_to_ptr(NEXT_SAMESZ_BLKP(curr));
#endif
remove_freed_block(curr);
return curr;
}

/*
* printblock - print a block for debugging
*/
static inline void printblock(block_ptr bp)
{
size_t hsize, halloc, fsize, falloc;

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;
}
if (halloc)
printf("\033[43;37m%p: header: [%zu:%c:%c] footer: -\033[0m\n", bp,
hsize, (GET_PREV_ALLOC(bp) ? 'a' : 'f'), (halloc ? 'a' : 'f'));
else
{
printf("\033[42;30m%p: header: [%zu:%c:%c] footer: [%zu:%c]\033[0m", bp,
hsize, (GET_PREV_ALLOC(bp) ? 'a' : 'f'), (halloc ? 'a' : 'f'),
fsize, (falloc ? 'a' : 'f'));
if (IS_BST_NODE(bp))
printf("\033[1;44;33m[BST Node| parent slotp: %p, l: %p, r: %p]\033[0m",
PARENT_CHLDSLOTP(bp), LCHLD_BLKP(bp), RCHLD_BLKP(bp));
if (PREV_SAMESZ_BLKP(bp))
printf("\033[1;33m[PREV] %p\033[0m", word_to_ptr(PREV_SAMESZ_BLKP(bp)));
putchar('\n');
}
}

/*
* checkblock - as the name goes
*/
static inline void checkblock(block_ptr bp)
{
if (!aligned(bp))
printf("\n\033[1;47;31m## Error: %p is not doubleword aligned\033[0m\n", bp);
if (!GET_ALLOC(HDRP(bp)) && (GET(HDRP(bp)) & ~0x2) != (GET(FTRP(bp)) & ~0x2))
printf("\n\033[1;47;31m## Error: header does not match footer, header: %u, footer: %u \033[0m\n",
GET(HDRP(bp)), GET(FTRP(bp)));
if (GET_ALLOC(HDRP(bp)) != (GET_PREV_ALLOC(NEXT_BLKP(bp)) >> 1))
printf("\n\033[1;47;31m## Error: %p allocation does not match next block's prev_alloc\033[0m\n", bp);
}

static void printchain(block_ptr node)
{
while (node)
{
printblock(node);
printf("->");
node = word_to_ptr(NEXT_SAMESZ_BLKP(node));
}
}

static void printtree(block_ptr node, int depth)
{
int i;
if (node == NULL)
return;
printf("BST: ");
for (i = 0; i < depth; i++)
putchar('-');
printchain(node);
putchar('\n');
printtree(LCHLD_BLKP(node), depth + 1);
printtree(RCHLD_BLKP(node), depth + 1);
}

static void checklist(block_ptr node)
{
if (node == NULL)
return;
if (PREV_SAMESZ_BLKP(node) &&
word_to_ptr(NEXT_SAMESZ_BLKP(word_to_ptr(PREV_SAMESZ_BLKP(node)))) != node)
printf("\n\033[1;47;31m## Bad neighbor pointer: %p\033[0m\n", node);
checklist(word_to_ptr(NEXT_SAMESZ_BLKP(node)));
}

static void checktree(block_ptr node)
{
if (node == NULL)
return;
if (*PARENT_CHLDSLOTP(node) != node)
printf("\n\033[1;47;31m## Bad parent pointer: %p\033[0m\n", node);
checklist(node);
checktree(LCHLD_BLKP(node));
checktree(RCHLD_BLKP(node));
}

/*
* checkheap - check the heap for consistency
*/
void mm_checkheap(int verbose)
{
char *bp = heap_listp;

if (verbose)
printf("Heap (%p):\n", heap_listp);

if ((GET_SIZE(HDRP(heap_listp)) != DSIZE) || !GET_ALLOC(HDRP(heap_listp)))
printf("\n\033[1;47;31m## Bad prologue header\033[0m\n");
checkblock(heap_listp);

for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp))
{
if (verbose)
printblock(bp);
checkblock(bp);
}

if (verbose)
{
printblock(bp);
{
unsigned int i;
for (i = 1; i <= fixed_bin_count; i++)
if (bins[i - 1])
{
printf("BIN #%d: size = %d ", i, i * DSIZE);
checklist(bins[i - 1]);
printchain(bins[i - 1]);
}
putchar('\n');
checktree(larger_bin_root);
printtree(larger_bin_root, 0);
}
}
if ((GET_SIZE(HDRP(bp)) != 0) || !(GET_ALLOC(HDRP(bp))))
printf("\n\033[1;47;31m## Bad epilogue header\033[0m\n");
}


/*
* mm.c
*
* Name: LegendaryPaladin
* 姓名:轩辕奇侠
*
* Brief introduction:
*       This solution used an algorithm with segregated free list
*   and a binary search tree combined to maximize both space utilization
*   and throughput. Please note that each free list is a unique size class,
*   which means it only contains blocks with EXACTLY THE SAME SIZE.
*
* Details of binary search tree:
*       The BST is used to store free blocks sized larger than a specific
*   threshold (I set it to 40 bytes), of which each node is a header of a
*   free list of a same size, When searching for a free block in BST,
*   I applied the best-fit policy which selects the free block with
*   approximately minimum size among all the blocks sized larger than
*   requested.
*
* Details of segregated free list:
*       I set up a small array to store the headers of segregated free lists.
*   Each free list, as described above, has blocks with a same size. Blocks
*   which is not large enough to be stored in BST will be stored here.
*
* Block layout:
*       Allocated block:
*
*           [Header: <size><prev_alloc><1>]
*           [Payload...]
*
*       Large free block:
*
*           [Header: <size><prev_alloc><0>]
*           [4-byte pointer to next block with same size]
*           [4-byte pointer to previous block with same size]
*           [Pointer to its left child in BST]
*           [Pointer to its right child in BST]
*           [Pointer to the pointer to itself in its parent block in BST]
*           [...]
*           [Footer: <size><0>]
*
*       Small free block:
*
*           [Header: <size><prev_alloc><0>]
*           [4-byte pointer to next block with same size]
*           [4-byte pointer to previous block with same size]
*           [...]
*           [Footer: <size><0>]
*
*           Note that I omitted the footer of allocated block, instead, I
*       stored its allocation info in the header of next block.
*
*
* 概述:
*       本次作业,我通过分离空闲链表 + 二叉搜索树来实现空间利用率和效率的
*   最大化。注意我的每个链表都只存储一种大小的空闲块。
*
* 二叉搜索树:
*       二叉搜索树用于存大于某个特定阈值的块(我设为40字节),每个节点是
*   具有该大小的空闲块链表的表头。在树中查找结点的时候,我会使用“最优适配”
*   策略,选择树中大于请求大小的块中近似最小的那个。
*
* 分离空闲链表:
*       我给分离空闲链表的表头开了个小数组,每个空闲链表同样也只存一种大小的块。
*   大小不够放到树里的块会放到这里。
*
* 块布局:
*       已分配块:
*
*           [头部: <大小><上一块分配状况><1>]
*           [有效载荷...]
*
*       大的空闲块:
*
*           [头部: <大小><上一块分配状况><0>]
*           [指向下一个相同大小块的4字节指针]
*           [指向上一个相同大小块的4字节指针]
*           [指向左儿子的指针]
*           [指向右儿子的指针]
*           [指向它父亲指向它的指针的指针……]
*           [...]
*           [脚部: <大小><0>]
*
*       小的空闲块:
*
*           [头部: <大小><上一块分配状况><0>]
*           [指向下一个相同大小块的4字节指针]
*           [指向上一个相同大小块的4字节指针]
*           [...]
*           [脚部: <大小><0>]
*
*           注意我把已分配块的脚部省略了,把它的分配状态信息
*       存到了下一个块的头部。
*
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/* Other helper headers */
#include <linux/kernel.h>
#include <linux/stddef.h>

#include "mm.h"
#include "memlib.h"

/* 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 */

typedef void *block_ptr;

/* Global var and data structure pointers */
static char *heap_listp = 0;        /* Pointer to first block */
static block_ptr larger_bin_root;   /* Root of the BST which contains larger blocks */
static block_ptr *bins;             /* Array of the headers of segregated free lists */
static const unsigned int fixed_bin_count = 5;  /* Number of segregated free lists */
#ifdef DEBUG
static int operid;
#endif

/* Function prototypes */
static block_ptr coalesce(block_ptr bp);
static block_ptr extend_heap(size_t words);
static void place(block_ptr bp, size_t asize);
static void insert_free_block(block_ptr bp, size_t blocksize);
static void printblock(block_ptr bp);
static void checkblock(block_ptr bp);
static block_ptr find_fit(size_t asize);
#ifdef DEBUG
static void printtree(block_ptr node, int depth);
#endif

/* Macros and utility inline functions */

/* Single word (4) or double word (8) alignment */
#define ALIGNMENT   8

/* Round size up to ALIGNMENT */
#define ALIGN(size) (((size) + (ALIGNMENT - 1)) & ~0x7)

/* Basic constants */
#define WSIZE       4           /* Word and header/footer size (bytes) */
#define DSIZE       8           /* Doubleword size (bytes) */
#define BLOCKSIZE   (1 << 6)  /* Extend heap by this amount (bytes) */

#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)              (*(unsigned int *)(p))
#define PUTTRUNC(p, val)    (GET(p) = (val))

/* Write header info at address p without modifying prev_alloc */
#define PUT(p, val)         (GET(p) = (GET(p) & 0x2) | (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)            ((char *)(bp) - WSIZE)
#define FTRP(bp)            ((char *)(bp) + GET_SIZE(HDRP(bp)) - DSIZE)

/* Given block ptr bp, compute address of next and previous blocks */
#define NEXT_BLKP(bp)       ((char *)(bp) + GET_SIZE(((char *)(bp) - WSIZE)))
#define PREV_BLKP(bp)       ((char *)(bp) - GET_SIZE(((char *)(bp) - DSIZE)))

/* Given block ptr bp, set next block's prev_alloc */
#define SET_ALLOC(bp)       (GET(HDRP(NEXT_BLKP(bp))) |= 0x2)
#define SET_UNALLOC(bp)     (GET(HDRP(NEXT_BLKP(bp))) &= ~0x2)

/* Given block ptr bp, get prev_alloc */
#define GET_PREV_ALLOC(bp)  (GET(HDRP(bp)) & 0x2)

/* Given a freed block ptr bp, compute 'address' of next and previous blocks of same size */
#define NEXT_SAMESZ_BLKP(bp)    (*(unsigned int *)(bp))
#define PREV_SAMESZ_BLKP(bp)    (*(unsigned int *)((char *)(bp) + WSIZE))

#define WNULL 0U

/* Convert 4-byte address to 8-byte address */
static inline block_ptr word_to_ptr(unsigned int w)
{
return ((w) == WNULL ? NULL : (block_ptr)((unsigned int)(w) + 0x800000000UL));
}

/* Convert 8-byte address to 4-byte address */
static inline unsigned int ptr_to_word(block_ptr p)
{
return ((p) == NULL ? WNULL : (unsigned int)((unsigned long)(p) - 0x800000000UL));
}

/* The following are macros for BST node blocks */

/* Check if this block is large enough to be a BST node */
#define IS_OVER_BST_SIZE(size)  ((size) > DSIZE * fixed_bin_count)
#define IS_BST_NODE(bp)         (IS_OVER_BST_SIZE(GET_SIZE(HDRP(bp))))

/* Given BST block ptr bp, compute address of its left child or right child */
#define LCHLD_BLKPREF(bp)       ((block_ptr *)((char *)(bp) + DSIZE))
#define RCHLD_BLKPREF(bp)       ((block_ptr *)((char *)(bp) + DSIZE * 2))

/* Crap-like triple pointer > < */
#define PARENT_CHLDSLOTPREF(bp) ((block_ptr **)((char *)(bp) + DSIZE * 3))

#define LCHLD_BLKP(bp) (*LCHLD_BLKPREF(bp))
#define RCHLD_BLKP(bp) (*RCHLD_BLKPREF(bp))
#define PARENT_CHLDSLOTP(bp) (*PARENT_CHLDSLOTPREF(bp))

/* Reset the fields of a free block bp */
#define reset_block(bp)                                                     \
{                                                                           \
NEXT_SAMESZ_BLKP(bp) = WNULL;                                           \
PREV_SAMESZ_BLKP(bp) = WNULL;                                           \
if (IS_BST_NODE(bp))                                                    \
{                                                                       \
LCHLD_BLKP(bp) = NULL;                                              \
RCHLD_BLKP(bp) = NULL;                                              \
PARENT_CHLDSLOTP(bp) = NULL;                                        \
}                                                                       \
}

/* Remove bp from its free list */
#define remove_linked_freed_block(bp)                                               \
{                                                                                   \
if (PREV_SAMESZ_BLKP(bp))                                                       \
NEXT_SAMESZ_BLKP(word_to_ptr(PREV_SAMESZ_BLKP(bp))) = NEXT_SAMESZ_BLKP(bp); \
if (NEXT_SAMESZ_BLKP(bp))                                                       \
PREV_SAMESZ_BLKP(word_to_ptr(NEXT_SAMESZ_BLKP(bp))) = PREV_SAMESZ_BLKP(bp); \
}

static inline void remove_freed_block(block_ptr bp)
{
if (IS_BST_NODE(bp) && PARENT_CHLDSLOTP(bp))
{
/* I hate deleting node. */
block_ptr l = LCHLD_BLKP(bp), r = RCHLD_BLKP(bp), cur;
if ((cur = word_to_ptr(NEXT_SAMESZ_BLKP(bp))))
{
PARENT_CHLDSLOTP(cur) = PARENT_CHLDSLOTP(bp);
*PARENT_CHLDSLOTP(bp) = cur;
LCHLD_BLKP(cur) = l;
if (l)
PARENT_CHLDSLOTP(l) = LCHLD_BLKPREF(cur);
RCHLD_BLKP(cur) = r;
if (r)
PARENT_CHLDSLOTP(r) = RCHLD_BLKPREF(cur);
}
else if (l && r)
{
/* Find left-most node in right branch to replace curr */
if (!(cur = LCHLD_BLKP(r)))
{
/* Right child doesn't have lchild */
LCHLD_BLKP(r) = l;
PARENT_CHLDSLOTP(r) = PARENT_CHLDSLOTP(bp);
*PARENT_CHLDSLOTP(bp) = r;
PARENT_CHLDSLOTP(l) = LCHLD_BLKPREF(r);
}
else
{
while (LCHLD_BLKP(cur))
cur = LCHLD_BLKP(cur);
*PARENT_CHLDSLOTP(bp) = cur;
*PARENT_CHLDSLOTP(cur) = RCHLD_BLKP(cur);
if (RCHLD_BLKP(cur))
PARENT_CHLDSLOTP(RCHLD_BLKP(cur)) = PARENT_CHLDSLOTP(cur);
PARENT_CHLDSLOTP(cur) = PARENT_CHLDSLOTP(bp);
LCHLD_BLKP(cur) = l;
RCHLD_BLKP(cur) = r;
PARENT_CHLDSLOTP(l) = LCHLD_BLKPREF(cur);
PARENT_CHLDSLOTP(r) = RCHLD_BLKPREF(cur);
}
}
else if (r)
{
*PARENT_CHLDSLOTP(bp) = r;
PARENT_CHLDSLOTP(r) = PARENT_CHLDSLOTP(bp);
}
else if (l)
{
*PARENT_CHLDSLOTP(bp) = l;
PARENT_CHLDSLOTP(l) = PARENT_CHLDSLOTP(bp);
}
else
*PARENT_CHLDSLOTP(bp) = NULL;
}
else if (!PREV_SAMESZ_BLKP(bp))
/* Remove a free list header from the array of headers */
bins[GET_SIZE(HDRP(bp)) / DSIZE - 1] = word_to_ptr(NEXT_SAMESZ_BLKP(bp));
remove_linked_freed_block(bp);
}

/* Function definitions */

/*
* bestfit_search - search for a block with requested size or larger in BST.
*/
block_ptr *bestfit_search(block_ptr *node, size_t size, int specific)
{
block_ptr *candidate, curr = *node;
size_t curr_size;

if (curr == NULL)
return NULL;

curr_size = GET_SIZE(HDRP(curr));

if (size < curr_size)
{
if (specific)
return bestfit_search(LCHLD_BLKPREF(curr), size, specific);
if ((candidate = bestfit_search(LCHLD_BLKPREF(curr), size, specific)))
return candidate;
return node;
}
else if (size > curr_size)
return bestfit_search(RCHLD_BLKPREF(curr), size, specific);
else
return node;
}

/*
* Return whether the pointer is in the heap.
* May be useful for debugging.
*/
static inline int in_heap(const block_ptr p)
{
return p <= mem_heap_hi() && p >= mem_heap_lo();
}

/*
* Return whether the pointer is aligned.
* May be useful for debugging.
*/
static inline int aligned(const block_ptr p)
{
return ((unsigned long)p % ALIGNMENT) == 0;
}

/*
* Initialize: return -1 on error, 0 on success.
*/
int mm_init(void)
{
/* Create the initial empty heap */
if ((bins = mem_sbrk(
ALIGN(fixed_bin_count * sizeof(block_ptr)) +
4 * WSIZE)) == (block_ptr)-1)
return -1;
memset(bins, 0, fixed_bin_count * sizeof(block_ptr));
heap_listp = (char *)ALIGN((unsigned long)(bins + fixed_bin_count));
larger_bin_root = NULL;
PUTTRUNC(heap_listp, 0);                            /* Alignment padding */
PUTTRUNC(heap_listp + (1 * WSIZE), PACK(DSIZE, 1)); /* Prologue header */
PUTTRUNC(heap_listp + (2 * WSIZE), PACK(DSIZE, 1)); /* Prologue footer */
PUTTRUNC(heap_listp + (3 * WSIZE), PACK(0, 1));     /* Epilogue header */
heap_listp += (2 * WSIZE);
SET_ALLOC(heap_listp);
#ifdef DEBUG
{
printblock(heap_listp);
checkblock(heap_listp);
}
operid = 0;
#endif
return 0;
}

/*
* malloc
*/
block_ptr malloc(size_t size)
{
size_t asize;      /* Adjusted block size */
size_t extendsize; /* Amount to extend heap if no fit */
char *bp;

if (heap_listp == 0)
{
mm_init();
}

/* Ignore spurious requests */
if (size == 0)
return NULL;

/* Adjust block size to include overhead and alignment reqs. */
if (size <= DSIZE)
asize = 2 * DSIZE;
else
asize = DSIZE * ((size + (WSIZE) + (DSIZE - 1)) / DSIZE);

#ifdef DEBUG
printf("\nMalloc request: size = %zu, rounded to %zu \033[41;37m[ID:%d]\033[0m\n", size, asize, operid++);
#endif

/* Search the free list for a fit */
if ((bp = find_fit(asize)) != NULL)
{
#ifdef DEBUG
{
puts("Found fit!");
checkblock(bp);
printblock(bp);
}
#endif
place(bp, asize);
return bp;
}

/* No fit found. Get more memory and place the block */
extendsize = MAX(asize, BLOCKSIZE);
if ((bp = extend_heap(extendsize / WSIZE)) == NULL)
return NULL;
place(bp, asize);
return bp;
}

/*
* free
*/
void free(block_ptr ptr)
{
block_ptr tmp;
size_t size;
if (!ptr || !in_heap(ptr) || !aligned(ptr))
return;

#ifdef DEBUG
{
printf("\nFree request: ptr = %p \033[41;37m[ID:%d]\033[0m\n", ptr, operid++);
printblock(ptr);
}
#endif

size = GET_SIZE(HDRP(ptr));
PUT(HDRP(ptr), PACK(size, 0));
PUT(FTRP(ptr), PACK(size, 0));
SET_UNALLOC(ptr);
reset_block(ptr);
tmp = coalesce(ptr);
insert_free_block(tmp, GET_SIZE(HDRP(tmp)));
}

/*
* realloc - I don't want to look at mm-naive.c
*/
block_ptr realloc(block_ptr oldptr, size_t size)
{
size_t oldsize;
block_ptr newptr;

/* If size == 0 then this is just free, and we return NULL. */
if (size == 0)
{
free(oldptr);
return 0;
}

/* If oldptr is NULL, then this is just malloc. */
if (oldptr == NULL)
{
return malloc(size);
}

newptr = malloc(size);

/* If realloc() fails the original block is left untouched  */
if (!newptr)
{
return 0;
}

/* Copy the old data. */
oldsize = GET_SIZE(HDRP(oldptr));
if (size < oldsize)
oldsize = size;
memcpy(newptr, oldptr, oldsize);

/* Free the old block. */
free(oldptr);

return newptr;
}

/*
* calloc - I don't want to look at mm-naive.c
* This function is not tested by mdriver, but it is
* needed to run the traces.
*/
block_ptr calloc(size_t nmemb, size_t size)
{
size_t bytes = nmemb * size;
block_ptr newptr;

newptr = malloc(bytes);
memset(newptr, 0, bytes);

return newptr;
}

/*
* coalesce - Boundary tag coalescing. Return ptr to coalesced block
*/
static block_ptr coalesce(block_ptr bp)
{
/*
* TODO Here is the bug: Do update the bins while doing this.
* Tried to fix. Not sure what will happen.
*/
block_ptr prev, next = NEXT_BLKP(bp);

/* Use GET_PREV_ALLOC to judge if prev block is allocated */
size_t prev_alloc = GET_PREV_ALLOC(bp);
size_t next_alloc = GET_ALLOC(HDRP(next));
size_t size = GET_SIZE(HDRP(bp));

if (prev_alloc && next_alloc)            /* Case 1 */
{
return bp;
}

else if (prev_alloc && !next_alloc)      /* Case 2 */
{
remove_freed_block(next);
size += GET_SIZE(HDRP(next));
PUT(HDRP(bp), PACK(size, 0));
PUT(FTRP(bp), PACK(size, 0));
}

else if (!prev_alloc && next_alloc)       /* Case 3 */
{
prev = PREV_BLKP(bp);
remove_freed_block(prev);
size += GET_SIZE(HDRP(prev));
PUT(FTRP(bp), PACK(size, 0));
PUT(HDRP(prev), PACK(size, 0));
bp = prev;
}

else                                      /* Case 4 */
{
prev = PREV_BLKP(bp);
remove_freed_block(next);
remove_freed_block(prev);
size += GET_SIZE(HDRP(prev)) + GET_SIZE(FTRP(next));
PUT(HDRP(prev), PACK(size, 0));
PUT(FTRP(next), PACK(size, 0));
bp = prev;
}
reset_block(bp);
// insert_free_block(bp, size);
return bp;
}

/*
* extend_heap - Extend heap with free block and return its block pointer
*/
static block_ptr extend_heap(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 ((long)(bp = mem_sbrk(size)) == -1)
return NULL;

#ifdef DEBUG
printf("\nExtended the heap by %zu words.\n", words);
#endif

/* 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 */
reset_block(bp);
PUT(HDRP(NEXT_BLKP(bp)), PACK(0, 1));   /* New epilogue header */

/* Coalesce if the previous block was free */
return coalesce(bp);
}

/*
* place - Place block of asize bytes at start of free block bp
*       and split if remainder would be at least minimum block size
*/
static void place(block_ptr bp, size_t asize)
{
size_t csize = GET_SIZE(HDRP(bp)), delta = csize - asize;

if (delta >= (2 * DSIZE))
{
PUT(HDRP(bp), PACK(asize, 1));
PUT(FTRP(bp), PACK(asize, 1));
SET_ALLOC(bp);
bp = NEXT_BLKP(bp);
PUT(HDRP(bp), PACK(delta, 0));
PUT(FTRP(bp), PACK(delta, 0));
SET_UNALLOC(bp);
reset_block(bp);
insert_free_block(bp, delta);
#ifdef DEBUG
{
printf("Block with size %zu remains a block:\n", asize);
printblock(bp);
}
#endif
}
else
{
PUT(HDRP(bp), PACK(csize, 1));
PUT(FTRP(bp), PACK(csize, 1));
SET_ALLOC(bp);
}
}

/*
* insert_free_block - insert a block into BST or segregated free list
* BLOCKSIZE should be duplicate of double word
*/
static void insert_free_block(block_ptr bp, size_t blocksize)
{
block_ptr *new = &larger_bin_root, parent = NULL;

if (!IS_OVER_BST_SIZE(blocksize))
{
/* Insert into segregated free list */
size_t dcount = blocksize / DSIZE;
if (bins[dcount - 1])
{
/* Connect it before the existing block as a new header */
NEXT_SAMESZ_BLKP(bp) = ptr_to_word(bins[dcount - 1]);
PREV_SAMESZ_BLKP(bins[dcount - 1]) = ptr_to_word(bp);
}
PREV_SAMESZ_BLKP(bp) = WNULL;
bins[dcount - 1] = bp;
return;
}

/* Figure out where to put the new node in BST */
while (*new)
{
size_t curr_size = GET_SIZE(HDRP(parent = *new));

if (blocksize < curr_size)
new = LCHLD_BLKPREF(parent);
else if (blocksize > curr_size)
new = RCHLD_BLKPREF(parent);
else
{
/* MWHAHAHAHAHA */
block_ptr next = word_to_ptr(NEXT_SAMESZ_BLKP(bp) = NEXT_SAMESZ_BLKP(parent));
if (next)
/* Connect it before the existing block as a new header */
PREV_SAMESZ_BLKP(next) = ptr_to_word(bp);
NEXT_SAMESZ_BLKP(parent) = ptr_to_word(bp);
PREV_SAMESZ_BLKP(bp) = ptr_to_word(parent);
return;
}
}

/* Connect this node as a child */
*new = bp;
PARENT_CHLDSLOTP(bp) = new;
#ifdef DEBUG
{
printf("Inserting a block: ");
printblock(bp);
}
#endif
}

/*
* find_fit - Find a fit for a block with asize bytes
* asize should be duplicate of double word
*/
static block_ptr find_fit(size_t asize)
{
block_ptr curr, *blocks;
size_t dcount = asize / DSIZE;

if (!IS_OVER_BST_SIZE(asize))
{
if (bins[dcount - 1])
{
/* Found a free list of this size! */
curr = bins[dcount - 1];
bins[dcount - 1] = word_to_ptr(NEXT_SAMESZ_BLKP(curr));
remove_freed_block(curr);
return curr;
}
/* ...not found? Proceed to BST */
}

if ((blocks = bestfit_search(&larger_bin_root, asize, 0)) == NULL)
/* No best-fit found...T T */
return NULL;

curr = *blocks;

/* Found a best-fit block! LOL */
#ifdef DEBUG
if ((*blocks = word_to_ptr(NEXT_SAMESZ_BLKP(curr))) == NULL)
{
printf("** All blocks with size %u (request: %zu) deleted.\n", GET_SIZE(HDRP(curr)), asize);
}
#else
/* Set the node to the next same size block if it has */
*blocks = word_to_ptr(NEXT_SAMESZ_BLKP(curr));
#endif
remove_freed_block(curr);
return curr;
}

/*
* printblock - print a block for debugging
*/
static inline void printblock(block_ptr bp)
{
size_t hsize, halloc, fsize, falloc;

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;
}
if (halloc)
printf("\033[43;37m%p: header: [%zu:%c:%c] footer: -\033[0m\n", bp,
hsize, (GET_PREV_ALLOC(bp) ? 'a' : 'f'), (halloc ? 'a' : 'f'));
else
{
printf("\033[42;30m%p: header: [%zu:%c:%c] footer: [%zu:%c]\033[0m", bp,
hsize, (GET_PREV_ALLOC(bp) ? 'a' : 'f'), (halloc ? 'a' : 'f'),
fsize, (falloc ? 'a' : 'f'));
if (IS_BST_NODE(bp))
printf("\033[1;44;33m[BST Node| parent slotp: %p, l: %p, r: %p]\033[0m",
PARENT_CHLDSLOTP(bp), LCHLD_BLKP(bp), RCHLD_BLKP(bp));
if (PREV_SAMESZ_BLKP(bp))
printf("\033[1;33m[PREV] %p\033[0m", word_to_ptr(PREV_SAMESZ_BLKP(bp)));
putchar('\n');
}
}

/*
* checkblock - as the name goes
*/
static inline void checkblock(block_ptr bp)
{
if (!aligned(bp))
printf("\n\033[1;47;31m## Error: %p is not doubleword aligned\033[0m\n", bp);
if (!GET_ALLOC(HDRP(bp)) && (GET(HDRP(bp)) & ~0x2) != (GET(FTRP(bp)) & ~0x2))
printf("\n\033[1;47;31m## Error: header does not match footer, header: %u, footer: %u \033[0m\n",
GET(HDRP(bp)), GET(FTRP(bp)));
if (GET_ALLOC(HDRP(bp)) != (GET_PREV_ALLOC(NEXT_BLKP(bp)) >> 1))
printf("\n\033[1;47;31m## Error: %p allocation does not match next block's prev_alloc\033[0m\n", bp);
}

static void printchain(block_ptr node)
{
while (node)
{
printblock(node);
printf("->");
node = word_to_ptr(NEXT_SAMESZ_BLKP(node));
}
}

static void printtree(block_ptr node, int depth)
{
int i;
if (node == NULL)
return;
printf("BST: ");
for (i = 0; i < depth; i++)
putchar('-');
printchain(node);
putchar('\n');
printtree(LCHLD_BLKP(node), depth + 1);
printtree(RCHLD_BLKP(node), depth + 1);
}

static void checklist(block_ptr node)
{
if (node == NULL)
return;
if (PREV_SAMESZ_BLKP(node) &&
word_to_ptr(NEXT_SAMESZ_BLKP(word_to_ptr(PREV_SAMESZ_BLKP(node)))) != node)
printf("\n\033[1;47;31m## Bad neighbor pointer: %p\033[0m\n", node);
checklist(word_to_ptr(NEXT_SAMESZ_BLKP(node)));
}

static void checktree(block_ptr node)
{
if (node == NULL)
return;
if (*PARENT_CHLDSLOTP(node) != node)
printf("\n\033[1;47;31m## Bad parent pointer: %p\033[0m\n", node);
checklist(node);
checktree(LCHLD_BLKP(node));
checktree(RCHLD_BLKP(node));
}

/*
* checkheap - check the heap for consistency
*/
void mm_checkheap(int verbose)
{
char *bp = heap_listp;

if (verbose)
printf("Heap (%p):\n", heap_listp);

if ((GET_SIZE(HDRP(heap_listp)) != DSIZE) || !GET_ALLOC(HDRP(heap_listp)))
printf("\n\033[1;47;31m## Bad prologue header\033[0m\n");
checkblock(heap_listp);

for (bp = heap_listp; GET_SIZE(HDRP(bp)) > 0; bp = NEXT_BLKP(bp))
{
if (verbose)
printblock(bp);
checkblock(bp);
}

if (verbose)
{
printblock(bp);
{
unsigned int i;
for (i = 1; i <= fixed_bin_count; i++)
if (bins[i - 1])
{
printf("BIN #%d: size = %d ", i, i * DSIZE);
checklist(bins[i - 1]);
printchain(bins[i - 1]);
}
putchar('\n');
checktree(larger_bin_root);
printtree(larger_bin_root, 0);
}
}
if ((GET_SIZE(HDRP(bp)) != 0) || !(GET_ALLOC(HDRP(bp))))
printf("\n\033[1;47;31m## Bad epilogue header\033[0m\n");
}


最终成绩如图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: