New allocators, testing framework, overall improvements
This commit is contained in:
+220
-180
@@ -1,198 +1,238 @@
|
||||
#include <memory.h>
|
||||
#include "internal.h"
|
||||
#include "malunal/allocators/arena.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;
|
||||
typedef struct {
|
||||
malunal_uint32_t size;
|
||||
malunal_int8_t data[];
|
||||
};
|
||||
malunal_uint32_t used;
|
||||
malunal_mptr_t next;
|
||||
malunal_uint8_t bytes[];
|
||||
} region_t;
|
||||
|
||||
typedef region_t* region_mptr_t;
|
||||
typedef const region_t* region_iptr_t;
|
||||
|
||||
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)
|
||||
typedef struct {
|
||||
allocator_t allocator;
|
||||
region_mptr_t context;
|
||||
} impl_t;
|
||||
|
||||
typedef impl_t* impl_mptr_t;
|
||||
typedef const impl_t* impl_iptr_t;
|
||||
_Static_assert(
|
||||
sizeof(arena_allocator_t) == sizeof(impl_t),
|
||||
"Arena allocator must be the size of its implementation"
|
||||
);
|
||||
|
||||
error_t
|
||||
create_region(region_mptr_t* region) {
|
||||
allocator_mptr_t allocator = platform_allocator();
|
||||
malunal_size_t rounded = align_to_page(MALUNAL_ARENA_REGION_SIZE);
|
||||
malunal_mptr_t* address = (malunal_mptr_t*)region;
|
||||
error_t result = allocator_acquire(allocator, rounded, address);
|
||||
|
||||
if (result.domain != null)
|
||||
return result;
|
||||
|
||||
page_mptr_t page = result.address;
|
||||
page->size = rounded;
|
||||
page->used = sizeof(page_t);
|
||||
page->next = NULL_ADDRESS;
|
||||
return result;
|
||||
(*region)->size = rounded;
|
||||
(*region)->used = sizeof(region_t);
|
||||
(*region)->next = null;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
allocation_error_t
|
||||
delete_page(
|
||||
allocator_mptr_t allocator,
|
||||
page_mptr_t page
|
||||
error_t
|
||||
delete_region(region_mptr_t region) {
|
||||
if (region == null)
|
||||
return NO_ERROR;
|
||||
|
||||
error_t result = delete_region(region->next);
|
||||
if (result.domain != null)
|
||||
return result;
|
||||
|
||||
region->next = null;
|
||||
allocator_mptr_t allocator = platform_allocator();
|
||||
return allocator_dispose(allocator, region, region->size);
|
||||
}
|
||||
|
||||
error_t
|
||||
region_acquire(
|
||||
region_mptr_t region,
|
||||
malunal_size_t size,
|
||||
malunal_mptr_t* out
|
||||
) {
|
||||
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;
|
||||
}
|
||||
malunal_uint32_t offset =
|
||||
region->used - sizeof(region_t);
|
||||
*out = region->bytes + offset;
|
||||
region->used += size;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
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_vtable_t arena_allocator_vtable = {
|
||||
.acquire = (allocator_acquire_pfn_t)&arena_allocator_acquire,
|
||||
.dispose = (allocator_dispose_pfn_t)&arena_allocator_dispose
|
||||
};
|
||||
|
||||
allocator_t
|
||||
arena_allocator() {
|
||||
implementation_t implementation = {
|
||||
.vtable = &arena_vtable,
|
||||
.upstream = NULL_ADDRESS,
|
||||
.context = NULL_ADDRESS,
|
||||
.allocated = 0,
|
||||
.reserved = 0,
|
||||
.acquires = 0,
|
||||
.releases = 0
|
||||
};
|
||||
error_t
|
||||
arena_allocator_size(
|
||||
arena_allocator_iptr_t allocator,
|
||||
malunal_uint32_t* out
|
||||
) {
|
||||
if (allocator == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_ALLOCATOR_T,
|
||||
.code = ALLOCATOR_ERROR_NULL_ALLOCATOR
|
||||
};
|
||||
|
||||
allocator_t result;
|
||||
memcpy(
|
||||
&result,
|
||||
&implementation,
|
||||
sizeof(implementation_t)
|
||||
);
|
||||
impl_iptr_t self = (impl_iptr_t)allocator;
|
||||
region_iptr_t region = self->context;
|
||||
malunal_size_t total = 0;
|
||||
while (region != null) {
|
||||
total += region->size;
|
||||
region = region->next;
|
||||
}
|
||||
|
||||
return result;
|
||||
*out = total;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
arena_allocator_used(
|
||||
arena_allocator_iptr_t allocator,
|
||||
malunal_uint32_t* out
|
||||
) {
|
||||
if (allocator == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_ALLOCATOR_T,
|
||||
.code = ALLOCATOR_ERROR_NULL_ALLOCATOR
|
||||
};
|
||||
|
||||
impl_iptr_t self = (impl_iptr_t)allocator;
|
||||
region_iptr_t region = self->context;
|
||||
malunal_size_t total = 0;
|
||||
while (region != null) {
|
||||
total += region->used;
|
||||
region = region->next;
|
||||
}
|
||||
|
||||
*out = total;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
arena_allocator_init(
|
||||
malunal_size_t capacity,
|
||||
arena_allocator_mptr_t allocator
|
||||
) {
|
||||
if (allocator == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_ALLOCATOR_T,
|
||||
.code = ALLOCATOR_ERROR_NULL_ALLOCATOR
|
||||
};
|
||||
|
||||
region_mptr_t region = null;
|
||||
error_t result = create_region(®ion);
|
||||
if (result.domain != null)
|
||||
return result;
|
||||
|
||||
impl_mptr_t self = (impl_mptr_t)allocator;
|
||||
self->allocator = (allocator_t){ &arena_allocator_vtable };
|
||||
self->context = region;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
arena_allocator_acquire(
|
||||
arena_allocator_mptr_t allocator,
|
||||
malunal_size_t size,
|
||||
malunal_mptr_t* out
|
||||
) {
|
||||
if (allocator == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_ALLOCATOR_T,
|
||||
.code = ALLOCATOR_ERROR_NULL_ALLOCATOR
|
||||
};
|
||||
|
||||
impl_mptr_t self = (impl_mptr_t)allocator;
|
||||
region_mptr_t region = self->context;
|
||||
while (region != null) {
|
||||
if (region->size - region->used >= size)
|
||||
return region_acquire(region, size, out);
|
||||
region = region->next;
|
||||
}
|
||||
|
||||
error_t result = create_region((region_mptr_t*)®ion->next);
|
||||
return result.domain == null
|
||||
? region_acquire(region->next, size, out)
|
||||
: result;
|
||||
}
|
||||
|
||||
error_t
|
||||
arena_allocator_dispose(
|
||||
arena_allocator_mptr_t allocator,
|
||||
malunal_mptr_t address,
|
||||
malunal_size_t size
|
||||
) {
|
||||
if (allocator == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_ALLOCATOR_T,
|
||||
.code = ALLOCATOR_ERROR_NULL_ALLOCATOR
|
||||
};
|
||||
|
||||
impl_mptr_t self = (impl_mptr_t)allocator;
|
||||
region_mptr_t reg = self->context;
|
||||
malunal_uint8_t* addr = address;
|
||||
malunal_uint8_t* beg = reg->bytes;
|
||||
malunal_uint8_t* end = reg->bytes + reg->size - sizeof(region_t);
|
||||
while (true) {
|
||||
if (addr < beg || addr > end)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_ALLOCATOR_T,
|
||||
.code = ALLOCATOR_ERROR_NOT_MY_ADDRESS
|
||||
};
|
||||
|
||||
reg = reg->next;
|
||||
if (reg == null)
|
||||
break;
|
||||
beg = reg->bytes;
|
||||
end = reg->bytes + reg->size - sizeof(region_t);
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
arena_allocator_reset(
|
||||
arena_allocator_mptr_t allocator
|
||||
) {
|
||||
if (allocator == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_ALLOCATOR_T,
|
||||
.code = ALLOCATOR_ERROR_NULL_ALLOCATOR
|
||||
};
|
||||
|
||||
impl_mptr_t self = (impl_mptr_t)allocator;
|
||||
region_mptr_t region = self->context;
|
||||
while (region != null) {
|
||||
region->used = sizeof(region_t);
|
||||
region = region->next;
|
||||
}
|
||||
}
|
||||
|
||||
error_t
|
||||
arena_allocator_free(
|
||||
arena_allocator_mptr_t allocator
|
||||
) {
|
||||
if (allocator == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_ALLOCATOR_T,
|
||||
.code = ALLOCATOR_ERROR_NULL_ALLOCATOR
|
||||
};
|
||||
|
||||
impl_mptr_t self = (impl_mptr_t)allocator;
|
||||
error_t result = delete_region(self->context);
|
||||
if (result.domain != null)
|
||||
return result;
|
||||
|
||||
self->context = null;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user