diff options
Diffstat (limited to 'memory.c')
-rw-r--r-- | memory.c | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..0d9fe13 --- /dev/null +++ b/memory.c @@ -0,0 +1,275 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License Version 2 for + * details. + + * You may distribute and modify this code under the terms of the GNU General + * Public License Version 2, which you should have received a copy of along with + * this program. If not, please go to <https://www.gnu.org/licenses/>. + + * Created: 2025-04-05 + * Description: Implementations for memory models. + */ + +#include "./memory.h" + +#include <malloc.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +page_t *page_create(u64 size) +{ + size = MAX(size, PAGE_DEFAULT_SIZE); + page_t *page = calloc(1, sizeof(*page) + size); + page->next = NULL; + page->size = 0; + page->capacity = size; + return page; +} + +void page_resize(page_t **page, u64 size) +{ + if (!page) + return; + else if (!(*page)) + *page = page_create(size); + else if (page[0]->capacity < size) + { + page[0]->capacity = MAX(size, page[0]->capacity * 1.5); + page[0] = realloc(page[0], sizeof(*page[0]) + page[0]->capacity); + } +} + +i64 page_append(page_t *page, void *data, u64 size) +{ + if (!page || page->size + size >= page->capacity) + return -1; + if (data) + memcpy(page->data + page->size, data, size); + u64 ptr = page->size; + page->size += size; + return ptr; +} + +u64 page_rappend(page_t **page, void *data, u64 size) +{ + page_resize(page, page[0]->size + size); + if (data) + memcpy(page[0]->data + page[0]->size, data, size); + u64 ptr = page[0]->size; + page[0]->size += size; + return ptr; +} + +void *arena_alloc(arena_t *arena, u64 size) +{ + if (!arena) + return NULL; + else if (!arena->start) + { + arena->start = page_create(1); + arena->end = arena->start; + } + + page_t *best_fit; + for (best_fit = arena->start; best_fit; best_fit = best_fit->next) + if (best_fit->size + size + MEMORY_ALIGNMENT < best_fit->capacity) + break; + + if (!best_fit) + { + best_fit = page_create(size); + arena->end->next = best_fit; + arena->end = best_fit; + } + + // NOTE: Fsanitize has a hissy fit if we don't align memory "correctly" + u64 offset = 0; + + if (size % MEMORY_ALIGNMENT == 0) + { + u64 div = (((u64)(best_fit->data + best_fit->size)) % MEMORY_ALIGNMENT); + if (div != 0) + offset = MEMORY_ALIGNMENT - div; + } + + void *start = best_fit->data + best_fit->size + offset; + + best_fit->size += size + offset; + + return start; +} + +void *arena_realloc(arena_t *arena, void *ptr, u64 oldsize, u64 newsize) +{ + if (!ptr) + return arena_alloc(arena, newsize); + else if (!arena || !arena->start) + return NULL; + else if (newsize <= oldsize) + // No need to change anything. + return ptr; + + bool copy_into_new = true; + void *start = NULL; + page_t *old_page = NULL, *best_fit = NULL; + + for (page_t *page = arena->start; page; page = page->next) + { + if (!best_fit && page->size + newsize < page->capacity) + best_fit = page; + if (page->data <= (u8 *)ptr && + (u8 *)(ptr) + oldsize <= page->data + page->capacity) + old_page = page; + } + + // If the old page exists, ptr is the latest allocation in it, and it has + // enough space to contain the new size, just resize and return. + if (old_page && old_page->data + old_page->size - oldsize == ptr && + old_page->size - oldsize + newsize < old_page->capacity) + { + start = ptr; + old_page->size += newsize - oldsize; + copy_into_new = false; + } + else + { + if (!old_page) + copy_into_new = false; + if (!best_fit) + { + best_fit = page_create(newsize); + arena->end->next = best_fit; + arena->end = best_fit; + } + + start = best_fit->data + best_fit->size; + best_fit->size += newsize; + } + + if (copy_into_new) + { + memcpy(start, ptr, oldsize); + memset(start + oldsize, 0, newsize - oldsize); + } + + return start; +} + +void arena_attach(arena_t *arena, page_t *page) +{ + if (!arena || !page) + return; + else if (!arena->start || !arena->end) + { + arena->start = page; + arena->end = page; + } + else + { + page->next = arena->start; + arena->start = page; + } +} + +void arena_reset(arena_t *arena) +{ + if (!arena || !arena->start) + return; + + for (page_t *cur = arena->start; cur; cur = cur->next) + { + if (cur->size == 0) + continue; + cur->size = 0; + memset(cur->data, 0, cur->capacity); + } +} + +void arena_cleanup(arena_t *arena) +{ + if (!arena || !arena->start) + return; + + for (page_t *cur = arena->start, *next = NULL; cur; + next = cur->next, free(cur), cur = next) + continue; + + memset(arena, 0, sizeof(*arena)); +} + +// Allocates against stable memory i.e. we can have pointers of this lying +// around without any fear of them being thrown away. +void *context_alloc(context_t *context, u64 size) +{ + return arena_alloc(&context->memory, size); +} + +// Allocate against a "scratch space", separate from main memory, for internal +// use. +void *context_salloc(context_t *context, u64 size) +{ + return arena_alloc(&context->scratch, size); +} + +void context_reset_read(context_t *context) +{ + arena_reset(&context->read); +} + +void context_reset_scratch(context_t *context) +{ + arena_reset(&context->scratch); +} + +void context_reset(context_t *context) +{ + arena_reset(&context->memory); + arena_reset(&context->read); + arena_reset(&context->scratch); +} + +void context_cleanup(context_t *context) +{ + if (!context) + return; + arena_cleanup(&context->memory); + arena_cleanup(&context->read); + arena_cleanup(&context->scratch); + memset(context, 0, sizeof(*context)); +} + +void context_report(context_t *context) +{ +#if DEBUG + // Figure this out at runtime + u64 mem_used = 0, mem_cap = 0; + for (page_t *page = context->memory.start; page; page = page->next) + { + mem_used += page->size; + mem_cap += page->capacity; + } + + u64 read_used = 0, read_cap = 0; + for (page_t *page = context->read.start; page; page = page->next) + { + read_used += page->size; + read_cap += page->capacity; + } + + u64 scr_used = 0, scr_cap = 0; + for (page_t *page = context->scratch.start; page; page = page->next) + { + scr_used += page->size; + scr_cap += page->capacity; + } + + info("<Context>: %luB/%luB main memory used\n", mem_used, mem_cap); + info("<Context>: %luB/%luB read space used\n", read_used, read_cap); + info("<Context>: %luB/%luB scratch space used\n", scr_used, scr_cap); +#endif +} |