diff options
Diffstat (limited to 'src/vector.c')
-rw-r--r-- | src/vector.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/src/vector.c b/src/vector.c new file mode 100644 index 0000000..65f004f --- /dev/null +++ b/src/vector.c @@ -0,0 +1,268 @@ +#include "config.h" + +#include <assert.h> +#include <errno.h> +#include <locale.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "logerr.h" +#include "catalog.h" +#include "i18n.h" + +#include "vector.h" + + +struct Vector { + void **values; + size_t capacity; + size_t back_count; + size_t front_count; + const size_t max_capacity; + const size_t multiplier; + const size_t value_size; +}; + +static const size_t +VECTOR_MAX_CAPACITY = SIZE_MAX; + +static const size_t +VECTOR_DEFAULT_CAPACITY = 8; + +static const size_t +GROWTH_MULTIPLIER = 2; + + +int +vector_new_with( + const size_t capacity, + const size_t max_capacity, + const size_t multiplier, + const size_t value_size, + struct Vector **const out +) { + int rc = -1; + + struct Vector v = { + .values = NULL, + .capacity = capacity, + .back_count = 0U, + .front_count = 0U, + .max_capacity = max_capacity, + .multiplier = multiplier, + .value_size = value_size, + }; + struct Vector *restrict vref = NULL; + + assert(capacity > 0); + if (capacity > max_capacity) { + logerr( + _(MSG_ERR_VECTOR_MAX_CAPACITY), + max_capacity, + strerror(EOVERFLOW) + ); + rc = EOVERFLOW; + goto out; + } + + const size_t array_bytes_size = capacity * value_size; + v.values = malloc(array_bytes_size); + if (v.values == NULL) { + logerr("malloc(%ld): %s\n", array_bytes_size, strerror(errno)); + goto out; + } + + vref = malloc(sizeof(*vref)); + if (vref == NULL) { + logerr("malloc(%ld): %s\n", sizeof(vref), strerror(errno)); + goto out; + } + memcpy(vref, &v, sizeof(v)); + + *out = vref; + rc = 0; +out: + if (rc) { + if (vref != NULL) { + free(vref); + } + if (v.values != NULL) { + free(v.values); + } + } + return rc; +} + +int +vector_new(const size_t value_size, struct Vector **const out) { + return vector_new_with( + VECTOR_DEFAULT_CAPACITY, + VECTOR_MAX_CAPACITY, + GROWTH_MULTIPLIER, + value_size, + out + ); +} + +void +vector_free(const struct Vector *const v) { + assert(v != NULL); + free(v->values); + free((void *)v); +} + +size_t +vector_count(const struct Vector *const v) { + const size_t count = v->back_count + v->front_count; + assert(count <= v->capacity); + return count; +} + +int +vector_nth(const struct Vector *const v, const size_t idx, const void **const out) { + int rc = -1; + + const size_t count = vector_count(v); + if (idx >= count) { + logerr(_(MSG_ERR_VECTOR_OUT_OF_BOUNDS), idx, count); + goto out; + } + + *out = v->values[idx]; // FIXME + rc = 0; +out: + return rc; +} + +static size_t +next_capacity(const struct Vector *const v) { + if (v->capacity > v->max_capacity / v->multiplier) { + return v->max_capacity; + } else { + return v->capacity * v->multiplier; + } +} + +int +vector_push_back(struct Vector *const v, void *const value) { + int rc = -1; + + void *new_values = NULL; + + const size_t count = vector_count(v); + if (count == v->capacity) { + if (count == v->max_capacity) { + logerr( + _(MSG_ERR_VECTOR_MAX_CAPACITY), + v->max_capacity, + strerror(EOVERFLOW) + ); + rc = EOVERFLOW; + goto out; + } + + const size_t new_capacity = next_capacity(v); + const size_t new_size = new_capacity * v->value_size; + new_values = realloc(v->values, new_size); + if (new_values == NULL) { + logerr( + "realloc(v->values, %ld): %s\n", + new_size, + strerror(errno) + ); + goto out; + } + v->capacity = new_capacity; + v->values = new_values; + } + + v->values[v->back_count] = value; + v->back_count++; + rc = 0; +out: + if (rc) { + if (new_values != NULL) { + free(new_values); + } + } + return rc; +} + +int +vector_push_front(struct Vector *const v, void *const value) { + int rc = -1; + + void *new_values = NULL; + + const size_t count = vector_count(v); + if (count == v->capacity) { + if (count == v->max_capacity) { + logerr( + _(MSG_ERR_VECTOR_MAX_CAPACITY), + v->max_capacity, + strerror(EOVERFLOW) + ); + } + + const size_t new_capacity = next_capacity(v); + const size_t new_size = new_capacity * v->value_size; + new_values = realloc(v->values, new_size); + if (new_values == NULL) { + logerr( + "realloc(v->values, %ld): %s\n", + new_size, + strerror(errno) + ); + goto out; + } + v->capacity = new_capacity; + // FIXME: copy, but leaving things at the beginning + } + + v->values[v->front_count] = value; + v->front_count++; + rc = 0; +out: + if (rc) { + if (new_values != NULL) { + free(new_values); + } + } + return rc; +} + +int +vector_pop_back(struct Vector *const v, const void **const out) { + int rc = -1; + + const size_t count = vector_count(v); + if (count == 0) { + logerr(_(MSG_ERR_VECTOR_UNDERFLOW)); + goto out; + } + + *out = v->values[v->back_count - 1]; // FIXME: can become negative! + v->back_count--; + rc = 0; +out: + return rc; +} + +int +vector_pop_front(struct Vector *const v, const void **const out) { + int rc = -1; + + const size_t count = vector_count(v); + if (count == 0) { + logerr(_(MSG_ERR_VECTOR_UNDERFLOW)); + goto out; + } + + *out = v->values[v->front_count - 1]; // FIXME: can become negative! + v->front_count--; + rc = 0; +out: + return rc; +} |