From 450ba063cf7ba474255a2d125f2ad9812266ec5e Mon Sep 17 00:00:00 2001 From: Robear Selwans Date: Mon, 3 Jan 2022 23:04:40 +0200 Subject: [PATCH] Added ev_vec.h Signed-off-by: Robear Selwans --- ev_vec.h | 625 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 625 insertions(+) create mode 100644 ev_vec.h diff --git a/ev_vec.h b/ev_vec.h new file mode 100644 index 0000000..9cfba9e --- /dev/null +++ b/ev_vec.h @@ -0,0 +1,625 @@ +/*! + * \file ev_vec.h + */ +#ifndef EV_VEC_HEADER +#define EV_VEC_HEADER +#include "ev_types.h" +#include "ev_numeric.h" + +#if defined(EV_VEC_SHARED) +# if defined (EV_VEC_IMPL) +# define EV_VEC_API EV_EXPORT +# else +# define EV_VEC_API EV_IMPORT +# endif +#else +# define EV_VEC_API +#endif + +#ifndef EV_VEC_INIT_CAP +/*! + * \brief Initial capacity that is first reserved when a vector is initialized + */ +#define EV_VEC_INIT_CAP 32 +#endif + +#ifndef EV_VEC_GROWTH_RATE +/*! + * \brief Rate at which a vector grows whenever a resize is needed + */ +#define EV_VEC_GROWTH_RATE 3 / 2 +#endif + +EV_TYPEDEF(ev_vec_t, void *); +TYPEDATA_GEN(ev_vec_t); +EV_TYPEDEF(ev_svec_t, void *); +TYPEDATA_GEN(ev_svec_t); + +EV_TYPEDEF(ev_vec_error_t, enum { + EV_VEC_ERR_NONE = 0, + EV_VEC_ERR_OOM = 1, +}); + +#if defined(EV_VEC_SHORTNAMES) +EV_TYPEDEF(vec_t, ev_vec_t); +TYPEDATA_GEN(vec_t); +EV_TYPEDEF(svec_t, ev_svec_t); +TYPEDATA_GEN(svec_t); + +EV_TYPEDEF(vec_error_t, ev_vec_error_t); +TYPEDATA_GEN(vec_error_t); + +# define vec(T) ev_vec(T) +# define svec(T) ev_svec(T) + +# define vec_init ev_vec_init +# define svec_init ev_svec_init +# define svec_init_w_cap ev_svec_init_w_cap +# define vec_iter_begin ev_vec_iter_begin +# define vec_iter_end ev_vec_iter_end +# define vec_iter_next ev_vec_iter_next +# define vec_foreach ev_vec_foreach +# define vec_fini ev_vec_fini +# define vec_push ev_vec_push +# define vec_append ev_vec_append +# define vec_last ev_vec_last +# define vec_len ev_vec_len +# define vec_capacity ev_vec_capacity +# define vec_clear ev_vec_clear +# define vec_setlen ev_vec_setlen +# define vec_setcapacity ev_vec_setcapacity +# define vec_grow ev_vec_grow +#endif + +/*! + * \brief For the sake of readability + * \details Sample usage: + * ``` + * ev_vec(int) v = ev_vec_init(int, 0, 0); + * ``` + */ +#define ev_vec(T) T* + +/*! + * \brief For the sake of readability + * \details Sample usage: + * ``` + * ev_svec(int) v = ev_svec_init(int, { 0, 0 }); + * ``` + */ +#define ev_svec(T) T* + +//! Metadata that is stored with a vector. Unique to each vector. +struct ev_vec_meta_t { + //! The number of elements in the vector. + u64 length; + //! The maximum length of the vector before it needs to be resized. + u64 capacity; + + //! The type data of the elements + EvTypeData typeData; + + enum { + EV_VEC_ALLOCATION_TYPE_STACK, + EV_VEC_ALLOCATION_TYPE_HEAP + } allocationType; +}; + +/*! + * \param typeData The EvTypeData for the element that the vector will contain + * + * \returns A vector object + */ +EV_VEC_API ev_vec_t +ev_vec_init_impl( + EvTypeData typeData); + +/*! + * \brief Syntactic sugar for `ev_vec_init_impl()` + * \details Sample usage: + * ``` + * ev_vec_init(int); // ev_vec_init_impl(sizeof(int), NULL, NULL); + * ev_vec_init(int, fn_destr); // ev_vec_init_impl(sizeof(int), NULL, fn_destr); + * ev_vec_init(int, fn_cpy, fn_destr); // ev_vec_init_impl(sizeof(int), fn_cpy, fn_destr); + * ``` + */ +#define ev_vec_init(T) ev_vec_init_impl(TypeData(T)) + +#define ev_svec_init(T, ...) __ev_svec_init_impl(T, EV_ARRSIZE((T[])__VA_ARGS__), __VA_ARGS__) +#define ev_svec_init_w_cap(T, cap) __ev_svec_init_w_cap_impl(T, cap) + +#define __ev_svec_init_impl(T, len, ...) \ + (ev_svec(T))&((struct { \ + struct ev_vec_meta_t meta; \ + EV_ALIGNAS(TypeData(T).alignment) T data[len]; \ + }) { \ + .meta.length = len, \ + .meta.capacity = len, \ + .meta.typeData = TypeData(T), \ + .meta.allocationType = EV_VEC_ALLOCATION_TYPE_STACK, \ + .data = __VA_ARGS__ \ + }).data + +#define __ev_svec_init_w_cap_impl(T, cap) \ + (ev_svec(T))&((struct { \ + struct ev_vec_meta_t meta; \ + EV_ALIGNAS(TypeData(T).alignment) T data[cap]; \ + }) { \ + .meta.length = 0, \ + .meta.capacity = cap, \ + .meta.typeData = TypeData(T), \ + .meta.allocationType = EV_VEC_ALLOCATION_TYPE_STACK, \ + .data = { 0 } \ + }).data + +#define ev_vec_push(v, x) ev_vec_push_impl((ev_vec_t*)&v,&x); + +/*! + * \param v The vector that we want an iterator for + * + * \returns A pointer to the first element in a vector + */ +EV_VEC_API void * +ev_vec_iter_begin( + ev_vec_t v); + +/*! + * \param v The vector that we want an iterator for + * + * \returns A pointer to the memory block right after the last element in the vector + */ +EV_VEC_API void * +ev_vec_iter_end( + ev_vec_t v); + +/*! + * \brief A function that increments an iterator to make it point to the next + * element in the vector + * + * \param v The vector that is being iterated over + * \param iter Reference to the iterator that is being incremented + */ +EV_VEC_API void +ev_vec_iter_next( + ev_vec_t v, + void **iter); + +/*! + * \brief A function that destroys a vector object. If a destructor function was + * passed while initializing the vector, then this function is called on every + * element before all reserved memory is freed. + * + * *Note*: For stack-allocated vectors (`svec`), destructors are called for + * elements but no memory is freed. + * + * \param v The vector that is being destroyed + */ +EV_VEC_API void +ev_vec_fini( + ev_vec_t v); + +/*! + * \brief A function that copies a value to the end of a vector. If a copy + * function was passed while initializing the vector, then this function is + * called to copy the new element into the vector. Otherwise, memcpy is used + * with a length of `vec_meta.elemsize`. If a resize is needed but fails due to + * 'OOM' issues, then the vector is left unchanged and VEC_ERR_OOM is returned. + * + * For `svec`, as long as the capacity is more than the current size, a push + * operation is permitted. Otherwise, the operation is treated as an OOM. + * + * \param v Reference to the vector object + * \param val A pointer to the element that is to be copied to the end of the + * vector + * + * \returns The index of the element that was just pushed. If the operation + * failed, a non-zero (vec_error_t) value is returned. + */ +EV_VEC_API int +ev_vec_push_impl( + ev_vec_t *v, + void *val); + +/*! + * \brief A function that appends the elements of an array to the end of a + * vector. If a resize is needed but fails due to 'OOM' issues, then the + * vector is left unchanged and a VEC_ERR_OOM is returned. + * + * For `svec`, as long as the capacity is more than the current size by the + * desired amount, an append operation is permitted. Otherwise, the operation + * is treated as an OOM. + * + * *NOTE* The vector's copy function is not used; this is merely a memcpy + * operation. If a deep copy is needed, individually pushing the elements of + * the array is the way to go. + * + * \param v Reference to the vector object + * \param arr A pointer to the array that is to be copied to the end of the + * vector + * \param size Number of elements in the array + * + * \returns The index of the first element that was appended to the vector. If + * the operation failed, a non-zero (vec_error_t) value is returned. + */ +EV_VEC_API u32 +ev_vec_append( + ev_vec_t *v, + void **arr, + u64 size); + +/*! + * \brief A function that copies the value at the end of a vector and removes + * it from the vector. If a copy function was passed while initializing the + * vector, then this function is used. Otherwise, memcpy is used with a length + * of `vec_meta.elemsize` + * + * \param v Reference to the vector object + * \param out A pointer to the memory block at which the popped element will be + * copied. If NULL is passed, then the element is destructed. Otherwise, the + * element is copied to `out` and the receiving code is responsible for its + * destruction. + * + * \returns An error code. If the operation was successful, then `VEC_ERR_NONE` + * is returned. + */ +EV_VEC_API ev_vec_error_t +ev_vec_pop( + ev_vec_t *v, + void *out); + +/*! + * \brief A function that returns the last element in the vector. + * + * \param v Reference to the vector object + * + * \returns Pointer to the last element in the vector. NULL if the vector is + * empty. + */ +EV_VEC_API void * +ev_vec_last( + ev_vec_t v); + +/*! + * \brief A function that returns the length of a vector + * + * \param v The vector object + * + * \returns Current length of the vector + */ +EV_VEC_API u64 +ev_vec_len( + ev_vec_t v); + +/*! + * \brief A function that returns the capacity of a vector + * + * \param v The vector object + * + * \returns Current capacity of the vector + */ +EV_VEC_API u64 +ev_vec_capacity( + ev_vec_t v); + +/*! + * \brief Calls the free operation (if exists) on every element, then sets + * the length to 0. + * + * \param v The vector object + * + * \returns 0 on success + */ +EV_VEC_API u32 +ev_vec_clear( + ev_vec_t v); + +/*! + * \brief Sets the length of the vector to `len`. + * + * \details If `len` is less than `v`'s current length, then `v`'s length is + * amended. Otherwise, the capacity is checked to make sure that there is enough + * space for the new len. + * + * For `svec`, if the `len` is more than the already allocated capacity, it is + * treated as an OOM. + * + * \param v Reference to the vector object + * \param len The desired new length + * + * \returns `VEC_ERR_NONE` on success + */ +EV_VEC_API ev_vec_error_t +ev_vec_setlen( + ev_vec_t *v, + u64 len); + +/*! + * \brief Sets the capacity of the vector to `cap`. + * + * \param v Reference to the vector object + * \param cap The desired new capacity + * + * For stack-allocated vectors, `VEC_ERR_OOM` is returned + * + * \returns `VEC_ERR_NONE` on success, `VEC_ERR_OOM` on OOM + */ +EV_VEC_API ev_vec_error_t +ev_vec_setcapacity( + ev_vec_t *v, + u64 cap); + +/*! + * \brief Grows the vector's capacity by a factor of `VEC_GROWTH_RATE` + * + * \param Reference to the vector object + * + * \returns `VEC_ERR_NONE` on success + */ +EV_VEC_API ev_vec_error_t +ev_vec_grow( + ev_vec_t *v); + +#ifdef EV_VEC_IMPLEMENTATION +#undef EV_VEC_IMPLEMENTATION + +#ifdef EV_VEC_API_CHECK +#define EV_VEC_CHECK(x) do { x; } while(0) +#else +#define EV_VEC_CHECK(x) +#endif + +#include +#include + +#define ev_vec_meta(v) \ + ((struct ev_vec_meta_t *)v) - 1 + +#define __ev_vec_getmeta(v) \ + struct ev_vec_meta_t *metadata = ((struct ev_vec_meta_t *)(v)) - 1; + +#define __ev_vec_syncmeta(v) \ + metadata = ((struct ev_vec_meta_t *)(v)) - 1; + +ev_vec_t +ev_vec_init_impl( + EvTypeData typeData) +{ + void *v = malloc(sizeof(struct ev_vec_meta_t) + (EV_VEC_INIT_CAP * typeData.size)); + if (!v) + return NULL; + + struct ev_vec_meta_t *metadata = (struct ev_vec_meta_t *)v; + *metadata = (struct ev_vec_meta_t){ + .length = 0, + .capacity = EV_VEC_INIT_CAP, + .allocationType = EV_VEC_ALLOCATION_TYPE_HEAP, + .typeData = typeData + }; + + return metadata + 1; +} + +void +ev_vec_fini( + ev_vec_t v) +{ + __ev_vec_getmeta(v) + + if (metadata->typeData.free_fn) { + for (void *elem = ev_vec_iter_begin(v); elem != ev_vec_iter_end(v); + ev_vec_iter_next(v, &elem)) { + metadata->typeData.free_fn(elem); + } + } + if(metadata->allocationType == EV_VEC_ALLOCATION_TYPE_HEAP) { + free(metadata); + } +} + +int +ev_vec_push_impl( + ev_vec_t *v, + void *val) +{ + __ev_vec_getmeta(*v) + + if (metadata->length == metadata->capacity) { + ev_vec_error_t grow_err = ev_vec_grow(v); + if(grow_err) { + return grow_err; + } else { + __ev_vec_syncmeta(*v) + } + } + + void *dst = ((char *)*v) + (metadata->length * metadata->typeData.size); + if (metadata->typeData.copy_fn) { + metadata->typeData.copy_fn(dst, val); + } else { + memcpy(dst, val, metadata->typeData.size); + } + + return (int)metadata->length++; +} + +void * +ev_vec_iter_begin( + vec_t v) +{ + return v; +} + +void * +ev_vec_iter_end( + vec_t v) +{ + __ev_vec_getmeta(v) + + return ((char *)v) + (metadata->typeData.size * metadata->length); +} + +void +ev_vec_iter_next( + vec_t v, + void **iter) +{ + __ev_vec_getmeta(v) + *iter = ((char*)*iter) + metadata->typeData.size; +} + +EV_VEC_API u32 +ev_vec_append( + ev_vec_t *v, + void **arr, + u64 size) +{ + __ev_vec_getmeta(*v) + size_t old_len = metadata->length; + size_t req_len = old_len + size; + + ev_vec_error_t setlen_err = ev_vec_setlen(v, req_len); + if(setlen_err) { + return setlen_err; + } + __ev_vec_syncmeta(*v) + + void *dst = ((char *)*v) + (old_len * metadata->typeData.size); + memcpy(dst, *arr, metadata->typeData.size * size); + + return (int)old_len; +} + +ev_vec_error_t +ev_vec_pop( + ev_vec_t *v, + void *out) +{ + __ev_vec_getmeta(*v) + + if(out != NULL) { + void *src = ((char *)*v) + ((metadata->length-1) * metadata->typeData.size); + if (metadata->typeData.copy_fn) { + metadata->typeData.copy_fn(out, src); + } else { + memcpy(out, src, metadata->typeData.size); + } + } else { + void *elem = ((char *)*v) + ((metadata->length-1) * metadata->typeData.size); + if (metadata->typeData.free_fn) { + metadata->typeData.free_fn(elem); + } + } + + metadata->length--; + + return EV_VEC_ERR_NONE; +} + +void * +ev_vec_last( + ev_vec_t v) +{ + __ev_vec_getmeta(v) + + if(metadata->length == 0) { + return NULL; + } + + return ((char *)v) + ((metadata->length-1) * metadata->typeData.size); +} + +u64 +ev_vec_len( + ev_vec_t v) +{ + __ev_vec_getmeta(v) + return metadata->length; +} + +u64 +ev_vec_capacity( + ev_vec_t v) +{ + __ev_vec_getmeta(v) + return metadata->capacity; +} + +u32 +ev_vec_clear( + ev_vec_t v) +{ + __ev_vec_getmeta(v) + + if (metadata->typeData.free_fn) { + for (void *elem = ev_vec_iter_begin(v); elem != ev_vec_iter_end(v); + ev_vec_iter_next(v, &elem)) { + metadata->typeData.free_fn(elem); + } + } + + metadata->length = 0; + return 0; +} + +ev_vec_error_t +ev_vec_setlen( + ev_vec_t *v, + u64 len) +{ + __ev_vec_getmeta(*v) + + while(len > metadata->capacity) { + ev_vec_error_t grow_err = ev_vec_grow(v); + if(grow_err) { + return grow_err; + } + __ev_vec_getmeta(*v) + } + + metadata->length = len; + return EV_VEC_ERR_NONE; +} + +ev_vec_error_t +ev_vec_setcapacity( + ev_vec_t *v, + u64 cap) +{ + __ev_vec_getmeta(*v) + + if(metadata->allocationType == EV_VEC_ALLOCATION_TYPE_STACK) { + return EV_VEC_ERR_OOM; + } + + if(metadata->capacity == cap) { + return EV_VEC_ERR_NONE; + } + + void *buf = ((char *)(*v) - sizeof(struct ev_vec_meta_t)); + void *tmp = realloc(buf, sizeof(struct ev_vec_meta_t) + (cap * metadata->typeData.size)); + + if (!tmp) { + return EV_VEC_ERR_OOM; + } + + if(buf != tmp) { + buf = tmp; + metadata = (struct ev_vec_meta_t *)buf; + *v = (char *)buf + sizeof(struct ev_vec_meta_t); + } + + metadata->capacity = cap; + return EV_VEC_ERR_NONE; +} + +ev_vec_error_t +ev_vec_grow( + ev_vec_t *v) +{ + __ev_vec_getmeta(*v) + return vec_setcapacity(v, metadata->capacity * EV_VEC_GROWTH_RATE); +} + +#endif + +#endif