malloc: defined set_footer, get_footer, init_gc_chunk find_free_chunk and merge_chunk

This commit is contained in:
Andrew Scott 2024-09-18 13:54:53 -04:00
parent 37d5cfd532
commit 5749c68035
Signed by: a
GPG key ID: 7CD5A5977E4931C1
2 changed files with 137 additions and 14 deletions

View file

@ -2,11 +2,13 @@
#define GC_MALLOC_H
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
// Generally, inline functions are preferable to macros resembling functions.
// https://www.kernel.org/doc/html/v4.10/process/coding-style.html#data-structures
#define ALIGN 8 /* Number of bytes to which chunk size is aligned */
#define OVERHEAD 1 /* Number of 8-byte segments for chunk size & flags */
#define MAGIC 2 /* Number of 8-byte segments from top of chunk to fields */
#define MIN 3 /* Minimum number of 8-byte segments for data in a chunk */
@ -14,34 +16,47 @@
/*
* Metadata and user data for allocated memory
*
* footer: user data for previous chunk
* footer: size of previous block or user data of previous block if allocated
* size: size of chunk in bytes
* merge: flag to identify when the chunk can be coalesced
* free: flag to identify when the chunk is free
* merge: flag to identify when the chunk can be coalesced,
* possible values 0 (false, don't merge) or non-zero (true, do merge)
* free: flag to identify when the chunk is free,
* possible values 0 (false, allocated) or non-zero (true, chunk is free)
* next: pointer to next chunk in the free list
* prev: pointer to previous chunk in the free list
*/
struct gc_chunk {
uint64_t footer;
uint32_t size;
uint16_t merge;
uint16_t free;
uint16_t merge; /* 1 = do merge, initialize to 0 */
uint16_t free; /* 0 = allocated, initialize to 1 */
struct gc_chunk *next;
struct gc_chunk *prev;
};
// Gets a pointer to the next chunk
struct gc_chunk *get_next_chunk(struct gc_chunk *curr);
struct gc_chunk *find_next_chunk(struct gc_chunk *curr);
// Gets a pointer to the previous chunk, ensuring that current can be coalesced
struct gc_chunk *get_prev_chunk(struct gc_chunk *curr);
struct gc_chunk *find_prev_chunk(struct gc_chunk *curr);
// Updates chunk footer information
// Updates chunk footer information (remember that the current chunk's footer
// data is actually part of the next chunk)
void set_footer(struct gc_chunk *curr);
// Gets footer information for the current chunk
// Gets footer information for the current chunk - useful for debugging
int get_footer(struct gc_chunk *curr);
// Creates a new chunk with sentinel
struct gc_chunk *init_gc_chunk(void);
// Gets a pointer to a suitable chunk from the free list, otherwise creates a
// new chunk and returns it's pointer
struct gc_chunk *find_free_chunk(size_t size);
// Coalesces chunks when merge flag is set
void merge_chunk(struct gc_chunk *curr);
// Performs allocations of SIZE bytes
void *gc_malloc(size_t size);

View file

@ -1,9 +1,12 @@
#include <assert.h>
#include <stdint.h>
#include <sys/mman.h>
#include <unistd.h>
#include "gc_malloc.h"
static struct gc_chunk *freelist = NULL;
// Hides chunk header
static inline void *hide(struct gc_chunk *curr)
{
@ -17,26 +20,131 @@ static inline struct gc_chunk *magic(void *mem)
}
// Aligns the amount of memory requested by the user with chunk size
static inline size_t align(size_t size)
/*
* Example: User requests 1 byte
* LHS
* requested size = 0b00000001
* alignment - 1 = 0b00000111
* => size + (alignment - 1) = 0b00001000
* RHS
* ~(alignment - 1) = 0b11111000
* LHS & RHS = 0b00001000
*/
// TODO: should probably be 16? would need to change chunk struct
static inline uint32_t align(uint32_t size)
{
const size_t alignment = 8;
size = (size + (alignment - 1)) & ~(alignment - 1);
size = (size + (ALIGN - 1)) & ~(ALIGN - 1);
// REVIEW: unnecessary with bit-wise math above?
if (size <= MIN)
return MIN;
else
return size;
}
struct gc_chunk *get_next_chunk(struct gc_chunk *curr)
struct gc_chunk *find_next_chunk(struct gc_chunk *curr)
{
unsigned size = curr->size;
return (struct gc_chunk *)((uint64_t *)curr + MAGIC + (size - 1));
}
struct gc_chunk *get_prev_chunk(struct gc_chunk *curr)
struct gc_chunk *find_prev_chunk(struct gc_chunk *curr)
{
assert(curr->merge);
unsigned size = curr->footer;
return (struct gc_chunk *)((uint64_t *)curr - (size - 1) - MAGIC);
}
void set_footer(struct gc_chunk *curr)
{
struct gc_chunk *next = find_next_chunk(curr);
next->footer = curr->size;
}
int get_footer(struct gc_chunk *curr)
{
struct gc_chunk *next = find_next_chunk(curr);
return next->footer;
}
struct gc_chunk *init_gc_chunk(void)
{
const size_t page_size = getpagesize();
const size_t len = page_size * sizeof(uint64_t);
struct gc_chunk *chunk =
(struct gc_chunk *)mmap(NULL, len, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (chunk == MAP_FAILED)
return NULL;
chunk->size = page_size - MAGIC - OVERHEAD;
chunk->merge = 0;
chunk->free = 1;
chunk->next = NULL;
chunk->prev = NULL;
struct gc_chunk *sentinel = find_next_chunk(chunk);
sentinel->footer = chunk->size;
sentinel->size = 0;
sentinel-> merge = 1;
sentinel->free = 1;
sentinel->next = NULL;
sentinel->prev = chunk;
return chunk;
}
struct gc_chunk *find_free_chunk(size_t size)
{
struct gc_chunk *found;
while (1) {
found = freelist;
while (found != NULL) {
if (found->size >= size) {
// found, TODO: split when appropriate
break;
}
found = found->next;
}
if (found == NULL) {
found = init_gc_chunk();
if (found == NULL) {
return found;
}
freelist = found;
break;
} else {
break;
}
}
return found;
}
void merge_chunk(struct gc_chunk *curr)
{
if (curr->merge) {
struct gc_chunk *prev = find_prev_chunk(curr);
struct gc_chunk *next = find_next_chunk(curr);
if (next->free) {
// TODO Case 1: merge prev and next
} else {
// TODO Case 2: merge with prev
}
set_footer(prev);
} else {
struct gc_chunk *next = find_next_chunk(curr);
if (next->free) {
// TODO Case 3: merge with after
} else {
// TODO Case 4: can't merge anything
}
curr->free = 1;
set_footer(curr);
}
}