diff --git a/freelist/Makefile b/freelist/Makefile new file mode 100644 index 0000000..7f6d8a6 --- /dev/null +++ b/freelist/Makefile @@ -0,0 +1,14 @@ +CC = gcc +CFLAGS = -Wall -O2 + +TARGET = main.elf +SRC = main.c freelist.c + +#LDFLAGS = + +$(TARGET): $(SRC) + @echo CC $@ + @$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + +clean: + rm -f $(TARGET) diff --git a/freelist/freelist.c b/freelist/freelist.c new file mode 100644 index 0000000..107dd63 --- /dev/null +++ b/freelist/freelist.c @@ -0,0 +1,119 @@ +#include "freelist.h" +#include "stdbool.h" +#include "stddef.h" +#include +#include +#include +#include +#include + +/* Fiddle these around according to your need. */ +#ifdef FREELIST_NOALIGN +#define ALIGN(x) (x) +#else // FREELIST_NOALIGN + +/* Align to nearest multiple of sizeof(void*) */ +static inline size_t align_up(size_t n) { + return (n + sizeof(void *) - 1) & ~(sizeof(void *) - 1); +} + +#define ALIGN(x) (align_up(x)) +#endif // FREELIST_NOALIGN + +/* Initialize the FreeList */ +int fl_init(FreeList *fl, uintptr_t start, uintptr_t end, size_t itemsize) { + size_t size = ALIGN(itemsize + sizeof(FreeListBlock)); + + if (!fl || end <= start) + return EXIT_FAILURE; + + fl->start = start; + fl->end = end; + fl->size = size; + fl->allocated = 0; + + FreeListBlock *block = (FreeListBlock *)start; + for (size_t i = 0; i < fl_capacity(fl); i++) { + block->next = (FreeListBlock *)((void *)block + size); + block->block_state = FL_FREE; + block = block->next; + } + + block->next = NULL; + + fl->free = (FreeListBlock *)start; + return EXIT_SUCCESS; +} + +/* Allocate some memory from the FreeList */ +void *fl_alloc(FreeList *fl) { + if (!fl->free || fl->free->block_state != FL_FREE) + return NULL; + + FreeListBlock *m = fl->free; + m->block_state = FL_USED; + + fl->free = fl->free->next; + fl->allocated++; + + return ((void *)m) + sizeof(FreeListBlock); +} + +/* Return some memory to the FreeList */ +int fl_free(FreeList *fl, void *ptr) { + if (!fl_is_managed(fl, ptr)) { + return EXIT_FAILURE; /* We cant free memory we do not own */ + } + + FreeListBlock *block = (FreeListBlock *)((uintptr_t)ptr - sizeof(FreeListBlock)); + + if (block->block_state != FL_USED) { + return EXIT_FAILURE; /* Block must be used */ + } + + block->block_state = FL_FREE; + block->next = fl->free; + fl->free = block; + fl->allocated--; + + return EXIT_SUCCESS; +} + +/* Returns how many slots are occupied */ +size_t fl_allocated(FreeList *fl) { + return fl->allocated; +} + +/* Returns how many free slots are available (O(1)) */ +size_t fl_available(FreeList *fl) { + return fl_capacity(fl) - fl->allocated; +} + +/* Returns the total amount of items the freelist will hold */ +size_t fl_capacity(FreeList *fl) { + return (fl->end - fl->start) / fl->size; +} + +/* Check if a piece of memory is managed by the FreeList */ +int fl_is_managed(FreeList *fl, void *ptr) { + return ((uintptr_t)ptr >= fl->start && (uintptr_t)ptr < fl->end); +} + +/* Returns the ratio of metadata versus data as a scalar in range 0..1 */ +float fl_utilization(FreeList *fl, size_t itemsize) { + return (float)itemsize / fl->size; +} + +/* Walks the free pages/slots, returns total count (O(n), given no cycles) */ +size_t fl_check(FreeList *fl) { + int avail = 0; + FreeListBlock *cursor = fl->free; + + while (cursor->next != NULL) { + avail++; + assert(cursor->block_state == FL_FREE); + cursor = cursor->next; + } + + return avail; +} diff --git a/freelist/freelist.h b/freelist/freelist.h new file mode 100644 index 0000000..885b58a --- /dev/null +++ b/freelist/freelist.h @@ -0,0 +1,34 @@ +#ifndef FREELIST_H +#define FREELIST_H + +#include +#include +#include + +#define FL_FREE ((uint8_t)0x00) +#define FL_USED ((uint8_t)0x01) + +typedef struct FreeListBlock { + struct FreeListBlock *next; + uint8_t block_state; +} FreeListBlock; + +typedef struct { + uintptr_t start; + uintptr_t end; + FreeListBlock *free; + size_t size; + size_t allocated; +} FreeList; + +int fl_init(FreeList *fl, uintptr_t start, uintptr_t end, size_t itemsize); +int fl_free(FreeList *fl, void *ptr); +int fl_is_managed(FreeList *fl, void *ptr); +void *fl_alloc(FreeList *fl); +size_t fl_check(FreeList *fl); +size_t fl_allocated(FreeList *fl); +size_t fl_available(FreeList *fl); +size_t fl_capacity(FreeList *fl); +float fl_utilization(FreeList *fl, size_t itemsize); + +#endif // FREELIST_H diff --git a/freelist/main.c b/freelist/main.c new file mode 100644 index 0000000..9d18438 --- /dev/null +++ b/freelist/main.c @@ -0,0 +1,85 @@ +#include "freelist.h" +#include +#include +#include +#include + +typedef struct { + int x, y, z; +} Vec3; + +Vec3 vec3_new(int a, int b, int c) { + return (Vec3){a, b, c}; +} + +// Print function +void printvec(const Vec3 *v) { + printf("Vec3: (%d, %d, %d)\n", v->x, v->y, v->z); +} + +int main() { + FreeList fl; + char *mem = malloc(4096); + + fl_init(&fl, (uintptr_t)mem, (uintptr_t)mem + 4096, sizeof(Vec3)); + size_t cap = fl_capacity(&fl); + assert(fl_available(&fl) == cap); + + printf("Space utilization: %.2f%%\n", 100.0 * fl_utilization(&fl, sizeof(Vec3))); + + Vec3 *a = fl_alloc(&fl); + Vec3 *b = fl_alloc(&fl); + Vec3 *c = fl_alloc(&fl); + + memset(a, 0x23, sizeof(Vec3)); + memset(b, 0x24, sizeof(Vec3)); + memset(c, 0x25, sizeof(Vec3)); + + *a = vec3_new(12, 13, 435); + *b = vec3_new(192, 199, 435); + *c = vec3_new(432, 11, 435); + + printvec(a); + printvec(b); + printvec(c); + + assert(fl_check(&fl) == cap - 3); + assert(fl_capacity(&fl) == fl_available(&fl) + 3); + + printf("Available: %zu of %zu\n", fl_available(&fl), fl_capacity(&fl)); + + assert(fl_free(&fl, a) == EXIT_SUCCESS); + assert(fl_free(&fl, b) == EXIT_SUCCESS); + assert(fl_free(&fl, c) == EXIT_SUCCESS); + assert(fl_free(&fl, a) == EXIT_FAILURE); // Double free + + printf("Available: %zu of %zu\n", fl_available(&fl), fl_capacity(&fl)); + + assert(fl_check(&fl) == fl_capacity(&fl) - fl_allocated(&fl)); + assert(fl_allocated(&fl) == 0); + + /* All memory is free here */ + + uintptr_t *ptr_buf = malloc(sizeof(uintptr_t) * fl_capacity(&fl)); + + /* Fill it up */ + for (int i = 0; i < fl_capacity(&fl); i++) { + ptr_buf[i] = (uintptr_t)fl_alloc(&fl); + } + + assert(0 == fl_available(&fl)); + assert(fl_allocated(&fl) == fl_capacity(&fl)); + assert(fl_check(&fl) == 0); + + /* Return it all */ + for (int i = 0; i < fl_capacity(&fl); i++) { + fl_free(&fl, (void *)ptr_buf[i]); + } + + assert(cap == fl_available(&fl)); + assert(fl_allocated(&fl) == 0); + assert(fl_check(&fl) == cap); + + printf("All tests passed!\n"); + return 0; +}