vector move callbacks allow mie_name instances to be stored in movable memory, as the internal bst pointers can now be fixed during vector resize operations.
237 lines
4.9 KiB
C
237 lines
4.9 KiB
C
#include <mie/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 mie_vector_ops *ops)
|
|
{
|
|
size_t items_to_copy = MIN(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 mie_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 mie_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 mie_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 mie_vector_ops *ops)
|
|
{
|
|
if (v->v_count > 0) {
|
|
v->v_count--;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void *vector_emplace_back(struct vector *v, const struct mie_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 mie_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 mie_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 mie_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 __mie_vector_push_back(
|
|
void **vector, const void *item, size_t item_size, size_t *count,
|
|
size_t *max, const struct mie_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 __mie_vector_pop_back(
|
|
void **vector, size_t item_size, size_t *count, size_t *max,
|
|
const struct mie_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 *__mie_vector_emplace_back(
|
|
void **vector, size_t item_size, size_t *count, size_t *max,
|
|
const struct mie_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 __mie_vector_trim(
|
|
void **vector, size_t item_size, size_t *count, size_t *max,
|
|
const struct mie_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 __mie_vector_destroy(
|
|
void **vector, size_t item_size, size_t *count, size_t *max,
|
|
const struct mie_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);
|
|
}
|