fx: add simple resizable array data structure
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
#ifndef FX_VECTOR_H_
|
||||
#define FX_VECTOR_H_
|
||||
|
||||
#include <fx/misc.h>
|
||||
#include <fx/status.h>
|
||||
#include <stddef.h>
|
||||
|
||||
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
|
||||
+270
@@ -0,0 +1,270 @@
|
||||
#include <fx/vector.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user