CSAPP: Malloc Lab 7

csapp malloc optimization lab ics

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


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

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

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

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

  1. 隐式空闲链表,只记录每个块的大小,分配时遍历整个堆寻找大小适合的块
  2. 显式空闲链表,在空闲块中额外记录前一个、后一个空闲块的位置,可以节省遍历时间
  3. 分离空闲链表,维护多个链表,将不同大小类的块分到同一个链表中
  • 首次适配:选择第一个合适的块
  • 下次适配:每次搜索从上次结束的地方开始
  • 最佳适配:选择大小合适的最小块
其他实现与堆块的布局在此就不赘述了~书上都有所介绍
这个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");  
    } 

    最终成绩如图:

你可能感兴趣的:(malloc,CSAPP)