New allocators, testing framework, overall improvements
This commit is contained in:
+248
@@ -0,0 +1,248 @@
|
||||
#include "malunal/allocators/pool.h"
|
||||
|
||||
typedef struct free_t free_t;
|
||||
typedef free_t* free_mptr_t;
|
||||
typedef const free_t* free_iptr_t;
|
||||
|
||||
struct free_t {
|
||||
free_mptr_t next;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
allocator_t allocator;
|
||||
allocator_mptr_t upstream;
|
||||
|
||||
malunal_size_t stride;
|
||||
malunal_size_t count;
|
||||
malunal_size_t capacity;
|
||||
malunal_mptr_t buffer;
|
||||
free_mptr_t freelist;
|
||||
} impl_t;
|
||||
|
||||
typedef impl_t* impl_mptr_t;
|
||||
typedef const impl_t* impl_iptr_t;
|
||||
_Static_assert(
|
||||
sizeof(pool_allocator_t) == sizeof(impl_t),
|
||||
"Pool allocator must be the size of its implemenation"
|
||||
);
|
||||
|
||||
static
|
||||
error_t
|
||||
pool_allocator_acquire_impl(
|
||||
pool_allocator_mptr_t allocator,
|
||||
malunal_size_t size,
|
||||
malunal_mptr_t* out
|
||||
) {
|
||||
MALUNAL_UNUSED(size);
|
||||
return pool_allocator_acquire(allocator, out);
|
||||
}
|
||||
|
||||
static
|
||||
error_t
|
||||
pool_allocator_dispose_impl(
|
||||
pool_allocator_mptr_t allocator,
|
||||
malunal_mptr_t address,
|
||||
malunal_size_t size
|
||||
) {
|
||||
MALUNAL_UNUSED(size);
|
||||
return pool_allocator_dispose(allocator, address);
|
||||
}
|
||||
|
||||
static const
|
||||
allocator_vtable_t pool_allocator_vtable = {
|
||||
.acquire = (allocator_acquire_pfn_t)&pool_allocator_acquire_impl,
|
||||
.dispose = (allocator_dispose_pfn_t)&pool_allocator_dispose_impl
|
||||
};
|
||||
|
||||
error_t
|
||||
pool_allocator_stride(
|
||||
pool_allocator_iptr_t allocator,
|
||||
malunal_size_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;
|
||||
*out = self->stride;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
pool_allocator_count(
|
||||
pool_allocator_iptr_t allocator,
|
||||
malunal_size_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;
|
||||
*out = self->count;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
pool_allocator_capacity(
|
||||
pool_allocator_iptr_t allocator,
|
||||
malunal_size_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;
|
||||
*out = self->capacity;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
pool_allocator_init(
|
||||
malunal_size_t stride,
|
||||
malunal_size_t capacity,
|
||||
allocator_mptr_t upstream,
|
||||
pool_allocator_mptr_t allocator
|
||||
) {
|
||||
if (stride < 8)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_ALLOCATOR_T,
|
||||
.code = ALLOCATOR_ERROR_FAILURE
|
||||
};
|
||||
|
||||
if (allocator == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_ALLOCATOR_T,
|
||||
.code = ALLOCATOR_ERROR_NULL_ALLOCATOR
|
||||
};
|
||||
|
||||
if (upstream == null)
|
||||
upstream = libc_allocator();
|
||||
|
||||
malunal_mptr_t address;
|
||||
malunal_size_t bytes = stride * capacity;
|
||||
error_t result = allocator_acquire(upstream, bytes, &address);
|
||||
if (result.domain != null)
|
||||
return result;
|
||||
|
||||
malunal_size_t index;
|
||||
malunal_uint8_t* asbytes = (malunal_uint8_t*)address;
|
||||
free_mptr_t chunk = (free_mptr_t)address;
|
||||
for (index = 1; index < capacity; index++)
|
||||
chunk = chunk->next = (free_mptr_t)(asbytes + stride * index);
|
||||
|
||||
impl_mptr_t self = (impl_mptr_t)allocator;
|
||||
self->allocator = (allocator_t){ &pool_allocator_vtable };
|
||||
self->upstream = upstream;
|
||||
self->stride = stride;
|
||||
self->count = 0;
|
||||
self->capacity = capacity;
|
||||
self->buffer = address;
|
||||
self->freelist = chunk;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
pool_allocator_acquire(
|
||||
pool_allocator_mptr_t allocator,
|
||||
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;
|
||||
if (self->count == self->capacity)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_ALLOCATOR_T,
|
||||
.code = ALLOCATOR_ERROR_OUT_OF_MEMORY
|
||||
};
|
||||
|
||||
free_mptr_t prev = self->freelist;
|
||||
free_mptr_t curr = prev;
|
||||
free_mptr_t next = prev->next;
|
||||
while (next != null) {
|
||||
prev = curr;
|
||||
curr = next;
|
||||
next = next->next;
|
||||
}
|
||||
|
||||
*out = curr;
|
||||
self->count++;
|
||||
if (curr == self->freelist)
|
||||
self->freelist = null;
|
||||
else
|
||||
prev->next = null;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
pool_allocator_dispose(
|
||||
pool_allocator_mptr_t allocator,
|
||||
malunal_mptr_t address
|
||||
) {
|
||||
if (allocator == null)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_ALLOCATOR_T,
|
||||
.code = ALLOCATOR_ERROR_NULL_ALLOCATOR
|
||||
};
|
||||
|
||||
impl_mptr_t self = (impl_mptr_t)allocator;
|
||||
malunal_uint8_t* addr = address;
|
||||
malunal_uint8_t* beg = self->buffer;
|
||||
malunal_uint8_t* end = beg + self->stride * self->capacity;
|
||||
if (addr < beg || addr > end)
|
||||
return (error_t) {
|
||||
.domain = &ERROR_DOMAIN_ALLOCATOR_T,
|
||||
.code = ALLOCATOR_ERROR_NOT_MY_ADDRESS
|
||||
};
|
||||
|
||||
if (self->freelist == null) {
|
||||
self->freelist = (free_mptr_t)addr;
|
||||
self->freelist->next = null;
|
||||
self->count--;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
free_mptr_t chunk = self->freelist;
|
||||
while (chunk->next != null)
|
||||
chunk = chunk->next;
|
||||
chunk->next = (free_mptr_t)addr;
|
||||
chunk->next->next = null;
|
||||
self->count--;
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
error_t
|
||||
pool_allocator_free(
|
||||
pool_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 = allocator_dispose(
|
||||
self->upstream,
|
||||
self->buffer,
|
||||
self->stride * self->capacity
|
||||
);
|
||||
if (result.domain != null)
|
||||
return result;
|
||||
|
||||
self->stride = 0;
|
||||
self->count = 0;
|
||||
self->capacity = 0;
|
||||
self->buffer = null;
|
||||
self->freelist = null;
|
||||
return NO_ERROR;
|
||||
}
|
||||
Reference in New Issue
Block a user