Initial Commit

This commit is contained in:
2026-08-17 04:16:46 -05:00
commit 83bd4a3492
9 changed files with 1708 additions and 0 deletions
+182
View File
@@ -0,0 +1,182 @@
#include "malunal/container.h"
typedef container_exception_t
(*container_initialize_pfn_t)(
container_mptr_t container,
allocator_mptr_t allocator,
malunal_size_t stride,
malunal_size_t capacity
);
typedef container_exception_t
(*container_finalize_pfn_t)(
container_mptr_t container
);
typedef container_exception_t
(*container_clear_pfn_t)(
container_mptr_t container
);
typedef container_exception_t
(*container_append_pfn_t)(
container_mptr_t container,
malunal_iptr_t element
);
typedef container_exception_t
(*container_remove_pfn_t)(
container_mptr_t container,
malunal_iptr_t element
);
typedef container_exception_t
(*container_contains_pfn_t)(
container_iptr_t container,
malunal_iptr_t element
);
typedef struct {
container_initialize_pfn_t const initialize;
container_finalize_pfn_t const finalize;
container_clear_pfn_t const clear;
container_append_pfn_t const append;
container_remove_pfn_t const remove;
container_contains_pfn_t const contains;
} virtual_table_t;
typedef virtual_table_t* base_vtable_mptr_t;
typedef const virtual_table_t* vtable_iptr_t;
typedef struct {
vtable_iptr_t const vtable;
allocator_mptr_t allocator;
malunal_mptr_t context;
malunal_size_t stride;
malunal_size_t count;
malunal_size_t capacity;
} implementation_t;
typedef implementation_t* impl_mptr_t;
typedef const implementation_t* impl_iptr_t;
// Verify these are the same.
_Static_assert(
CONTAINER_SIZE == sizeof(implementation_t),
"container_t must be the same size as its implementation"
);
container_exception_t
container_allocator(
container_iptr_t container,
allocator_mptr_t* out
) {
impl_iptr_t implementation = (impl_iptr_t)container;
if (implementation == NULL_ADDRESS)
return CONTAINER_ERROR_NULL_CONTAINER;
*out = implementation->allocator;
return CONTAINER_ERROR_SUCCESS;
}
container_exception_t
container_stride(
container_iptr_t container,
malunal_size_t* out
) {
impl_iptr_t implementation = (impl_iptr_t)container;
if (implementation == NULL_ADDRESS)
return CONTAINER_ERROR_NULL_CONTAINER;
*out = implementation->stride;
return CONTAINER_ERROR_SUCCESS;
}
container_exception_t
container_count(
container_iptr_t container,
malunal_size_t* out
) {
impl_iptr_t implementation = (impl_iptr_t)container;
if (implementation == NULL_ADDRESS)
return CONTAINER_ERROR_NULL_CONTAINER;
*out = implementation->count;
return CONTAINER_ERROR_SUCCESS;
}
container_exception_t
container_capacity(
container_iptr_t container,
malunal_size_t* out
) {
impl_iptr_t implementation = (impl_iptr_t)container;
if (implementation == NULL_ADDRESS)
return CONTAINER_ERROR_NULL_CONTAINER;
*out = implementation->capacity;
return CONTAINER_ERROR_SUCCESS;
}
container_exception_t
container_initialize(
container_mptr_t container,
allocator_mptr_t allocator,
malunal_size_t stride,
malunal_size_t capacity
) {
impl_mptr_t implementation = (impl_mptr_t)container;
return implementation != NULL_ADDRESS
? implementation->vtable->initialize(container, allocator, stride, capacity)
: CONTAINER_ERROR_NULL_CONTAINER;
}
container_exception_t
container_finalize(container_mptr_t container) {
impl_mptr_t implementation = (impl_mptr_t)container;
return implementation != NULL_ADDRESS
? implementation->vtable->finalize(container)
: CONTAINER_ERROR_NULL_CONTAINER;
}
container_exception_t
container_clear(container_mptr_t container) {
impl_mptr_t implementation = (impl_mptr_t)container;
return implementation != NULL_ADDRESS
? implementation->vtable->clear(container)
: CONTAINER_ERROR_NULL_CONTAINER;
}
container_exception_t
container_append(
container_mptr_t container,
malunal_iptr_t element
) {
impl_mptr_t implementation = (impl_mptr_t)container;
return implementation != NULL_ADDRESS
? implementation->vtable->append(container, element)
: CONTAINER_ERROR_NULL_CONTAINER;
}
container_exception_t
container_remove(
container_mptr_t container,
malunal_iptr_t element
) {
impl_mptr_t implementation = (impl_mptr_t)container;
return implementation != NULL_ADDRESS
? implementation->vtable->remove(container, element)
: CONTAINER_ERROR_NULL_CONTAINER;
}
container_exception_t
container_contains(
container_iptr_t container,
malunal_iptr_t element
) {
impl_iptr_t implementation = (impl_iptr_t)container;
return implementation != NULL_ADDRESS
? implementation->vtable->contains(container, element)
: CONTAINER_ERROR_NULL_CONTAINER;
}
+500
View File
@@ -0,0 +1,500 @@
#include <memory.h>
#include "malunal/containers/vector.h"
typedef container_exception_t
(*vector_initialize_pfn_t)(
vector_mptr_t vector,
allocator_mptr_t allocator,
malunal_size_t stride,
malunal_size_t capacity
);
typedef container_exception_t
(*vector_finalize_pfn_t)(
vector_mptr_t vector
);
typedef container_exception_t
(*vector_clear_pfn_t)(
vector_mptr_t vector
);
typedef container_exception_t
(*vector_append_pfn_t)(
vector_mptr_t vector,
malunal_iptr_t element
);
typedef container_exception_t
(*vector_remove_pfn_t)(
vector_mptr_t vector,
malunal_iptr_t element
);
typedef container_exception_t
(*vector_contains_pfn_t)(
vector_iptr_t vector,
malunal_iptr_t element
);
typedef container_exception_t
(*vector_get_pfn_t)(
vector_iptr_t vector,
malunal_size_t index,
malunal_mptr_t outptr
);
typedef container_exception_t
(*vector_set_pfn_t)(
vector_mptr_t vector,
malunal_size_t index,
malunal_iptr_t element
);
typedef container_exception_t
(*vector_insert_at_pfn_t)(
vector_mptr_t vector,
malunal_size_t index,
malunal_iptr_t element
);
typedef container_exception_t
(*vector_remove_at_pfn_t)(
vector_mptr_t vector,
malunal_size_t index
);
typedef container_exception_t
(*vector_index_of_pfn_t)(
vector_iptr_t vector,
malunal_iptr_t element,
malunal_size_t* outidx
);
typedef struct VectorVTable {
// Container generic.
vector_initialize_pfn_t const initialize;
vector_finalize_pfn_t const finalize;
vector_clear_pfn_t const clear;
vector_append_pfn_t const append;
vector_remove_pfn_t const remove;
vector_contains_pfn_t const contains;
// Vector specific.
vector_get_pfn_t const get;
vector_set_pfn_t const set;
vector_insert_at_pfn_t const insert_at;
vector_remove_at_pfn_t const remove_at;
vector_index_of_pfn_t const index_of;
} virtual_table_t;
typedef virtual_table_t* vtable_mptr_t;
typedef const virtual_table_t* vtable_iptr_t;
// Identical to container.
typedef struct {
vtable_iptr_t const vtable;
allocator_mptr_t allocator;
malunal_mptr_t context;
malunal_size_t stride;
malunal_size_t count;
malunal_size_t capacity;
} implementation_t;
typedef implementation_t* impl_mptr_t;
typedef const implementation_t* impl_iptr_t;
static
container_exception_t
vector_initialize_impl(
impl_mptr_t vector,
allocator_mptr_t allocator,
malunal_size_t stride,
malunal_size_t capacity
) {
malunal_size_t bytes = capacity * stride;
allocation_result_t result = allocator_acquire(allocator, bytes);
if (!result.threw) {
vector->allocator = allocator;
vector->context = result.address;
vector->stride = stride;
vector->capacity = capacity;
return CONTAINER_ERROR_SUCCESS;
}
switch (result.error) {
case ALLOCATION_ERROR_NULL_ALLOCATOR:
case ALLOCATION_ERROR_NULL_CONTEXT:
case ALLOCATION_ERROR_NULL_UPSTREAM:
return CONTAINER_ERROR_NULL_ALLOCATOR;
case ALLOCATION_ERROR_OUT_OF_MEMORY:
return CONTAINER_ERROR_OUT_OF_MEMORY;
}
}
static
container_exception_t
vector_finalize_impl(impl_mptr_t vector) {
malunal_size_t bytes = vector->capacity * vector->stride;
allocation_error_t error = allocator_release(
vector->allocator,
vector->context,
bytes
);
switch (error) {
case ALLOCATION_ERROR_SUCCESS:
return CONTAINER_ERROR_SUCCESS;
case ALLOCATION_ERROR_NULL_ALLOCATOR:
case ALLOCATION_ERROR_NULL_CONTEXT:
case ALLOCATION_ERROR_NULL_UPSTREAM:
return CONTAINER_ERROR_NULL_ALLOCATOR;
case ALLOCATION_ERROR_LEAKY_MEMORY:
case ALLOCATION_ERROR_NOT_MY_ADDRESS:
case ALLOCATION_ERROR_FAILURE:
return CONTAINER_ERROR_FAILURE;
}
}
static
container_exception_t
vector_clear_impl(impl_mptr_t vector) {
vector->count = 0;
return CONTAINER_ERROR_SUCCESS;
}
static
container_exception_t
vector_append_impl(
impl_mptr_t vector,
malunal_iptr_t element
) {
if (vector->context == NULL_ADDRESS)
return CONTAINER_ERROR_NULL_CONTEXT;
if (vector->count < vector->capacity)
goto copy_element;
malunal_size_t doubled = vector->capacity * 2;
allocation_result_t result = allocator_reacquire(
vector->allocator,
vector->context,
vector->capacity * vector->stride,
doubled * vector->stride
);
if (!result.threw) {
vector->context = result.address;
vector->capacity = doubled;
goto copy_element;
}
switch (result.error) {
case ALLOCATION_ERROR_NULL_ALLOCATOR:
case ALLOCATION_ERROR_NULL_UPSTREAM:
case ALLOCATION_ERROR_NULL_CONTEXT:
return CONTAINER_ERROR_NULL_ALLOCATOR;
case ALLOCATION_ERROR_LEAKY_MEMORY:
case ALLOCATION_ERROR_NOT_MY_ADDRESS:
case ALLOCATION_ERROR_FAILURE:
return CONTAINER_ERROR_FAILURE;
}
copy_element:
malunal_uint8_t* buffer = vector->context;
buffer = buffer + vector->stride * vector->count;
memcpy(buffer, element, vector->stride);
vector->count++;
return CONTAINER_ERROR_SUCCESS;
}
static
container_exception_t
vector_remove_impl(
impl_mptr_t vector,
malunal_iptr_t element
) {
if (vector->context == NULL_ADDRESS)
return CONTAINER_ERROR_NULL_CONTEXT;
malunal_size_t index = 0;
vector_mptr_t erased = (vector_mptr_t)vector;
container_exception_t error = vector_index_of(erased, element, &index);
if (error != CONTAINER_ERROR_SUCCESS)
return error;
return index != -1
? vector_remove_at(erased, index)
: CONTAINER_ERROR_SUCCESS;
}
static
container_exception_t
vector_contains_impl(
impl_iptr_t vector,
malunal_iptr_t element
) {
if (vector->context == NULL_ADDRESS)
return CONTAINER_ERROR_NULL_CONTEXT;
malunal_size_t index = 0;
vector_mptr_t erased = (vector_mptr_t)vector;
container_exception_t error = vector_index_of(erased, element, &index);
if (error != CONTAINER_ERROR_SUCCESS)
return error;
return index != -1
? CONTAINER_ERROR_SUCCESS
: CONTAINER_ERROR_FAILURE;
}
static
container_exception_t
vector_get_def_impl(
impl_iptr_t vector,
malunal_size_t index,
malunal_mptr_t outptr
) {
if (vector->context == NULL_ADDRESS)
return CONTAINER_ERROR_NULL_CONTEXT;
if (index < 0 || index >= vector->count)
return CONTAINER_ERROR_OUT_OF_BOUNDS;
malunal_uint8_t* buffer = vector->context;
buffer = buffer + vector->stride * index;
memcpy(outptr, buffer, vector->stride);
return CONTAINER_ERROR_SUCCESS;
}
static
container_exception_t
vector_set_def_impl(
impl_mptr_t vector,
malunal_size_t index,
malunal_iptr_t element
) {
if (vector->context == NULL_ADDRESS)
return CONTAINER_ERROR_NULL_CONTEXT;
if (index < 0 || index >= vector->count)
return CONTAINER_ERROR_OUT_OF_BOUNDS;
malunal_uint8_t* buffer = vector->context;
buffer = buffer + vector->stride * index;
memcpy(buffer, element, vector->stride);
return CONTAINER_ERROR_SUCCESS;
}
static
container_exception_t
vector_insert_at_def_impl(
impl_mptr_t vector,
malunal_size_t index,
malunal_iptr_t element
) {
if (vector->context == NULL_ADDRESS)
return CONTAINER_ERROR_NULL_CONTEXT;
if (index < 0 || index > vector->count)
return CONTAINER_ERROR_OUT_OF_BOUNDS;
if (vector->count < vector->capacity)
goto insert_element;
malunal_size_t doubled = vector->capacity * 2;
allocation_result_t result = allocator_reacquire(
vector->allocator,
vector->context,
vector->capacity * vector->stride,
doubled * vector->stride
);
if (!result.threw) {
vector->context = result.address;
vector->capacity = doubled;
goto insert_element;
}
switch (result.error) {
case ALLOCATION_ERROR_NULL_ALLOCATOR:
case ALLOCATION_ERROR_NULL_UPSTREAM:
case ALLOCATION_ERROR_NULL_CONTEXT:
return CONTAINER_ERROR_NULL_ALLOCATOR;
case ALLOCATION_ERROR_LEAKY_MEMORY:
case ALLOCATION_ERROR_NOT_MY_ADDRESS:
case ALLOCATION_ERROR_FAILURE:
return CONTAINER_ERROR_FAILURE;
}
insert_element:
malunal_size_t offset = vector->stride * index;
malunal_uint8_t* orig = vector->context;
orig = orig + offset;
malunal_size_t window = vector->count - index;
malunal_size_t bytes = vector->stride * window;
malunal_uint8_t* dest = orig + vector->stride;
memmove(dest, orig, bytes);
memcpy(orig, element, vector->stride);
vector->count++;
return CONTAINER_ERROR_SUCCESS;
}
static
container_exception_t
vector_remove_at_def_impl(
impl_mptr_t vector,
malunal_size_t index
) {
if (vector->context == NULL_ADDRESS)
return CONTAINER_ERROR_NULL_CONTEXT;
if (index < 0 || index >= vector->count)
return CONTAINER_ERROR_OUT_OF_BOUNDS;
malunal_size_t offset = vector->stride * index;
malunal_uint8_t* dest = vector->context;
dest = dest + offset;
malunal_size_t window = vector->count - index;
malunal_size_t bytes = vector->stride * window;
malunal_uint8_t* orig = dest + vector->stride;
memmove(dest, orig, bytes);
vector->count--;
return CONTAINER_ERROR_SUCCESS;
}
static
container_exception_t
vector_index_of_def_impl(
impl_mptr_t vector,
malunal_iptr_t element,
malunal_size_t* outidx
) {
if (vector->context == NULL_ADDRESS)
return CONTAINER_ERROR_NULL_CONTEXT;
malunal_size_t index = 0;
malunal_uint8_t* buffer = vector->context;
while (index < vector->count) {
if (memcmp(buffer, element, vector->stride) == 0) {
*outidx = index;
return CONTAINER_ERROR_SUCCESS;
}
index += 1;
buffer += vector->stride;
}
*outidx = -1;
return CONTAINER_ERROR_SUCCESS;
}
container_exception_t
vector_get(
vector_iptr_t vector,
malunal_size_t index,
malunal_mptr_t outptr
) {
impl_iptr_t implementation = (impl_iptr_t)vector;
return implementation != NULL_ADDRESS
? implementation->vtable->get(vector, index, outptr)
: CONTAINER_ERROR_NULL_CONTAINER;
}
container_exception_t
vector_set(
vector_mptr_t vector,
malunal_size_t index,
malunal_iptr_t element
) {
impl_iptr_t implementation = (impl_iptr_t)vector;
return implementation != NULL_ADDRESS
? implementation->vtable->set(vector, index, element)
: CONTAINER_ERROR_NULL_CONTAINER;
}
container_exception_t
vector_insert_at(
vector_mptr_t vector,
malunal_size_t index,
malunal_iptr_t element
) {
impl_iptr_t implementation = (impl_iptr_t)vector;
return implementation != NULL_ADDRESS
? implementation->vtable->insert_at(vector, index, element)
: CONTAINER_ERROR_NULL_CONTAINER;
}
container_exception_t
vector_remove_at(
vector_mptr_t vector,
malunal_size_t index
) {
impl_iptr_t implementation = (impl_iptr_t)vector;
return implementation != NULL_ADDRESS
? implementation->vtable->remove_at(vector, index)
: CONTAINER_ERROR_NULL_CONTAINER;
}
container_exception_t
vector_index_of(
vector_mptr_t vector,
malunal_iptr_t element,
malunal_size_t* outidx
) {
impl_iptr_t implementation = (impl_iptr_t)vector;
return implementation != NULL_ADDRESS
? implementation->vtable->index_of(vector, element, outidx)
: CONTAINER_ERROR_NULL_CONTAINER;
}
static const
virtual_table_t vector_vtable = {
.initialize = (vector_initialize_pfn_t)&vector_initialize_impl,
.finalize = (vector_finalize_pfn_t)&vector_finalize_impl,
.clear = (vector_clear_pfn_t)&vector_clear_impl,
.append = (vector_append_pfn_t)&vector_append_impl,
.remove = (vector_remove_pfn_t)&vector_remove_impl,
.contains = (vector_contains_pfn_t)&vector_contains_impl,
.get = (vector_get_pfn_t)&vector_get_def_impl,
.set = (vector_set_pfn_t)&vector_set_def_impl,
.insert_at = (vector_insert_at_pfn_t)&vector_insert_at_def_impl,
.remove_at = (vector_remove_at_pfn_t)&vector_remove_at_def_impl,
.index_of = (vector_index_of_pfn_t)&vector_index_of_def_impl
};
vector_t
vector_container() {
implementation_t implementation = {
.vtable = &vector_vtable,
.allocator = NULL_ADDRESS,
.context = NULL_ADDRESS,
.stride = 0,
.count = 0,
.capacity = 0,
};
vector_t result;
memcpy(
&result,
&implementation,
sizeof(implementation_t)
);
return result;
}