#include #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; }