// ::Allocator::Globals:: FLAlloc FL_ALLOC = {0}; Allocator ALLOC = {0}; read_only FLNode FL_NIL_NODE = {0}; read_only FreeList FL_NIL_LIST = {0}; constexpr usize FL_GLOBAL_SIZE = MB(32); static b32 FL_GLOBAL_INIT = false; // ::Allocator::Util::Header:: static inline usize CalcPaddingWithHeader(uintptr ptr, uintptr alignment, usize header_size) { Assert(IsPow2(alignment), "Alignment provided to CalcPaddingWithHeader is not a power of two"); uintptr padding = CalcPadding(ptr, alignment); uintptr needed_space = (uintptr)header_size; if (padding < needed_space) { needed_space -= padding; if ((needed_space & (alignment-1)) != 0) padding += alignment * (1 + (needed_space/alignment)); else padding += alignment * (needed_space/alignment); } return (usize)padding; } static inline usize CalcPadding(uintptr ptr, uintptr alignment) { Assert(IsPow2(alignment), "CalcPadding failure: IsPow2 failed"); uintptr padding = 0; uintptr modulo = ptr & (alignment-1); if (modulo != 0) padding = alignment - modulo; return (usize)padding; } // ::Allocator::Arena::Start:: static Arena *ArenaInit(rawptr buffer, usize size) { Arena *arena = (Arena *)buffer; buffer = PtrAdd(buffer, ARENA_HEADER_SIZE); arena->buffer = buffer; arena->length = size; arena->pos = 0; return arena; } static Arena *ArenaCreate(usize size) { u8 *mem = pMemAllocZeroed(size); return ArenaInit(mem, size); } static Arena *ArenaCreateDebug(usize size, u32 init_line_no) { u8 *mem = pMemAllocZeroed(size); return ArenaInitDebug(mem, size, init_line_no); } // TODO: investigate overflows when out of memory because something bad is going on static rawptr ArenaAllocAlign(Arena *arena, usize size, usize align) { rawptr ptr = NULL; uintptr curr_ptr = (uintptr)arena->buffer + (uintptr)arena->pos; uintptr offset = AlignPow2(curr_ptr, align); offset -= (uintptr)arena->buffer; if (offset+size <= arena->length) { ptr = &arena->buffer[offset]; arena->pos = offset+size; } else { Printfln("Out of memory: %d", arena->init_line_no); Assert(0, "Memory Failure"); } return ptr; } static rawptr ArenaAlloc(Arena *arena, usize size) { return ArenaAllocAlign(arena, size, DEFAULT_ALIGNMENT); } static void ArenaFree(Arena *arena) { arena->pos = 0; } static void ArenaFreeZeroed(Arena *arena) { MemZero(arena->buffer, arena->pos); ArenaFree(arena); } static void DeallocArena(Arena *arena) { pMemFree(arena, arena->length); } static Arena * ArenaInitDebug(rawptr buffer, usize size, u32 init_line_no) { Arena *arena = ArenaInit(buffer, size); arena->init_line_no = init_line_no; return arena; } // ::Allocator::Arena::End:: // ::Allocator::GlobalAlloc::Start:: static void InitAllocator(usize init_size) { ALLOC.grow_size = init_size; ALLOC.buffer = pMemAllocZeroed(init_size); ALLOC.size = init_size; ALLOC.free_size = init_size; ALLOC.hash_table = FLMemAlloc(sizeof(HashTable)); HashTableInit(ALLOC.hash_table, 32); ALLOC.tree = FLMemAlloc(sizeof(RBTree)); RBTreeInit(ALLOC.tree); RBTreeInsert(ALLOC.tree, init_size, ALLOC.buffer); } static void DeinitAlloc() { pMemFree(ALLOC.buffer, ALLOC.size); } static rawptr Alloc(usize size) { return AllocAlign(size, DEFAULT_ALIGNMENT); } static rawptr AllocAlign(usize size, usize alignment) { if (size == 0) return NULL; RBNode *node = P_RB_NIL; rawptr mem = NULL; if (!RBTreeSearchNearest(ALLOC.tree, size + alignment, &node)) { AllocGrow(size); RBTreeSearchNearest(ALLOC.tree, size + alignment, &node); } u64 alloc_size = node->key; rawptr free_alloc = node->bucket.last->data; RBTreeDelete(ALLOC.tree, alloc_size, free_alloc); usize padding = CalcPadding(uintptr(free_alloc), alignment); uintptr new_addr = uintptr(free_alloc) + size + padding; RBTreeInsert(ALLOC.tree, alloc_size - size - padding, rawptr(new_addr)); HashTablePushRawptrU64(ALLOC.hash_table, free_alloc, size + padding); return free_alloc; } // TODO: finish allocator // need an idea static void Free(rawptr ptr) { if (ptr == NULL) return; } static void AllocGrow(usize size) { usize grow_size = size < ALLOC.grow_size ? ALLOC.grow_size : ALLOC.grow_size + size; pMemRealloc(ALLOC.buffer, ALLOC.size, ALLOC.size + grow_size); RBTreeInsert(ALLOC.tree, grow_size, ALLOC.buffer + ALLOC.size); // TODO: check this if things fuck up it could be wrong ALLOC.size += grow_size; ALLOC.free_size += grow_size; } // ::Allocator::GlobalAlloc::End:: // ::Allocator::FreeList::Start:: static void GlobalFreeListInit(usize size) { FreeListInit(&FL_ALLOC, size); FL_GLOBAL_INIT = true; } static rawptr FLMemAlloc(usize size) { if (!FL_GLOBAL_INIT) { GlobalFreeListInit(FL_GLOBAL_SIZE); } return FreeListAlloc(&FL_ALLOC, size); } static rawptr FLMemAllocZeroed(usize size) { rawptr ptr = FLMemAlloc(size); MemZero(ptr, size); return ptr; } static rawptr FLMemRealloc(rawptr old_ptr, usize size) { if (!FL_GLOBAL_INIT) { GlobalFreeListInit(FL_GLOBAL_SIZE); } rawptr ptr = NULL; if (old_ptr == NULL) ptr = FLMemAlloc(size); else ptr = FreeListRealloc(&FL_ALLOC, old_ptr, size); return ptr; } static void FLMemFree(rawptr ptr) { if (!FL_GLOBAL_INIT) { GlobalFreeListInit(FL_GLOBAL_SIZE); } FreeListFree(&FL_ALLOC, ptr); } /* * Should be async safe given no other thread frees a pointer in the middle of a realloc */ static rawptr FreeListRealloc(FLAlloc *alloc, rawptr old_ptr, usize size) { rawptr ptr = FreeListAlloc(alloc, size); usize old_size = FreeListPtrSize(alloc, old_ptr); MemCpy(ptr, old_ptr, old_size); FreeListFree(alloc, old_ptr); return ptr; } static usize FreeListPtrSize(FLAlloc *alloc, rawptr ptr) { if (ptr == NULL) return 0; usize size = 0; TicketMutLock(&alloc->mut); FreeList *list = _FreeListFindList(alloc, ptr); if (list != NULL) { FLAllocHeader *header = cast(FLAllocHeader *, u8ptr(ptr) - sizeof(FLAllocHeader)); size = header->size + header->padding; } TicketMutUnlock(&alloc->mut); return size; } static void FreeListInit(FLAlloc *alloc, usize size) { alloc->list_head = pMemAllocZeroed(sizeof(FreeList)); alloc->nil = &FL_NIL_NODE; alloc->grow_size = size; _FreeListInit(&alloc->list_head, size); FreeListFreeAll(alloc); } static void _FreeListInit(FreeList **alloc, usize size) { *alloc = (FreeList *)pMemAllocZeroed(size); (*alloc)->data = (rawptr)(((uintptr)*alloc) + ((uintptr)sizeof(FreeList))); (*alloc)->size = size; (*alloc)->used = sizeof(FreeList); (*alloc)->next = &FL_NIL_LIST; } static void FreeListFreeAll(FLAlloc *alloc) { TicketMutLock(&alloc->mut); FreeList *current = alloc->list_head->next; FreeList *next = &FL_NIL_LIST; while (current != &FL_NIL_LIST) { next = current->next; pMemFree(current, current->size); current = next; } current = alloc->list_head; FLNode *node = (FLNode *)current->data; node->size = current->size; node->next = &FL_NIL_NODE; current->head = node; current->used = sizeof(FreeList); TicketMutUnlock(&alloc->mut); } static FLNode *FreeListSearch(FreeList *alloc, usize size, usize alignment, usize *out_padding, FLNode **prev_node) { FLNode *node = alloc->head; FLNode *prev = &FL_NIL_NODE; usize padding = 0; while (node != &FL_NIL_NODE) { padding = CalcPaddingWithHeader((uintptr)node, (uintptr)alignment, sizeof(FLNode)); usize required_size = size + padding; if (node->size >= required_size) break; prev = node; node = node->next; } if (out_padding) *out_padding = padding; if (prev_node) *prev_node = prev; return node; } /* * NOT SAFE TO CALL OUTSIDE OF FreeListAlloc */ static FreeList *FreeListGrow(FLAlloc *alloc, usize alloc_size) { usize grow_size = alloc->grow_size; if (alloc_size > grow_size) grow_size += alloc_size; FreeList *list = NULL; _FreeListInit(&list, grow_size); FLNode *node = (FLNode *)list->data; node->size = list->size; node->next = alloc->nil; list->head = node; FreeList *current = alloc->list_head; while (current != &FL_NIL_LIST) { if (current->next == &FL_NIL_LIST) { current->next = list; break; } current = current->next; } return list; } static rawptr _FreeListAllocAlign(FreeList *alloc, usize size, usize alignment) { if (size == 0) return NULL; usize padding = 0; FLNode *prev_node = &FL_NIL_NODE; FLAllocHeader *header; if (size < sizeof(FLNode)) size = sizeof(FLNode); if (alignment < 8) alignment = 8; FLNode *node = FreeListSearch(alloc, size, alignment, &padding, &prev_node); if (node == &FL_NIL_NODE) Assert(0, "FreeListAllocAlign failed to allocate, oom"); usize alignment_padding = padding - sizeof(FLAllocHeader); usize required_space = size + padding; usize remaining = node->size - required_space; if (remaining > 0) { FLNode *new_node = (FLNode *)(((c8 *)node) + required_space); new_node->size = remaining; FreeListInsert(&alloc->head, node, new_node); } FreeListRemove(&alloc->head, prev_node, node); header = (FLAllocHeader *)(((c8 *)node) + alignment_padding); header->size = required_space; header->padding = alignment_padding; alloc->used += required_space; return (rawptr)(((c8 *)header) + sizeof(FLAllocHeader)); } static rawptr FreeListAlloc(FLAlloc *alloc, usize size) { return FreeListAllocAlign(alloc, size, DEFAULT_ALIGNMENT); } static rawptr FreeListAllocAlign(FLAlloc *alloc, usize size, usize alignment) { TicketMutLock(&alloc->mut); FreeList *fl = &FL_NIL_LIST; FreeList *current = alloc->list_head; while (current != &FL_NIL_LIST) { usize remaining = current->size - current->used; if (size < remaining) { fl = current; break; } current = current->next; } if (fl == &FL_NIL_LIST) { fl = FreeListGrow(alloc, size); } rawptr ptr = _FreeListAllocAlign(fl, size, alignment); TicketMutUnlock(&alloc->mut); return ptr; } static FreeList *_FreeListFindList(FLAlloc *alloc, rawptr ptr) { FreeList *list = NULL; FreeList *current = alloc->list_head; while (current != &FL_NIL_LIST) { uintptr ptr_addr = uintptr(ptr); uintptr start = uintptr(current->data); uintptr end = uintptr(current->data) + uintptr(current->size); if (ptr_addr >= start && ptr_addr < end) { list = current; break; } } return list; } static void _FreeListFree(FreeList *alloc, rawptr ptr) { FLAllocHeader *header = cast(FLAllocHeader *, u8ptr(ptr) - sizeof(FLAllocHeader)); FLNode *free_node = cast(FLNode *,header); free_node->size = header->size + header->padding; free_node->next = &FL_NIL_NODE; FLNode *prev_node = &FL_NIL_NODE; FLNode *node = alloc->head; while (node != &FL_NIL_NODE) { if (ptr < node) { FreeListInsert(&alloc->head, prev_node, free_node); break; } prev_node = node; node = node->next; } alloc->used -= free_node->size; FreeListCoalescence(alloc, prev_node, free_node); } static void FreeListFree(FLAlloc *alloc, rawptr ptr) { if (ptr == NULL) return; TicketMutLock(&alloc->mut); FreeList *list = _FreeListFindList(alloc, ptr); if (list != NULL) { _FreeListFree(list, ptr); } TicketMutUnlock(&alloc->mut); } static void FreeListCoalescence(FreeList *alloc, FLNode *prev_node, FLNode *free_node) { if (free_node->next != &FL_NIL_NODE && (rawptr)(((c8 *)free_node) + free_node->size) == free_node->next) { free_node->size += free_node->next->size; FreeListRemove(&alloc->head, free_node, free_node->next); } if (prev_node->next != &FL_NIL_NODE && (rawptr)(((c8 *)prev_node) + prev_node->size) == free_node) { prev_node->size += free_node->next->size; FreeListRemove(&alloc->head, prev_node, free_node); } } static void FreeListRemove(FLNode **head, FLNode *prev_node, FLNode *del_node) { if (prev_node == &FL_NIL_NODE) *head = del_node->next; else prev_node->next = del_node->next; } static void FreeListInsert(FLNode **head, FLNode *prev_node, FLNode *new_node) { if (prev_node == &FL_NIL_NODE) { if (*head != &FL_NIL_NODE) { new_node->next = *head; *head = new_node; } else *head = new_node; } else { new_node->next = prev_node->next; prev_node->next = new_node; } } // ::Allocator::FreeList::End::