501 lines
12 KiB
C
501 lines
12 KiB
C
#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;
|
|
}
|
|
|