diff --git a/fx/include/fx/vector.h b/fx/include/fx/vector.h new file mode 100644 index 0000000..8460824 --- /dev/null +++ b/fx/include/fx/vector.h @@ -0,0 +1,159 @@ +#ifndef FX_VECTOR_H_ +#define FX_VECTOR_H_ + +#include +#include +#include + +typedef struct fx_vector_ops { + fx_status (*v_copy)(void *, const void *, size_t); + fx_status (*v_move)(void *, void *, size_t); + fx_status (*v_destroy)(void *); +} fx_vector_ops; + +#define FX_VECTOR_DEFINE(type, name) \ + struct { \ + size_t count; \ + size_t max; \ + type *items; \ + } name = {0} + +#define FX_VECTOR_DECLARE(type, name) \ + struct { \ + size_t count; \ + size_t max; \ + type *items; \ + } name + +#define FX_VECTOR_ITEM(name, index) name.items[index] +#define FX_VECTOR_ITEM_PTR(name, index) &name.items[index] +#define FX_VECTOR_COUNT(name) name.count +#define FX_VECTOR_MAX(name) name.max + +/* use this macros in your function prototype, to allow the function to accept + * a reference to a vector as a parameter */ +#define FX_VECTOR_REF_PARAM(type, name) \ + type **name, size_t *name##_count, size_t *name##_max +/* use this macro to pass a reference to a vector as a parameter to a function + * whose prototype uses FX_VECTOR_REF_PARAM */ +#define FX_VECTOR_REF(name) &(name.items), &(name.count), &(name.max) +/* use this macro to forward your reference to a vector (which you got via + * FX_VECTOR_REF_PARAM in your function prototype), to another function whose + * prototype also uses FX_VECTOR_REF_PARAM */ +#define FX_VECTOR_REF2(name) name, name##_count, name##_max + +/* use these functions if you're accessing a vector directly. */ +#define fx_vector_push_back(vector, ptr, ops) \ + __fx_vector_push_back( \ + (void **)&(vector.items), \ + ptr, \ + sizeof *ptr, \ + &(vector.count), \ + &(vector.max), \ + ops) +#define fx_vector_pop_back(vector, ops) \ + __fx_vector_pop_back( \ + (void **)&(vector.items), \ + sizeof *vector.items, \ + &(vector.count), \ + &(vector.max), \ + ops) +#define fx_vector_emplace_back(vector, ops) \ + __fx_vector_emplace_back( \ + (void **)&(vector.items), \ + sizeof *vector.items, \ + &(vector.count), \ + &(vector.max), \ + ops) +#define fx_vector_trim(vector, ops) \ + __fx_vector_trim( \ + (void **)&(vector.items), \ + sizeof *vector.items, \ + &(vector.count), \ + &(vector.max), \ + ops) +#define fx_vector_destroy(vector, ops) \ + __fx_vector_destroy( \ + (void **)&(vector.items), \ + sizeof *vector.items, \ + &(vector.count), \ + &(vector.max), \ + ops) + +/* use these functions if you're accessing a vector as a reference + * via FX_VECTOR_REF_PARAM. */ +#define fx_vector_ref_push_back(vector, ptr, ops) \ + __fx_vector_push_back( \ + (void **)(vector), \ + ptr, \ + sizeof *ptr, \ + (vector##_count), \ + (vector##_max), \ + ops) +#define fx_vector_ref_pop_back(vector, ops) \ + __fx_vector_pop_back( \ + (void **)(vector), \ + sizeof **vector, \ + (vector##_count), \ + (vector##_max), \ + ops) +#define fx_vector_ref_emplace_back(vector, ops) \ + __fx_vector_emplace_back( \ + (void **)(vector), \ + sizeof **vector, \ + (vector##_count), \ + (vector##_max), \ + ops) +#define fx_vector_ref_trim(vector, ops) \ + __fx_vector_trim( \ + (void **)(vector), \ + sizeof **vector, \ + (vector##_count), \ + (vector##_max), \ + ops) +#define fx_vector_ref_destroy(vector, ops) \ + __fx_vector_destroy( \ + (void **)(vector), \ + sizeof **vector, \ + (vector##_count), \ + (vector##_max), \ + ops) +#define fx_vector_ref_get_item(vector, index) (*vector)[index] +#define fx_vector_ref_get_item_ptr(vector, index) (&(*vector)[index]) +#define fx_vector_ref_get_count(vector) *(vector##_count) +#define fx_vector_ref_get_max(vector) *(vector##_count) + +/* don't use these functions */ +FX_API int __fx_vector_push_back( + void **vector, + const void *item, + size_t item_size, + size_t *count, + size_t *max, + const struct fx_vector_ops *ops); +FX_API void __fx_vector_pop_back( + void **vector, + size_t item_size, + size_t *count, + size_t *max, + const struct fx_vector_ops *ops); +FX_API void *__fx_vector_emplace_back( + void **vector, + size_t item_size, + size_t *count, + size_t *max, + const struct fx_vector_ops *ops); +FX_API void __fx_vector_trim( + void **vector, + size_t item_size, + size_t *count, + size_t *max, + const struct fx_vector_ops *ops); +FX_API void __fx_vector_destroy( + void **vector, + size_t item_size, + size_t *count, + size_t *max, + const struct fx_vector_ops *ops); + +#endif diff --git a/fx/vector.c b/fx/vector.c new file mode 100644 index 0000000..3130b7d --- /dev/null +++ b/fx/vector.c @@ -0,0 +1,270 @@ +#include +#include +#include + +#define DEFAULT_CAPACITY 16 + +struct vector { + void *v_buf; + size_t v_itemsz; + size_t v_count; + size_t v_max; +}; + +static void vector_wrap( + struct vector *v, + void **vector, + size_t item_size, + size_t *count, + size_t *max) +{ + v->v_buf = *vector; + v->v_itemsz = item_size; + v->v_count = *count; + v->v_max = *max; +} + +static void vector_unwrap( + struct vector *v, + void **vector, + size_t item_size, + size_t *count, + size_t *max) +{ + *vector = v->v_buf; + *count = v->v_count; + *max = v->v_max; +} + +static int move_vector_items( + struct vector *v, + void *new_buf, + size_t new_capacity, + const struct fx_vector_ops *ops) +{ + size_t items_to_copy = fx_min(size_t, v->v_count, new_capacity); + if (!ops || (!ops->v_copy && !ops->v_move)) { + memcpy(new_buf, v->v_buf, items_to_copy * v->v_itemsz); + return 0; + } + + char *src = v->v_buf; + char *dst = new_buf; + for (size_t i = 0; i < items_to_copy; i++) { + if (ops->v_move) { + ops->v_move(dst, src, v->v_itemsz); + } else if (ops->v_copy) { + ops->v_copy(dst, src, v->v_itemsz); + } else { + memcpy(dst, src, v->v_itemsz); + } + + src += v->v_itemsz; + dst += v->v_itemsz; + } + + return 0; +} + +static int trim_excess_items( + struct vector *v, + size_t new_capacity, + const struct fx_vector_ops *ops) +{ + size_t to_trim = v->v_count - new_capacity; + size_t start = new_capacity; + char *item = &v->v_buf[start]; + + for (size_t i = start; i < v->v_count; i++) { + ops->v_destroy(item); + item += v->v_itemsz; + } + + return 0; +} + +static int vector_resize( + struct vector *v, + size_t new_capacity, + const struct fx_vector_ops *ops) +{ + if (v->v_max >= new_capacity) { + return 0; + } + + void *new_buf = malloc(new_capacity * v->v_itemsz); + if (!new_buf) { + return -1; + } + + move_vector_items(v, new_buf, new_capacity, ops); + if (ops && ops->v_destroy && new_capacity < v->v_count) { + trim_excess_items(v, new_capacity, ops); + } + + if (v->v_buf) { + free(v->v_buf); + } + + v->v_buf = new_buf; + v->v_max = new_capacity; + + if (new_capacity < v->v_count) { + v->v_count = new_capacity; + } + + return 0; +} + +static int vector_push_back( + struct vector *v, + const void *item, + const struct fx_vector_ops *ops) +{ + int err = vector_resize(v, v->v_count + DEFAULT_CAPACITY, ops); + if (err != 0) { + return err; + } + + void *dest = (char *)v->v_buf + (v->v_count * v->v_itemsz); + memcpy(dest, item, v->v_itemsz); + v->v_count++; + + return 0; +} + +static int vector_pop_back(struct vector *v, const struct fx_vector_ops *ops) +{ + if (v->v_count > 0) { + v->v_count--; + } + + return 0; +} + +static void *vector_emplace_back( + struct vector *v, + const struct fx_vector_ops *ops) +{ + int err = 0; + if (v->v_count >= v->v_max) { + err = vector_resize(v, v->v_count + DEFAULT_CAPACITY, ops); + } + + if (err != 0) { + return NULL; + } + + void *dest = (char *)v->v_buf + (v->v_count * v->v_itemsz); + memset(dest, 0x0, v->v_itemsz); + v->v_count++; + + return dest; +} + +static void vector_destroy_items( + struct vector *v, + const struct fx_vector_ops *ops) +{ + for (size_t i = 0; i < v->v_count; i++) { + void *item = (char *)v->v_buf + (i * v->v_itemsz); + ops->v_destroy(item); + } +} + +static void vector_trim(struct vector *v, const struct fx_vector_ops *ops) +{ + if (v->v_max > v->v_count) { + vector_resize(v, v->v_count, ops); + } +} + +static void vector_destroy(struct vector *v, const struct fx_vector_ops *ops) +{ + if (ops && ops->v_destroy) { + vector_destroy_items(v, ops); + } + + if (v->v_buf) { + free(v->v_buf); + } + + v->v_buf = NULL; + v->v_count = 0; + v->v_max = 0; +} + +int __fx_vector_push_back( + void **vector, + const void *item, + size_t item_size, + size_t *count, + size_t *max, + const struct fx_vector_ops *ops) +{ + struct vector v = {}; + int err = 0; + + vector_wrap(&v, vector, item_size, count, max); + err = vector_push_back(&v, item, ops); + vector_unwrap(&v, vector, item_size, count, max); + + return err; +} + +void __fx_vector_pop_back( + void **vector, + size_t item_size, + size_t *count, + size_t *max, + const struct fx_vector_ops *ops) +{ + struct vector v = {}; + + vector_wrap(&v, vector, item_size, count, max); + vector_pop_back(&v, ops); + vector_unwrap(&v, vector, item_size, count, max); +} + +void *__fx_vector_emplace_back( + void **vector, + size_t item_size, + size_t *count, + size_t *max, + const struct fx_vector_ops *ops) +{ + struct vector v = {}; + void *p = 0; + + vector_wrap(&v, vector, item_size, count, max); + p = vector_emplace_back(&v, ops); + vector_unwrap(&v, vector, item_size, count, max); + + return p; +} + +void __fx_vector_trim( + void **vector, + size_t item_size, + size_t *count, + size_t *max, + const struct fx_vector_ops *ops) +{ + struct vector v = {}; + vector_wrap(&v, vector, item_size, count, max); + vector_trim(&v, ops); + vector_unwrap(&v, vector, item_size, count, max); +} + +void __fx_vector_destroy( + void **vector, + size_t item_size, + size_t *count, + size_t *max, + const struct fx_vector_ops *ops) +{ + struct vector v = {}; + vector_wrap(&v, vector, item_size, count, max); + vector_destroy(&v, ops); + vector_unwrap(&v, vector, item_size, count, max); +}