#include #include "internal.h" typedef struct ArenaPage page_t; typedef const page_t* page_iptr_t; typedef page_t* page_mptr_t; struct ArenaPage { page_mptr_t next; malunal_uint32_t used; malunal_uint32_t size; malunal_int8_t data[]; }; allocation_result_t create_page( allocator_mptr_t allocator, malunal_size_t size ) { malunal_size_t rounded = align_to_page(size); allocation_result_t result = allocator_acquire(allocator, rounded); if (result.threw) return result; page_mptr_t page = result.address; page->size = rounded; page->used = sizeof(page_t); page->next = NULL_ADDRESS; return result; } allocation_error_t delete_page( allocator_mptr_t allocator, page_mptr_t page ) { if (allocator == NULL_ADDRESS) return ALLOCATION_ERROR_NULL_UPSTREAM; if (page == NULL_ADDRESS) return ALLOCATION_ERROR_FAILURE; allocation_error_t err = delete_page(allocator, page->next); return err == ALLOCATION_ERROR_SUCCESS ? allocator_release(allocator, page, page->size) : err; } allocation_result_t page_acquire( page_mptr_t page, malunal_size_t size ) { malunal_uint32_t offset = page->used - sizeof(page_t); malunal_mptr_t result = page->data + offset; page->used += size; return (allocation_result_t) { .threw = 0, .address = result }; } static allocation_error_t arena_initialize( impl_mptr_t allocator, allocator_mptr_t upstream, malunal_size_t capacity ) { if (upstream == NULL_ADDRESS) return ALLOCATION_ERROR_NULL_UPSTREAM; allocation_result_t result = create_page(upstream, capacity); page_mptr_t currpage = !result.threw ? result.address : NULL_ADDRESS; allocator->upstream = upstream; allocator->context = currpage; allocator->allocated = currpage->used; allocator->reserved = currpage->size; return ALLOCATION_ERROR_SUCCESS; } static allocation_error_t arena_finalize(impl_mptr_t allocator) { delete_page(allocator->upstream, allocator->context); } static allocation_result_t arena_acquire( impl_mptr_t allocator, malunal_size_t size ) { if (allocator->context == NULL_ADDRESS) return (allocation_result_t) { .threw = 1, .error = ALLOCATION_ERROR_NULL_CONTEXT }; struct ArenaPage* page = allocator->context; if (size > page->size) return (allocation_result_t) { .threw = 1, .error = ALLOCATION_ERROR_OUT_OF_MEMORY }; while (page->next != NULL_ADDRESS) { malunal_uint32_t remaining = page->size - page->used; if (size <= remaining) return page_acquire(page, size); page = page->next; continue; } allocation_result_t result = create_page(allocator->upstream, page->size); page = page->next = !result.threw ? result.address : NULL_ADDRESS; return page != NULL_ADDRESS ? page_acquire(page, size) : (allocation_result_t) { .threw = 1, .error = ALLOCATION_ERROR_OUT_OF_MEMORY }; } static allocation_result_t arena_reacquire( impl_mptr_t allocator, malunal_mptr_t address, malunal_size_t oldsize, malunal_size_t newsize ) { MALUNAL_UNUSED(address); MALUNAL_UNUSED(oldsize); return arena_acquire(allocator, newsize); } allocation_error_t arena_release( impl_mptr_t allocator, malunal_mptr_t address, malunal_size_t size ) { MALUNAL_UNUSED(allocator); MALUNAL_UNUSED(address); MALUNAL_UNUSED(size); } allocation_error_t arena_reset(allocator_mptr_t arena) { if (arena == NULL_ADDRESS) return ALLOCATION_ERROR_NULL_ALLOCATOR; impl_mptr_t impl = (impl_mptr_t)arena; page_mptr_t page = impl->context; while (page != NULL_ADDRESS) { page->used = sizeof(page_t); page = page->next; } } static const virtual_table_t arena_vtable = { .initialize = (allocator_initialize_pfn_t)&arena_initialize, .finalize = (allocator_finalize_pfn_t)&arena_finalize, .acquire = (allocator_acquire_pfn_t)&arena_acquire, .reacquire = (allocator_reacquire_pfn_t)&arena_reacquire, .release = (allocator_release_pfn_t)&arena_release }; allocator_t arena_allocator() { implementation_t implementation = { .vtable = &arena_vtable, .upstream = NULL_ADDRESS, .context = NULL_ADDRESS, .allocated = 0, .reserved = 0, .acquires = 0, .releases = 0 }; allocator_t result; memcpy( &result, &implementation, sizeof(implementation_t) ); return result; }