#include //i guess we could figure out which parts of the pages the kernel allocated to //load the application were not actually needed and add those to the memory //that is available to be allocated, but whatever. namespace euler::allocator { struct block_info { //vaddr, divisible by size uint64_t start; //power of 2, 0 for unused uint64_t size; }; struct block_info_page { block_info infos[255]; block_info_page *next; }; static_assert(sizeof(block_info_page) == 4088); static block_info_page *first_bi_page = 0; static block_info *try_find_block(uint64_t start, uint64_t size) { for (block_info_page *bip = first_bi_page; bip; bip = bip->next) for (int i = 0; i < 255; ++i) if (bip->infos[i].start == start && bip->infos[i].size == size) return bip->infos + i; return 0; } static block_info *add_block(uint64_t start, uint64_t size) { for (block_info_page *bip = first_bi_page; bip; bip = bip->next) for (int i = 0; i < 255; ++i) if (bip->infos[i].size == 0) { bip->infos[i].start = start; bip->infos[i].size = size; return bip->infos + i; } block_info_page *new_bip = (block_info_page *)_syscall_get_new_pages(1); new_bip->infos[0].start = start; new_bip->infos[0].size = size; for (int i = 1; i < 255; ++i) new_bip->infos[i].size = 0; new_bip->next = first_bi_page; first_bi_page = new_bip; return new_bip->infos; } static block_info *find_minimal_block(uint64_t minimum_length) { block_info *minimal_so_far = 0; uint64_t length_so_far = UINT64_MAX; for (block_info_page *bip = first_bi_page; bip; bip = bip->next) for (int i = 0; i < 255; ++i) if (bip->infos[i].size == minimum_length) return bip->infos + i; else if ( bip->infos[i].size > minimum_length && bip->infos[i].size < length_so_far ) { minimal_so_far = bip->infos + i; length_so_far = bip->infos[i].size; } if (minimal_so_far != 0) return minimal_so_far; uint64_t pages_needed = (minimum_length - 1) / 4096 + 1; void *block_start = _syscall_get_new_pages(pages_needed); return add_block((uint64_t)block_start, pages_needed * 4096); } static void deallocate_aligned(uint64_t start, uint64_t length) { block_info *buddy = try_find_block(start ^ length, length); if (buddy) { buddy->size = 0; deallocate_aligned(start & ~length, length * 2); return; } add_block(start, length); } static void deallocate(void *start, uint64_t length) { uint64_t at = (uint64_t)start; uint64_t left = length; uint64_t i; for (i = 1; i <= left; i *= 2) if (at & i) { deallocate_aligned(at, i); at += i; left -= i; } for (i /= 2; left > 0; i /= 2) if (i <= left) { deallocate_aligned(at, i); at += i; left -= i; } } [[nodiscard]] static void *allocate(uint64_t length) { block_info *bi = find_minimal_block(length); void *to_return = (void *)bi->start; void *new_start = (void *)(bi->start + length); uint64_t new_length = bi->size - length; bi->size = 0; deallocate(new_start, new_length); return to_return; } static void deallocate_with_length(void *start) { uint64_t real_start = (uint64_t)start - 8; deallocate((void *)real_start, *(uint64_t *)real_start); } [[nodiscard]] static void *allocate_with_length(uint64_t length) { uint64_t *real_start = (uint64_t *)allocate(length + 8); *real_start = length + 8; return real_start + 1; } } using namespace euler::allocator; void operator delete[](void *start) { deallocate_with_length(start); } void operator delete(void *start) { deallocate_with_length(start); } void operator delete[](void *start, uint64_t) { deallocate_with_length(start); } void operator delete(void *start, uint64_t) { deallocate_with_length(start); } void *operator new[](uint64_t size) { return allocate_with_length(size); } void *operator new(uint64_t size) { return allocate_with_length(size); }