// vim:fileencoding=utf-8:foldmethod=marker #include "mem_arena.h" #include "../../mem/mem_os.h" #include "../../../common/aliases/aliases.h" #include "../../../common/assert/assert.h" #include "../../../common/misc/misc_utils.h" #include "../../../base/mem/utils/mem_utils.h" #include #ifndef DEFAULT_ALIGNMENT // Why 2 * sizeof(void *) instead of sizeof(void *) // https://handmade.network/forums/t/6860-alignment_arena_allocator #define DEFAULT_ALIGNMENT (2 * sizeof(void *)) #endif /* ifndef DEFAULT_ALIGNMENT */ #define ARENA_MINIMUM_CAPACITY KiB(16) // Allocate minimum of 4 pages typedef enum { ARENA_STORAGE_TYPE_ALLOCATED, ARENA_STORAGE_TYPE_BUFFER, } ArenaStorageType; struct WpArena { u8 *buf; u8 *offset; u8 *prev_offset; u64 capacity; ArenaStorageType type; b8 committed; wpMiscUtilsReservePadding(sizeof(u8 *) * 3 + sizeof(u64) + sizeof(ArenaStorageType) + sizeof(b8)); }; b8 wpMemArenaInitBuffer(WpArena **arena, u8 *buffer, u64 buffer_size) { if (!arena || *arena || buffer_size < sizeof(WpArena)) { return false; } *arena = (WpArena *)buffer; WpArena *arena_ptr = *arena; arena_ptr->buf = (u8 *)(arena_ptr + 1); arena_ptr->offset = arena_ptr->buf; arena_ptr->prev_offset = NULL; arena_ptr->capacity = buffer_size - sizeof(WpArena); arena_ptr->type = ARENA_STORAGE_TYPE_BUFFER; arena_ptr->committed = true; return true; } b8 wpMemArenaInitAllocatedCustom(WpArena **arena, u64 base_capacity, WpMemAllocFlags flags, b8 zero_buffer) { if (!arena || *arena || base_capacity == 0) { return false; } u64 size = sizeof(WpArena) + (base_capacity >= ARENA_MINIMUM_CAPACITY ? base_capacity : ARENA_MINIMUM_CAPACITY); u64 alloc_size = wpMiscUtilsU64RoundUpPow2(size); u8 *allocated = (u8 *)wpOsMemAlloc(NULL, alloc_size, WP_MEM_ACCESS_READ_WRITE, flags, zero_buffer ? WP_MEM_INIT_INITIALISED : WP_MEM_INIT_UNINITIALISED); if (!allocated) { return false; } b8 committed = (flags & WP_MEM_ALLOC_COMMIT) == WP_MEM_ALLOC_COMMIT; #ifdef WP_PLATFORM_WINDOWS if (!committed) { wpOsMemAlloc(allocated, sizeof(WpArena), WP_MEM_ACCESS_READ_WRITE, WP_MEM_ALLOC_COMMIT, WP_MEM_INIT_INITIALISED); } #endif // ifdef WP_PLATFORM_WINDOWS if (!wpMemArenaInitBuffer(arena, allocated, alloc_size)) { wpMemArenaDestroy(arena); return false; } WpArena *arena_ptr = *arena; arena_ptr->type = ARENA_STORAGE_TYPE_ALLOCATED; arena_ptr->committed = committed; return true; } void *wpMemArenaAlloc(WpArena *arena, u64 size) { return wpMemArenaAllocAligned(arena, size, DEFAULT_ALIGNMENT); } void *wpMemArenaAllocAligned(WpArena *arena, u64 size, u64 alignment) { wpDebugAssert(arena != NULL, "`arena` should not be NULL"); u8 *alloc_start = arena->offset; u8 *output = wpMemUtilAlignForward((void *)alloc_start, alignment); if (output + size >= arena->buf + arena->capacity) { return NULL; } arena->offset = output + size; #ifdef WP_PLATFORM_WINDOWS if (arena->type == ARENA_STORAGE_TYPE_ALLOCATED && !(arena->committed)) { wpOsMemAlloc(alloc_start, (uptr)(arena->offset) - (uptr)(alloc_start), WP_MEM_ACCESS_READ_WRITE, WP_MEM_ALLOC_COMMIT, WP_MEM_INIT_UNINITIALISED); } #endif // ifdef WP_PLATFORM_WINDOWS memset(output, 0, size); return (void *)output; } void *wpMemArenaRealloc(WpArena *arena, void *ptr, u64 old_size, u64 new_size) { wpDebugAssert(arena != NULL, "`arena` should not be NULL"); if ((u8*)ptr < arena->buf || (u8*)ptr > arena->offset || arena->offset + new_size >= arena->buf + arena->capacity) { return NULL; } void *new_ptr = wpMemArenaAlloc(arena, new_size); if (!new_ptr) { return NULL; } u64 copy_size = new_size <= old_size ? new_size : old_size; memcpy(new_ptr, ptr, copy_size); return new_ptr; } void *wpMemArenaReallocAligned(WpArena *arena, void *ptr, u64 old_size, u64 new_size, u64 alignment) { wpDebugAssert(arena != NULL, "`arena` should not be NULL"); if ((u8*)ptr < arena->buf || (u8*)ptr > arena->offset || arena->offset + new_size >= arena->buf + arena->capacity) { return NULL; } void *new_ptr = wpMemArenaAllocAligned(arena, new_size, alignment); if (!new_ptr) { return NULL; } u64 copy_size = new_size <= old_size ? new_size : old_size; memcpy(new_ptr, ptr, copy_size); return new_ptr; } void wpMemArenaTempBegin(WpArena *arena) { wpDebugAssert(arena != NULL, "`arena` should not be NULL"); if (arena->prev_offset != NULL) { return; } arena->prev_offset = arena->offset; } void wpMemArenaTempEnd(WpArena *arena) { wpDebugAssert(arena != NULL, "`arena` should not be NULL"); if (arena->prev_offset == NULL) { return; } arena->offset = arena->prev_offset; arena->prev_offset = NULL; } void wpMemArenaClear(WpArena *arena) { wpDebugAssert(arena != NULL, "`arena` should not be NULL"); memset(arena->buf, 0, arena->offset - arena->buf); arena->offset = arena->buf; } void wpMemArenaDestroy(WpArena **arena) { wpDebugAssert(arena != NULL && (*arena) != NULL, "`arena` double pointer is not valid"); WpArena *arena_ptr = *arena; if (arena_ptr->type == ARENA_STORAGE_TYPE_ALLOCATED) { wpOsMemFree(*arena, sizeof(WpArena) + arena_ptr->capacity); } *arena = NULL; }