Starting from:
$30

$24

Homework #3 Dynamic Memory Allocator Solution

We **HIGHLY** suggest that you read this entire document, the book chapter,

and examine the base code prior to beginning. If you do not read the entire

document before beginning, you may find yourself doing extra work.




:scream: Start early so that you have an adequate amount of time to test

your program!




:scream: The functions `malloc`, `free`, `realloc`, `memalign`, `calloc`,

etc., are **NOT ALLOWED** in your implementation. If any of these functions,

or any other function with similar functionality is found in your program,

you **will receive a <span style="color:red;"ZERO</span**.




**NOTE:** In this document, we refer to a word as 2 bytes (16 bits) and a memory

row as 4 words (64 bits). We consider a page of memory to be 4096 bytes (4 KB)




# Introduction




You must read **Chapter 9.9 Dynamic Memory Allocation Page 839** before

starting this assignment. This chapter contains all the theoretical

information needed to complete this assignment. Since the textbook has

sufficient information about the different design strategies and

implementation details of an allocator, this document will not cover this

information. Instead, it will refer you to the necessary sections and pages in

the textbook.




## Takeaways




After completing this assignment, you will have a better understanding of:

* The inner workings of a dynamic memory allocator

* Memory padding and alignment

* Structs and linked lists in C

* [errno](https://linux.die.net/man/3/errno) numbers in C

* Unit testing in C




# Overview




You will create a segregated free list allocator for the x86-64 architecture

with the following features:




- Best-fit placement policy.

- One free list for each block size. The collection of free lists will

itself be organized as a "list of lists", maintained in increasing order

of block size.

- Immediate coalescing on free with adjacent free blocks.

- Boundary tags with footer optimization that allows footers to be omitted

from allocated blocks.

- Block splitting without creating splinters.

- Allocated blocks aligned to "double memory row" (16-byte) boundaries.

- Free lists maintained using **last in first out (LIFO)** discipline.




You will implement your own versions of the **malloc**, **realloc**, and

**free** functions.




You will use existing Criterion unit tests and write your own to help debug

your implementation.




## Segregated Free List Management Policy




You **MUST** use a **segregated free list** as described in **Chapter 9.9.14

Page 863** to manage free blocks. Your segregated list will consist of a

"list of free lists", one for each size block that has ever been created.

This list of free lists will be maintained in increasing order of block size.

This will permit you to implement a best fit placement policy, by scanning

the free lists in increasing order of block size until a large enough free block

has been found.

Within each free list, the blocks are to be maintained using a LIFO (last-in, first-out)

discipline. This means that each time a block is allocated, it is taken from

the front of the appropriate free list, and each time a block is freed, it is added

to the front of the appropriate free list.

If a block is to be freed, but there is currently no free list for that size block,

a new free list is to be created and inserted into the "list of free lists"

at the proper position.




## Splitting Blocks & Splinters




Your allocator must split blocks at allocation time to reduce the amount of

internal fragmentation. Details about this feature can be found in

**Chapter 9.9.8 Page 849**.

Note that the minimum useful block size is 32 bytes. No "splinters" of smaller

size than this are ever to be created. If splitting a block to be allocated would

result in a splinter, then the block should not be split; rather, the block should

be used as-is to satisfy the allocation request (*i.e.*, you will "over-allocate"

by issuing a block slightly larger than that required).




:thinking: Why is the minimum block size 32? As you read more details about

the format of a block header, block footer, and alignment requirements,

think about how these constrain the minimum block size.




## Block Placement Policy




When allocating memory, use **best fit placement** (discussed in **Chapter 9.9.7 Page 849**).

In brief, you should search the list of free lists in increasing order of block size

o find the first nonempty free list for a block size that is large enough to accommodate

the request, taking into account the additional space required to store the block header,

footer, and free list links. You should then use the first block in this free list,

splitting it if it is too large and doing so would not create a splinter. If the block is

split, the remainder should be inserted at the beginning of the correct free list for

its size.




If there is no nonempty free list for a block size large enough to satisfy

the allocation request, then `sf_mem_grow` should be called to extend the heap

by an additional page of memory. After coalescing this page with any free block

that immediately precedes it, you should attempt to use the resulting block of memory

to satisfy the allocation request; splitting it as usual if it is too large and

no splinter would result. If the block of memory is still not large enough,

another call to `sf_mem_grow` should be made; continuing to grow the heap until

either a large enough block is obtained or the return value from `sf_mem_grow`

indicates that there is no more memory.




## Immediate Coalescing




Your implementation must perform **immediate coalescing** upon freeing a block.

This essentially amounts to the procedure described in **Chapter 9.9.10 Page 850**.

Coalescing must **only** be done when `sf_free` is called, for some cases of

calls to `sf_realloc` described below, and when `sf_mem_grow` is called.




## Block Headers & Footers




In **Chapter 9.9.6 Page 847 Figure 9.35**, the header is defined as 2 words (32

bits) to hold the block size and allocated bit. In this assignment, the header

will be 4 words (i.e. 64 bits or 1 memory row). The header fields will be similar

to those in the textbook but you will keep an extra field for recording whether

or not the previous block is allocated (*i.e.* for the the optimization that permits

footers to be omitted from allocated blocks).




**Block Header Format (allocated block):**

<pre

+--------------------------------------------+------------------+-------+-------+---------+ <- header

| requested_size | block_size | | prev | alloc |

| | | 00 | alloc | 1 |

| 32 bits | 28 bits | | 1 bit | bit |

+--------------------------------------------+------------------+-------+-------+---------+ <- (double-row

aligned)

</pre




**Block Header Format (free block):**

<pre

+--------------------------------------------+------------------+-------+-------+---------+

| unused | block_size | | prev | alloc |

| | | 00 | alloc | 1 |

| 32 bits | 28 bits | | 1 bit | bit |

+--------------------------------------------+------------------+-------+-------+---------+ <- (double-row

| | aligned)

| Pointer to next free block |

| |

+--------------------------------------------+------------------+-------+-------+---------+

| |

| Pointer to previous free block |

| |

+--------------------------------------------+------------------+-------+-------+---------+

</pre




**Block Footer Format (free block only):**

<pre

+--------------------------------------------+------------------+-------+-------+---------+

| requested_size | block_size | | | alloc |

| | | 00 | 0 | 1 |

| 32 bits | 28 bits | | | bit |

+--------------------------------------------+------------------+-------+-------+---------+

</pre




- The `block_size` field is 28 bits (but really 32 bits - see the note below).

It gives the number of bytes for the **entire** block (including header/footer,

payload, and padding)

- The `requested_size` field is 32 bits. It is the number of bytes that the

client requested.

- The `alloc` bit is a boolean. It is 1 if the block is allocated and 0 if it

is free.

- The `prev_alloc` bit is also a boolean. It is 1 if the **immediately preceding** block

in the heap is allocated and 0 if it is not.




:thinking: The block size field represents a 32-bit block size, but requires

only 28 bits to do so because the four low-order bits of a block size are always

zero due to the alignment requirement. The definition of the C structure `sf_block_info`

(see below) defines the `block_size` field to be a "bit field" of 28 bits in width.

If you extract the value of this field using the normal way of referencing a field

in a structure, the value you obtain should be regarded as the block size shifted

right by four bits. To obtain the actual block size, you need to shift this value

left by four bits. Similarly, when storing a 32-bit block size into the `block_size`

field, you first need to shift it right by four bits to obtain a 28-bit value.




:thinking: Here is an example of determining the block size required to satisfy

a particular requested payload size. Suppose the requested size is 25 bytes.

An additional 8 bytes will be required to store the block header, which must always

be present. That means a block of at least 33 bytes must be used, however due to

alignment requirements this has to be rounded up to the next multiple of 16,

which is 48. As a result, there will be 15 bytes of "padding" at the end of

the payload area, which contributes to internal fragmentation.

Besides the header, when the block is freed, it will also be necessary

to store a footer, as well and next and previous links for the freelist.

These will take an additional 24 bytes of space, however when the block is free there

is no payload so the payload area can be used to store this information, assuming that

the payload area is big enough in the first place. But the payload area is 40 bytes

(25 bytes plus 15 bytes of padding), which is certainly bigger than 24 bytes,

so a block of total size 48 will be fine.

Note that if a block is smaller than 32 bytes, there would not be enough space to

store the header, footer, and freelist links when the block is free.

This is why the minimum block size is 32 bytes.




# Getting Started




**Remember to use the `--strategy-option theirs` flag with the `git merge`

command as described in the `hw1` doc to avoid merge conflicts in the Gitlab

CI file.**




Fetch and merge the base code for `hw3` as described in `hw0` from the

following link: https://gitlab02.cs.stonybrook.edu/cse320/hw3




## Directory Structure




<pre

.

├── .gitlab-ci.yml

└── hw3

├── include

│   ├── debug.h

│   └── sfmm.h

├── lib

│   └── sfutil.o

├── Makefile

├── src

│   ├── main.c

│   └── sfmm.c

└── tests

└── sfmm_tests.c

</pre




The `lib` folder contains the object file for the `sfutil` library. This

library provides you with several functions to aid you with the implementation

of your allocator. <span style="color:red"**Do NOT delete this file as it

is an essential part of your homework assignment.**</span




The provided `Makefile` creates object files from the `.c` files in the `src`

directory, places the object files inside the `build` directory, and then links

the object files together, including `lib/sfutil.o`, to make executables that

are stored to the `bin` directory.




**Note:** `make clean` will not delete `sfutil.o` or the `lib` folder, but it

will delete all other contained `.o` files.




The `sfmm.h` header file contains function prototypes and defines the format

of the various data structures that you are to use.




**DO NOT modify `sfmm.h` or the Makefile.** Both will be replaced when we run

tests for grading. If you wish to add things to a header file, please create

a new header file in the `include` folder




All functions for your allocator (`sf_malloc`, `sf_realloc`, and `sf_free`)

**must be** implemented in `src/sfmm.c`.




The program in `src/main.c` contains a basic example of using the initialization

and allocation functions together. Running `make` will create a `sfmm`

executable in the `bin` directory. This can be run using the command `bin/sfmm`.




Any functions other than `sf_malloc`, `sf_free`, and `sf_realloc` **WILL NOT**

be graded.




# Allocation Functions




You will implement the following three functions in the file `src/sfmm.c`.

The file `include/sfmm.h` contains the prototypes and documentation found here.




Standard C library functions set `errno` when there is an error. To avoid

conflicts with these functions, your allocation functions will set `sf_errno`, a

variable declared as `extern` in `sfmm.h`.




```c

/*

* This is your implementation of sf_malloc. It acquires uninitialized memory that

* is aligned and padded properly for the underlying system.

*

* @param size The number of bytes requested to be allocated.

*

* @return If size is 0, then NULL is returned without setting sf_errno.

* If size is nonzero, then if the allocation is successful a pointer to a valid region of

* memory of the requested size is returned. If the allocation is not successful, then

* NULL is returned and sf_errno is set to ENOMEM.

*/

void *sf_malloc(size_t size);




/*

* Resizes the memory pointed to by ptr to size bytes.

*

* @param ptr Address of the memory region to resize.

* @param size The minimum size to resize the memory to.

*

* @return If successful, the pointer to a valid region of memory is

* returned, else NULL is returned and sf_errno is set appropriately.

*

* If sf_realloc is called with an invalid pointer sf_errno should be set to EINVAL.

* If there is no memory available sf_realloc should set sf_errno to ENOMEM.

*

* If sf_realloc is called with a valid pointer and a size of 0 it should free

* the allocated block and return NULL without setting sf_errno.

*/

void* sf_realloc(void *ptr, size_t size);




/*

* Marks a dynamically allocated region as no longer in use.

* Adds the newly freed block to the free list.

*

* @param ptr Address of memory returned by the function sf_malloc.

*

* If ptr is invalid, the function calls abort() to exit the program.

*/

void sf_free(void *ptr);

```




:scream: <span style="color:red;"Make sure these functions have these exact names

and arguments. They must also appear in the correct file. If you do not name

the functions correctly with the correct arguments, your program will not

compile when we test it. **YOU WILL GET A ZERO**</span




# Initialization Functions




In the `build` directory, we have provided you with the `sfutil.o` object file.

When linked with your program, this object file allows you to access the

`sfutil` library, which contains the following functions:




This library contains the following functions:




```c

/*

* Any program using the sfmm library must call this function ONCE

* before issuing any allocation requests. This function DOES NOT

* allocate any space to your allocator.

*/

void sf_mem_init();




/*

* Any program using the sfmm library must call this function ONCE

* after all allocation requests are complete. If implemented cleanly,

* your program should have no memory leaks in valgrind after this function

* is called.

*/

void sf_mem_fini();




/*

* This function increases the size of your heap by adding one page of

* memory to the end.

*

* @return On success, this function returns a pointer to the start of the

* additional page, which is the same as the value that would have been returned

* by sf_mem_heap_end() before the size increase. On error, NULL is returned

* and sf_errno is set to ENOMEM.

*/

void *sf_mem_grow();




/* The size of a page of memory returned by sf_mem_grow(). */

#define PAGE_SZ 4096




/*

* @return The starting address of the heap for your allocator.

*/

void *sf_mem_start();




/*

* @return The ending address of the heap for your allocator.

*/

void *sf_mem_end();

```




The function `sf_mem_init` **MUST** be used to initialize memory. It is to be

called once **BEFORE** using any of the other functions from `sfutil.o`.

The function `sf_mem_grow` is to be invoked by `sf_malloc`, at the time of the

first allocation request to set up the heap prologue and epilogue and obtain

an initial free block, and on subsequent allocations when a large enough block

to satisfy the request is not found.




:scream: As these functions are provided in a pre-built .o file, the source

is not available to you. You will not be able to debug these using gdb.

You must treat them as black boxes.




# sf_mem_grow




For this assignment, your implementation **MUST ONLY** use `sf_mem_grow` to

extend the heap. **DO NOT** use any system calls such as **brk** or **sbrk**

to do this.




Function `sf_mem_grow` returns memory to your allocator in pages.

Each page is 4096 bytes (4 KB) and there are a limited, small number of pages

available (the actual number may vary, so do not hard-code any particular limit

into your program). Each call to `sf_mem_grow` extends the heap by one page and

returns a pointer to the new page (this will be the same pointer as would have

been obtained from `sf_mem_end` before the call to `sf_mem_grow`.




The `sf_mem_grow` function also keeps track of the starting and ending addresses

of the heap for you. You can get these addresses through the `sf_mem_start` and

`sf_mem_end` functions.




:smile: A real allocator would typically use the **brk**/**sbrk** system calls

calls for small memory allocations and the **mmap**/**munmap** system calls

for large allocations. To allow your program to use other functions provided by

glibc, which rely on glibc's allocator (*i.e.* `malloc`), we have provided

`sf_mem_grow` as a safe wrapper around **sbrk**. This makes it so your heap and

the one managed by glibc do not interfere with each other.




# Implementation Details




## Memory Row Size




The table below lists the sizes of data types (following Intel standard terminlogy)

on x86-64 Linux Mint:




| C declaration | Data type | x86-64 Size (Bytes) |

| :--------------: | :----------------: | :----------------------: |

| char | Byte | 1 |

| short | Word | 2 |

| int | Double word | 4 |

| long int | Quadword | 8 |

| unsigned long | Quadword | 8 |

| pointer | Quadword | 8 |

| float | Single precision | 4 |

| double | Double precision | 8 |

| long double | Extended precision | 16




:nerd: You can find these sizes yourself using the sizeof operator. For

example, `printf("%lu\n", sizeof(int))` prints 4




In this assignment we will assume that each "memory row" is 8 bytes (64 bits) in size.

All pointers returned by `sf_malloc` are to be "double memory row" aligned;

that is, they will be addresses that are multiples of 16.

This will permit such pointers to be used to store values of the largest data type

(`long double`) supported by the hardware.




## Free List Heads




In the file `include/sfmm.h`, you will see the following declarations:




```c

typedef struct sf_free_list_node {

size_t size;

sf_header head; /* Head of list of free nodes of same size. */

struct sf_free_list_node *next;

struct sf_free_list_node *prev;

} sf_free_list_node;




sf_free_list_node sf_free_list_head;

```




The variable `sf_free_list_head` is the head of the "list of free lists",

which contains a `sf_free_list_node` for each size block that has ever been

inserted. Each `sf_free_list_node` contains a `head` field, which gives

access to a list of headers of free blocks, and a `size` field, which

records the size of the blocks in that particular list (all blocks in

a given list have the same size). The nodes in the "list of free lists"

are maintained in increasing order of the value of their size field.




The "list of free lists" is maintained as a **circular, doubly linked list**.

Each node in the list contains a `next` pointer that points to the next

node in the list, and a `prev` pointer that points the previous node.

The variable `sf_free_list_head` is a dummy, "sentinel" node, which is

used to connect the beginning and the end of the list. This node is always

present and (aside from its `next` and `free` pointers) does **not** contain

any other data. If the list is empty, then the fields `sf_freelist_head.next`

and `sf_freelist_head.prev` both contain `&sf_freelist_head`

(*i.e.* the sentinel node points back to itself). If the list is nonempty,

then `sf_freelist_head.next` points to the first node in the list and

`sf_freelist_head.prev` points to the last node in the list.

Inserting into and deleting from a circular doubly linked list is done

in the usual way, except that, owing to the use of the sentinel, there

are no edge cases for inserting or removing at the beginning or the end

of the list.

If you need a further introduction to this data structure, you can readily

find information on it by googling ("circular doubly linked lists with sentinel").




As stated above the `head` field of each `sf_free_list_node` serves as

the head of a list that consists of the headers of free blocks of a single size.

These free lists are also maintained as circular, doubly linked lists,

with the `head` field of `sf_free_list_node` as the sentinel.

Insertion into and deletion from these individual free lists is entirely

analogous to the same operations on the "list of free lists".

For our purposes, all insertions and deletions into an individual free list

will be done at the beginning of the list (resulting in a LIFO discipline).

So inserting into a list would set the `next` field of the sentinel to be

the inserted node, and removing from a list would likewise modify the

`next` field of the sentinel.




:scream: You **MUST** use the `sf_free_list_head` variable as the head

of your "list of free lists" and you **MUST** maintain this as a

circular, doubly linked list. You **MUST** also maintain the individual

free lists as circular, doubly linked lists and you must access them

using a LIFO discipline.

The helper functions discussed later, as well as the unit tests,

will assume that you have done this when accessing your free lists.




The `sf_mem_init` function in the `sfutil.o` library takes care of initializing

the `sf_free_list_head` sentinel with the proper self-referential links, so you

don't have to worry about that.

The `sfutil.o` library also contains a function `sf_add_free_list`, which you

should use when you need to create a new free list for blocks of a specified size.

The `sf_add_free_list` function handles the tasks of allocating an `sf_free_list_node`,

initializing the `head` field as an empty list, and inserting the node before

a specified existing node in the "list of free lists". A pointer to the newly added

node is returned. Although the mechanics of adding a new node the the list of

free lists is handled for you, you are responsible for determining the proper

position to add the new node, so as to maintain the list in increasing order of

block size.




## Block Header & Footer Fields




The various header and footer formats are specified in `include/sfmm.h`:




```c

Format of an allocated memory block

+-----------------------------------------------------------------------------------------+

| 64-bits wide |

+-----------------------------------------------------------------------------------------+




+--------------------------------------------+------------------+-------+-------+---------+ <- header

| requested_size | block_size | | prev | alloc |

| | | 00 | alloc | 1 |

| 32 bits | 28 bits | | 1 bit | bit |

+--------------------------------------------+------------------+-------+-------+---------+ <- (double-row

| | aligned)

| Payload and Padding |

| (N Memory Rows) |

| |

| |

+--------------------------------------------+------------------+-------+-------+---------+










Format of a free memory block

+--------------------------------------------+------------------+-------+-------+---------+ <- header

| unused | block_size | | prev | alloc |

| | | 00 | alloc | 1 |

| 32 bits | 28 bits | | 1 bit | bit |

+--------------------------------------------+------------------+-------+-------+---------+ <- (double-row

| | aligned)

| Pointer to next free block |

| |

+--------------------------------------------+------------------+-------+-------+---------+

| |

| Pointer to previous free block |

| |

+--------------------------------------------+------------------+-------+-------+---------+

| |

| Unused |

| (N Memory Rows) |

| |

| |

+--------------------------------------------+------------------+-------+-------+---------+ <- footer

| unused | block_size | | prev | alloc |

| | | 00 | alloc | 1 |

| 32 bits | 28 bits | | 1 bit | bit |

+--------------------------------------------+------------------+-------+-------+---------+

```




The `sfmm.h` header file contains C structure definitions corresponding to the above diagrams:




```c

/* Struct for the common part of block header and block footer. */

typedef struct {

unsigned allocated : 1;

unsigned prev_allocated : 1;

unsigned two_zeroes : 2;

unsigned block_size : 28; // Note: value is size4

unsigned requested_size : 32;

} __attribute__((packed)) sf_block_info;

```




:smile: The `__attribute__((__packed__))` annotation tells gcc to leave out

all padding between members of the struct. In this way, the fields are forcibly

placed next to each other.




```c

/* Struct for a block header */

typedef struct sf_header {

sf_block_info info;

/*

* A free block has pointers to the next and previous free block of the same size.

* An allocated block has a payload, and since the next and previous pointers are

* only needed for a free block, the payload can use this space when the block is

* allocated. We use a union to reflect this idea.

*/

union {

uint64_t payload; /* First word of payload (aligned). */

struct {

struct sf_header *next;

struct sf_header *prev;

} links; /* Pointers to next and previous free blocks. */

};

} sf_header;




/* Struct for a block footer (footers are only present in free blocks) */

typedef struct {

sf_block_info info;

} sf_footer;

```




:thumbsup: You can use casts to convert a generic pointer value to one

of type `sf_header` or `sf_footer`, in order to make use of the above

structure definitions to easily access the various fields.




## Overall Structure of the Heap: Prologue and Epilogue




Your heap should use a prologue and epilogue (as described in the book) to

arrange for the proper block alignment and to avoid edge cases when coalescing blocks.

The overall organization of the heap is as shown below:




```c

Format of the heap

+-----------------------------------------------------------------------------------------+

| 64-bits wide |

+-----------------------------------------------------------------------------------------+




heap start

+-----------------------------------------------------------------------------------------+ <- (aligned)

| |

| 0 | padding

| 64 bits |

+-----------------------------------------------------------------------------------------+

| | | | | |

| 0 | 0 | 00 | 0 | 1 | prologue

| 32 bits | 28 bits | | | | header

+--------------------------------------------+------------------+-------+-------+---------+ <- (aligned)

| | | | | |

| 0 | 0 | 00 | 0 | 1 | prologue

| 32 bits | 28 bits | | | | footer

+-----------------------------------------------------------------------------------------+

| |

| |

| |

| |

| Allocated and free blocks |

| |

| |

| |

+-----------------------------------------------------------------------------------------+

| | | | prev | |

| 0 | 0 | 00 | alloc | 1 | epilogue

| 32 bits | 28 bits | | 1 bit | |

+-----------------------------------------------------------------------------------------+ <- heap end

(aligned)

```




The "prologue" consists of padding (to achieve the desired alignment)

and an allocated block with just a header and a footer and no payload area.

The "epilogue" consists only of an allocated footer.

The prologue and epilogue are never freed.

When the heap is extended, a new epilogue is created at the end of the

newly added region and the old epilogue becomes the header of the new block.

This is as described in the book.




As your heap is initially empty, at the time of the first call to `sf_malloc`

you will need to make one call to `sf_mem_grow` to obtain a page of memory

within which to set up the prologue and initial epilogue.

The remainder of the memory in this first page should then be inserted into

the free list as a single block.




## Notes on sf_malloc




When implementing your `sf_malloc` function, first determine if the request size

is 0. If so, then return `NULL` without setting `sf_errno`.

If the request size is non-zero, then you should determine the size of the

block to be allocated by adding the header size and the size of any necessary

padding to reach a size that is a multiple of 16 to maintain proper alignment.

Remember also that the block has to be big enough to store the footer and

`next` and `prev` pointers when it is free, though as these fields are not

present in an allocated block this space can (and should) be overlapped with

the payload area. These constraints lead to a minimum block size of 32 bytes,

so you should not attempt to allocate any block smaller than this.

After having determined the required block size, you should search the free lists

to obtain the first block on the first freelist whose blocks are at least

that big. If there is no such block, then you must use `sf_mem_grow` to

request more memory. (For requests larger than a page, more than one such

call might be required). If your allocator ultimately cannot satisfy the

request, your `sf_malloc` function must set `sf_errno` to `ENOMEM` and

return `NULL`.




### Notes on sf_mem_grow




After each call to `sf_mem_grow`, you must attempt to coalesce the newly

allocated page with any free block immediately preceding it, in order to build

blocks larger than one page. After coalescing, add the entire new block to the

**beginning** of the correct list.




**Note:** Do not coalesce with the prologue or past the beginning of the heap.




### Notes on sf_free




When implementing `sf_free`, you must first verify that the pointer being

passed to your function belongs to an allocated block. This can be done by

examining the fields in the block header and footer. In this assignment,

we will consider the following cases for invalid pointers:




- The pointer is `NULL`.

- The header of the block is before the end of the prologue, or after the

beginning of the epilogue.

- The `allocated` bit in the header or footer is 0

- The `block_size` field is not a multiple of 16 or is less than the

minimum block size of 32 bytes.

***NOTE: It is always a multiple of 16***

- The `requested_size` field, plus the size required for the block header,

is greater than the `block_size` field.

- If the `prev_alloc` field is 0, indicating that the previous block is free,

then the `alloc` fields of the previous block header and footer should also be 0.




If an invalid pointer is passed to your function, you must call `abort` to exit

the program. Use the man page for the `abort` function to learn more about this.




After confirming that a valid pointer was given, you must free the block.

First, determine if it is possible to coalesce the block with either one or

both of the adjacent blocks. If it can, remove any blocks to be coalesced

from their free lists and combine the blocks. Then, insert the newly freed

block into the proper free list.




**Note that the only other times you should coalesce a block is after calling

`sf_mem_grow` or in some cases of `sf_realloc`**




### Notes on sf_realloc




When implementing your `sf_realloc` function, you must first verify that the

pointer and size parameters passed to your function are valid. The criteria for

pointer validity are the same as those described in the 'Notes on sf_free'

section above. If the pointer is valid but the size parameter is 0,

free the block and return `NULL`.




After verifying both parameters, consider the cases described below.

Note that in some cases, `sf_realloc` is more complicated than calling `sf_malloc`

to allocate more memory, `memcpy` to move the old memory to the new memory, and

`sf_free` to free the old memory.




## Reallocating to a Larger Size




When reallocating to a larger size, always follow these three steps:




1. Call `sf_malloc` to obtain a larger block.




2. Call `memcpy` to copy the data in the block given by the client to the block

returned by `sf_malloc`.




3. Call `sf_free` on the block given by the client (coalescing if necessary).




4. Return the block given to you by `sf_malloc` to the client.




If `sf_malloc` returns `NULL`, `sf_realloc` must also return `NULL`. Note that

you do not need to set `sf_errno` in `sf_realloc` because `sf_malloc` should

take care of this.




## Reallocating to a Smaller Size




When reallocating to a smaller size, your allocator must use the block that was

passed by the caller. You must attempt to split the returned block. There are

two cases for splitting:




- Splitting the returned block results in a splinter. In this case, do not

split the block. Leave the splinter in the block, update the header field

if necessary, and return the same block back to the caller.




**Example:**




<pre

b b

+----------------------+ +------------------------+

| allocated | | allocated. |

| Blocksize: 64 bytes | sf_realloc(b, 32) | Block size: 64 bytes |

| payload: 48 bytes | | payload: 32 bytes |

| | | |

| | | |

+----------------------+ +------------------------+

</pre




In the example above, splitting the block would have caused a 16-byte splinter.

Therefore, the block is not split. The `requested_size` field in the footer

is set to 32.




- The block can be split without creating a splinter. In this case, split the

block and update the block size fields in both headers. Free the remaining block

(i.e. coalesce if possible and insert the block into the head of the correct

free list). Return a pointer to the payload of the smaller block to the caller.




Note that in both of these sub-cases, you return a pointer to the same block

that was given to you.




**Example:**




<pre

b b

+----------------------+ +------------------------+

| allocated | | allocated | free |

| Blocksize: 64 bytes | sf_realloc(b, 16) | 32 bytes | 32 bytes. |

| payload: 48 bytes | | payload: | |

| | | 16 bytes | goes into |

| | | | free list |

+----------------------+ +------------------------+

</pre




# Helper Functions




The `sfutil` library additionally contains the following helper functions,

which should be self explanatory. They all output to `stderr`.




```c

void sf_show_block_info(sf_block_info *ip);

void sf_show_blocks();

void sf_show_free_list();

void sf_show_heap();

```




We have provided these functions to help you visualize your free lists and

allocated blocks. We have also provided you with additional unit tests which

will check certain properties of each free block when a snapshot is being

performed and the snapshot verbose value is set to **true**.




# Things to Remember




- Make sure that memory returned to the client is aligned and padded correctly for

the system we use (64-bit Linux Mint).

- We will not grade using Valgrind. However, you are encouraged to use it to

detect alignment errors.




# Unit Testing




For this assignment, we will use Criterion to test your allocator. We have

provided a basic set of test cases and you will have to write your own as well.




You will use the Criterion framework alongside the provided helper functions to

ensure your allocator works exactly as specified.




In the `tests/sfmm_tests.c` file, there are ten unit test examples. These tests

check for the correctness of `sf_malloc`, `sf_realloc`, and `sf_free`.

We provide some basic assertions, but by no means are they exhaustive. It is your

job to ensure that your header/footer bits are set correctly and that blocks are

allocated/freed as specified.




## Compiling and Running Tests




When you compile your program with `make`, a `sfmm_tests` executable will be

created in the `bin` folder alongside the `main` executable. This can be run

with `bin/sfmm_tests`. To obtain more information about each test run, you can

use the verbose print option: `bin/sfmm_tests --verbose=0`.




## Writing Criterion Tests




The first test `malloc_an_Integer` tests `sf_malloc`. It allocates space for an

integer and assigns a value to that space. It then runs an assertion to make

sure that the space returned by `sf_malloc` was properly assigned.




```c

cr_assert(*x == 4, "sf_malloc failed to give proper space for an int!");

```




The string after the assertion only gets printed to the screen if the assertion

failed (i.e. `*x != 4`). However, if there is a problem before the assertion,

such as a SEGFAULT, the unit test will print the error to the screen and

continue to run the rest of the unit tests.




For this assignment **<span style="color:red;"you must write 5 additional unit tests

which test new functionality and add them to `sfmm_tests.c` below the following

comment:</span**




```

//############################################

//STUDENT UNIT TESTS SHOULD BE WRITTEN BELOW

//DO NOT DELETE THESE COMMENTS

//############################################

```




For additional information on Criterion library, take a look at the official

documentation located [here](http://criterion.readthedocs.io/en/master/)! This

documentation is VERY GOOD.




# Hand-in instructions

Make sure your directory tree looks like this and that your homework compiles.




<pre

.

├── .gitlab-ci.yml

└── hw3

├── include

│   ├── debug.h

│   └── sfmm.h

├── lib

│   └── sfutil.o

├── Makefile

├── src

│   ├── main.c

│   └── sfmm.c

└── tests

└── sfmm_tests.c

</pre




This homework's tag is: `hw3`




<pre

$ git submit hw3

</pre




:nerd: This program will be very difficult to get working unless you are

extremely disciplined about your coding style. Think carefully about how

to modularize your code in a way that makes it easier to understand and

avoid mistakes. Verbose, repetitive code is error-prone and **evil!**

When writing your program try to comment as much as possible.

Format the code consistently. It is much easier for your TA and the

professor to help you if we can quickly figure out what your code does.

More products