diff --git a/Makefile b/Makefile index a264171..e89714c 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ dynamic : $(BIN_DIR)/libarena.so tests : test/test_arena $(TEST_DIR)/test_arena : src/test_arena.c $(BIN_DIR)/libarena.a | $(TEST_DIR) - $(CC) -o $@ $(CFLAGS) $^ + $(CC) -o $@ -O0 -ggdb $^ $(BIN_DIR)/libarena.a : $(OBJS) | $(BIN_DIR) ar cr $@ $^ diff --git a/include/arena.h b/include/arena.h index ece1fe5..68b5e2f 100644 --- a/include/arena.h +++ b/include/arena.h @@ -3,15 +3,17 @@ #define ARENA_H #include // ptrdiff_t +#include // uintptr_t -typedef unsigned char byte_t; +#define _WORD_SIZE (sizeof(intptr_t)) +// linked list terminology: +// https://web.archive.org/web/20230604145332/https://i.stack.imgur.com/2FbXf.png typedef struct arena { - size_t next, prev, cap; - void* data; -} __attribute__((aligned(sizeof(void*)))) arena_t; + struct page *head, *last; +} __attribute__((aligned(_WORD_SIZE))) arena_t; -arena_t arena_new(); +arena_t arena_new(void); void arena_reset(arena_t* a); void* arena_alloc(arena_t* a, size_t len); void* arena_calloc(arena_t* a, size_t nmemb, size_t size); diff --git a/src/arena.c b/src/arena.c index 0a6a67c..d1cc6ef 100644 --- a/src/arena.c +++ b/src/arena.c @@ -9,76 +9,125 @@ // (sizeof(intptr_t) isn't guaranteed to be the machine word size but on most // compilers it is) -#define WORD_SIZE (sizeof(intptr_t)) +#define SYS_PAGE_SIZE ((size_t)sysconf(_SC_PAGE_SIZE)) -#define ARENA_SIZE ((size_t)(1 * sysconf(_SC_PAGE_SIZE))) +typedef unsigned char byte_t; -arena_t arena_new() +struct page { + void* data; + size_t offset, prev_offset; + struct page* next; +}; + +// linked list terminology: +// https://web.archive.org/web/20230604145332/https://i.stack.imgur.com/2FbXf.png + +arena_t arena_new(void) { - arena_t a = { - .next = 0, - .prev = 0, - .cap = ARENA_SIZE, - .data = malloc(ARENA_SIZE) - }; + struct page* p = malloc(sizeof(struct page)); - if (a.data == NULL) + if (p == NULL) exit(errno); + p->next = NULL; + p->offset = 0; + p->prev_offset = 0; + p->data = malloc(SYS_PAGE_SIZE); + + if (p->data == NULL) + exit(errno); + + arena_t a = { + .head = p, + .last = p, + }; + return a; } -void _arena_realloc_or_panic(arena_t* a, size_t len) +void _arena_new_page(arena_t* a, size_t size) { - // TODO: gracefully recover. - // although unlikely to be recoverable in most use cases - a->data = realloc(a->data, len); + if (a->head->next != NULL) { + a->head = a->head->next; + a->head->offset = 0; + a->head->prev_offset = 0; + return; + } - if (a->data == NULL) + a->head->next = calloc(1, sizeof *(a->head)); + + if (a->head->next == NULL) exit(errno); - a->cap = len; + a->head = a->head->next; + a->head->offset = 0; + a->head->prev_offset = 0; + a->head->next = NULL; + a->head->data = malloc(size); + + if (a->head->data == NULL) + exit(errno); } void arena_reset(arena_t* a) { - a->next = 0; - a->prev = 0; + a->head = a->last; + a->head->offset = 0; + a->head->prev_offset = 0; } -void* arena_alloc(arena_t* a, size_t len) +void* _arena_big_alloc(arena_t* a, size_t size) { - // align len to machine word size - len = (len + WORD_SIZE - 1) & ~(WORD_SIZE - 1); + _arena_new_page(a, size); + a->head->offset = SIZE_MAX; + a->head->prev_offset = SIZE_MAX; - a->prev = a->next; - a->next += len; + return a->head->data; +} - if (a->next > a->cap) - _arena_realloc_or_panic(a, a->cap * 2); +void* arena_alloc(arena_t* a, size_t size) +{ + // align size to machine word size + size = (size + _WORD_SIZE - 1) & ~(_WORD_SIZE - 1); - return (byte_t*)(a->data) + a->prev; + if (size > SYS_PAGE_SIZE) + return _arena_big_alloc(a, size); + + a->head->prev_offset = a->head->offset; + a->head->offset += size; + + if (a->head->offset > SYS_PAGE_SIZE) { + _arena_new_page(a, SYS_PAGE_SIZE); + return arena_alloc(a, size); + } + + return (byte_t*)(a->head->data) + a->head->prev_offset; } void* arena_calloc(arena_t* a, size_t nmemb, size_t size) { void* p = arena_alloc(a, nmemb * size); - memset((byte_t*)(a->data) + a->prev, 0, size); + memset((byte_t*)p, 0, size); return p; } void* arena_realloc_tail(arena_t* a, size_t len) { - a->next = a->prev; + a->head->offset = a->head->prev_offset; return arena_alloc(a, len); } void arena_delete(arena_t* a) { - free(a->data); - arena_reset(a); - a->data = NULL; + struct page* p = a->last; + + while (p != NULL) { + struct page* next = p->next; + free(p->data); + free(p); + p = next; + } } // included for completeness diff --git a/src/test_arena.c b/src/test_arena.c index 93c66f4..d44125e 100644 --- a/src/test_arena.c +++ b/src/test_arena.c @@ -7,26 +7,34 @@ #include #include #include - -static struct arena default_arena = { 0 }; +#include int main() { - default_arena = arena_new(); + arena_t default_arena = arena_new(); - printf("\nAttempt to do allocations of 1 byte, cap is %zu, ", default_arena.cap); + printf("\nAttempting to allocate and write to 1 byte 1024 times"); + for (size_t i = 0; i < 1024; i++) { + char* c = arena_alloc(&default_arena, sizeof *c); - size_t i; - for (i = 0; default_arena.next < default_arena.cap; i++) { - char* c = arena_alloc(&default_arena, 1); - *c = i & 0xff; - if (c == NULL) { + *c = i & 0xFF; + + if (c == NULL) err(EXIT_FAILURE, "failed to allocate memory"); - } } - printf("did %zu allocations", i); + printf("\n OK!\n"); - printf("\n OK"); + printf("\nAttempting to allocate and write to `_SC_PAGESIZE+1` bytes"); + size_t psz = sysconf(_SC_PAGESIZE) + 1; + char* c = arena_alloc(&default_arena, psz); + + if (c == NULL) + err(EXIT_FAILURE, "failed to allocate memory"); + + for (size_t i = 0; i < psz; i++) + *c = i & 0xFF; + + printf("\n OK!\n"); return 0; }