#ifndef GUF_DBUF_H #define GUF_DBUF_H #include "guf_common.h" #include "guf_assert.h" #include "guf_alloc.h" #define GUF_DBUF_FOREACH(DBUF, ELEM_TYPE, ELEM_PTR_NAME) for (ELEM_TYPE *ELEM_PTR_NAME = (DBUF).data, *guf_foreach_end = (DBUF).data ? (DBUF).data + (DBUF).size : NULL; ELEM_PTR_NAME != guf_foreach_end; ++ELEM_PTR_NAME) #endif /* Template parameters: - GUF_T: The value type stored in the container. - GUF_CNT_NAME: The typename of the resulting container (default: dbuf_GUF_T) - GUF_T_IS_INTEGRAL_TYPE: whether the type is an integral type (if defined, must not define COPY, MOVE, FREE and EQ) - GUF_T_COPY: cpy function with signature GUF_T *copy(GUF_T *dst, const GUF_T *src, void *ctx) (default: copy by value) - GUF_T_MOVE: move function with signature GUF_T *move(GUF_T *dst, GUF_T *src, void *ctx) (default: undefined) - GUF_T_FREE: free function with signature void free(GUF_T *a, void *ctx) (default: undefined) - GUF_T_EQ: equality function with signature bool eq(const GUF_T *a, const GUF_T *a) (default: undefined, or equality by value if GUF_T_IS_INTEGRAL_TYPE is defined) - GUF_DBUF_INITIAL_CAP: The capacity used for the first allocation if the container got initialised with a capacity of zero (default: 8) - GUF_DBUF_USE_GROWTH_FAC_ONE_POINT_FIVE: If defined, always use 1.5 as the growth-factor (instead of 2) */ #ifndef GUF_T #error "Undefined container template GUF_T" #endif #ifndef GUF_CNT_NAME #define GUF_CNT_NAME GUF_CAT(dbuf_, GUF_T) #endif // Used for the first growth if dbuf->capacity is zero. #ifndef GUF_DBUF_INITIAL_CAP #define GUF_DBUF_INITIAL_CAP 8 #endif #if (defined(GUF_T_COPY) || defined(GUF_T_MOVE)) && !defined(GUF_T_FREE) #error "Must define GUF_T_FREE because GUF_T_COPY or GUF_T_MOVE is defined" #endif #if defined(GUF_T_IS_INTEGRAL_TYPE) && (defined(GUF_T_COPY) || defined(GUF_CNT_MOVE) || defined(GUF_CNT_FREE) || defined(GUF_T_EQ)) #error "Integral types do not need COPY, MOVE, FREE or EQ functions" #endif #ifdef GUF_IMPL_STATIC #define GUF_FN_KEYWORDS static #else #define GUF_FN_KEYWORDS #endif typedef struct GUF_CNT_NAME { GUF_T *data; ptrdiff_t size, capacity; guf_allocator *allocator; #ifdef GUF_CNT_WITH_ELEM_CTX void *elem_ctx; // NULL by default; is passed as the ctx argument to GUF_T_COPY, GUF_T_MOVE and GUF_T_FREE #endif } GUF_CNT_NAME; /* - Regular iterator: base is always NULL - Reverse iterator: base points the element after ptr Examples: - rbegin(): base points to end().ptr, and ptr to end().ptr - 1 - rend(): base points to begin().ptr and ptr to NULL - reverse iterator for the first element of the container: base points to the second element, ptr points to the first element */ typedef struct GUF_CAT(GUF_CNT_NAME, _iter) { GUF_T *ptr; GUF_T *base; } GUF_CAT(GUF_CNT_NAME, _iter); GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _valid)(const GUF_CNT_NAME* dbuf); GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity, guf_err *err); GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity); GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_init)(GUF_CNT_NAME *dbuf, ptrdiff_t capacity, guf_allocator *allocator, guf_err *err); GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _init)(GUF_CNT_NAME *dbuf, ptrdiff_t start_cap, guf_allocator *allocator); GUF_FN_KEYWORDS GUF_CNT_NAME GUF_CAT(GUF_CNT_NAME, _new)(guf_allocator *allocator); GUF_FN_KEYWORDS GUF_CNT_NAME GUF_CAT(GUF_CNT_NAME, _try_new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator, guf_err *err); GUF_FN_KEYWORDS GUF_CNT_NAME GUF_CAT(GUF_CNT_NAME, _new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator); #ifdef GUF_CNT_WITH_ELEM_CTX GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _set_elem_ctx)(GUF_CNT_NAME *dbuf, void *elem_ctx); #endif GUF_FN_KEYWORDS void *GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(const GUF_CNT_NAME *dbuf); // Always returns NULL if GUF_CNT_WITH_ELEM_CTX is not defined GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _free)(GUF_CNT_NAME *dbuf, void *ctx); GUF_FN_KEYWORDS GUF_CNT_NAME *GUF_CAT(GUF_CNT_NAME, _copy)(GUF_CNT_NAME *dst, const GUF_CNT_NAME *src, void *ctx); GUF_FN_KEYWORDS GUF_CNT_NAME *GUF_CAT(GUF_CNT_NAME, _move)(GUF_CNT_NAME *dst, GUF_CNT_NAME *src, void *ctx); GUF_FN_KEYWORDS ptrdiff_t GUF_CAT(GUF_CNT_NAME, _try_get_next_capacity)(ptrdiff_t old_cap, guf_err *err); GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_grow_if_full)(GUF_CNT_NAME *dbuf, guf_err *err); GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_shrink_to_fit)(GUF_CNT_NAME *dbuf, guf_err *err); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_insert)(GUF_CNT_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt, guf_err *err); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _insert)(GUF_CNT_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_insert_val)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx, guf_err *err); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _insert_val)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx); #ifdef GUF_T_COPY GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_insert_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx, guf_err *err); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _insert_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx); #endif GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_push)(GUF_CNT_NAME *dbuf, GUF_T *elem, guf_cpy_opt cpy_opt, guf_err *err); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _push)(GUF_CNT_NAME *dbuf, GUF_T *elem, guf_cpy_opt cpy_opt); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_push_val)(GUF_CNT_NAME *dbuf, GUF_T elem, guf_err *err); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _push_val)(GUF_CNT_NAME *dbuf, GUF_T elem); #ifdef GUF_T_COPY GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_push_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem, guf_err *err); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _push_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem); #endif GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _try_erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx, guf_err *err); GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx); GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _try_pop)(GUF_CNT_NAME *dbuf, guf_err *err); GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _pop)(GUF_CNT_NAME *dbuf); GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _try_pop_cpy)(GUF_CNT_NAME *dbuf, guf_cpy_opt cpy_opt, guf_err *err); GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _pop_cpy)(GUF_CNT_NAME *dbuf, guf_cpy_opt cpy_opt); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx, guf_err *err); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_front)(GUF_CNT_NAME *dbuf, guf_err *err); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _front)(GUF_CNT_NAME *dbuf); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_back)(GUF_CNT_NAME *dbuf, guf_err *err); GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _back)(GUF_CNT_NAME *dbuf); GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _begin)(const GUF_CNT_NAME* dbuf); GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _end)(const GUF_CNT_NAME* dbuf); GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _rbegin)(const GUF_CNT_NAME* dbuf); GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _rend)(const GUF_CNT_NAME* dbuf); GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _iter_next)(const GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) it, ptrdiff_t step); GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _iter_at_idx)(const GUF_CNT_NAME* dbuf, ptrdiff_t idx); GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _reverse_iter_at_idx)(const GUF_CNT_NAME* dbuf, ptrdiff_t idx); GUF_FN_KEYWORDS ptrdiff_t GUF_CAT(GUF_CNT_NAME, _iter_to_idx)(const GUF_CNT_NAME* dbuf, GUF_CAT(GUF_CNT_NAME, _iter) it); #if defined(GUF_T_IS_INTEGRAL_TYPE) || defined(GUF_T_EQ) GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find)(GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) begin, GUF_CAT(GUF_CNT_NAME, _iter) end, const GUF_T *needle); GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find_val)(GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) begin, GUF_CAT(GUF_CNT_NAME, _iter) end, GUF_T needle_val); GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find_if)(GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) begin, GUF_CAT(GUF_CNT_NAME, _iter) end, bool (*predicate)(const GUF_T *)); GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _contains)(GUF_CNT_NAME *dbuf, const GUF_T *needle); GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _contains_val)(GUF_CNT_NAME *dbuf, GUF_T needle); #endif #if defined(GUF_IMPL) || defined(GUF_IMPL_STATIC) GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _valid)(const GUF_CNT_NAME* dbuf) { if (!dbuf) { return false; } bool valid_data_ptr = (!dbuf->data && !dbuf->capacity) || (dbuf->data && dbuf->capacity); bool valid_allocator = dbuf->allocator && dbuf->allocator->alloc && dbuf->allocator->realloc && dbuf->allocator->free; return valid_data_ptr && valid_allocator && dbuf->capacity >= 0 && dbuf->size >= 0 && dbuf->size <= dbuf->capacity; } GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity, guf_err *err) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); GUF_ASSERT_RELEASE(min_capacity >= 0); if (min_capacity <= dbuf->capacity || min_capacity == 0) { guf_err_set_if_not_null(err, GUF_ERR_NONE); return; } GUF_ASSERT(min_capacity > 0); ptrdiff_t new_alloc_bytes = -1; if (!guf_size_calc_safe(min_capacity, sizeof(GUF_T), &new_alloc_bytes)) { guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_reserve: overflow of ptrdiff_t")); return; } GUF_ASSERT(new_alloc_bytes > 0); if (!dbuf->data) { // a.) Allocate. GUF_T *data = dbuf->allocator->alloc(new_alloc_bytes, dbuf->allocator->ctx); if (!data) { guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_reserve: failed to allocate initial dbuf->data")); return; } dbuf->data = data; } else { // b.) Re-allocate. ptrdiff_t old_alloc_bytes = dbuf->capacity * sizeof(GUF_T); GUF_ASSERT(old_alloc_bytes < new_alloc_bytes); GUF_T *data = dbuf->allocator->realloc(dbuf->data, old_alloc_bytes, new_alloc_bytes, dbuf->allocator->ctx); if (!data) { guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_reserve: failed to reallocate dbuf->data")); return; } dbuf->data = data; } guf_err_set_if_not_null(err, GUF_ERR_NONE); dbuf->capacity = min_capacity; GUF_ASSERT(dbuf->data && dbuf->capacity); } GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity) { GUF_CAT(GUF_CNT_NAME, _try_reserve)(dbuf, min_capacity, NULL); } GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_init)(GUF_CNT_NAME *dbuf, ptrdiff_t capacity, guf_allocator *allocator, guf_err *err) { GUF_ASSERT_RELEASE(dbuf); GUF_ASSERT_RELEASE(capacity >= 0); if (dbuf->size != 0 || dbuf->capacity != 0 || dbuf->data || GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)) { guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in function dbuf_try_init: dbuf might have been already initialised")); return; } dbuf->size = dbuf->capacity = 0; #ifdef GUF_CNT_WITH_ELEM_CTX dbuf->elem_ctx = NULL; #endif if (!allocator || !(allocator->alloc && allocator->realloc && allocator->free)) { guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in function dbuf_try_init: allocator (or one of it's function pointers) is NULL")); return; } else { dbuf->allocator = allocator; } if (capacity == 0) { dbuf->data = NULL; guf_err_set_if_not_null(err, GUF_ERR_NONE); } else { GUF_CAT(GUF_CNT_NAME, _try_reserve)(dbuf, capacity, err); } } GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _init)(GUF_CNT_NAME *dbuf, ptrdiff_t start_cap, guf_allocator *allocator) { GUF_CAT(GUF_CNT_NAME, _try_init)(dbuf, start_cap, allocator, NULL); } GUF_FN_KEYWORDS GUF_CNT_NAME GUF_CAT(GUF_CNT_NAME, _new)(guf_allocator *allocator) { GUF_CNT_NAME dbuf = {0}; GUF_CAT(GUF_CNT_NAME, _init)(&dbuf, 0, allocator); GUF_ASSERT(dbuf.size == 0 && dbuf.capacity == 0); return dbuf; } GUF_FN_KEYWORDS GUF_CNT_NAME GUF_CAT(GUF_CNT_NAME, _try_new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator, guf_err *err) { GUF_CNT_NAME dbuf = {0}; GUF_CAT(GUF_CNT_NAME, _try_init)(&dbuf, capacity, allocator, err); if (err && *err != GUF_ERR_NONE) { return (GUF_CNT_NAME){0}; } guf_err_set_if_not_null(err, GUF_ERR_NONE); return dbuf; } GUF_FN_KEYWORDS GUF_CNT_NAME GUF_CAT(GUF_CNT_NAME, _new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator) { return GUF_CAT(GUF_CNT_NAME, _try_new_with_capacity)(capacity, allocator, NULL); } #ifdef GUF_CNT_WITH_ELEM_CTX GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _set_elem_ctx)(GUF_CNT_NAME *dbuf, void *elem_ctx) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); #ifdef GUF_CNT_WITH_ELEM_CTX dbuf->elem_ctx = elem_ctx; #else (void)elem_ctx; GUF_ASSERT_RELEASE(false); #endif } #endif GUF_FN_KEYWORDS void *GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(const GUF_CNT_NAME *dbuf) { #ifdef GUF_CNT_WITH_ELEM_CTX return dbuf->elem_ctx; #else (void)dbuf; return NULL; #endif } GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _free)(GUF_CNT_NAME *dbuf, void *ctx) { (void)ctx; GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->capacity == 0) { GUF_ASSERT_RELEASE(!dbuf->data); GUF_ASSERT_RELEASE(dbuf->size == 0); return; } GUF_ASSERT_RELEASE(dbuf->data); #ifdef GUF_T_FREE for (ptrdiff_t idx = 0; idx < dbuf->size; ++idx) { GUF_T_FREE(dbuf->data + idx, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); } #endif dbuf->allocator->free(dbuf->data, sizeof(GUF_T) * dbuf->capacity, dbuf->allocator->ctx); dbuf->data = NULL; dbuf->capacity = dbuf->size = 0; dbuf->allocator = NULL; #ifdef GUF_CNT_WITH_ELEM_CTX dbuf->elem_ctx = NULL; #endif } GUF_FN_KEYWORDS GUF_CNT_NAME *GUF_CAT(GUF_CNT_NAME, _copy)(GUF_CNT_NAME *dst, const GUF_CNT_NAME *src, void *ctx) { (void)ctx; if (!dst || !src || GUF_CAT(GUF_CNT_NAME, _valid)(src)) { return NULL; } if (src->capacity == 0) { GUF_ASSERT(!src->data); GUF_ASSERT(src->size == 0); *dst = *src; return dst; } GUF_ASSERT(src->data && src->capacity > 0); dst->allocator = src->allocator; #ifdef GUF_CNT_WITH_ELEM_CTX dst->elem_ctx = src->elem_ctx; #endif if (src->size == 0) { dst->capacity = 0; dst->size = 0; dst->data = NULL; return dst; } else { ptrdiff_t dst_cap = src->size; GUF_T *dst_data = src->allocator->alloc(dst_cap * sizeof(GUF_T), src->allocator->ctx); dst->data = dst_data; if (!dst->data) { dst->capacity = 0; dst->size = 0; return NULL; } else { dst->capacity = dst_cap; dst->size = src->size; } for (ptrdiff_t i = 0; i < src->size; ++i) { #ifdef GUF_T_COPY GUF_T *cpy_success = GUF_T_COPY(dst->data + i, src->data + i, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(src)); if (!cpy_success) { dst->size = i; return NULL; } #else dst->data[i] = src->data[i]; #endif } return dst; } } GUF_FN_KEYWORDS GUF_CNT_NAME *GUF_CAT(GUF_CNT_NAME, _move)(GUF_CNT_NAME *dst, GUF_CNT_NAME *src, void *ctx) { (void)ctx; if (!dst || !src) { return NULL; } *dst = *src; src->capacity = 0; src->size = 0; src->data = NULL; src->allocator = NULL; #ifdef GUF_CNT_WITH_ELEM_CTX src->elem_ctx = NULL; #endif return dst; } GUF_FN_KEYWORDS ptrdiff_t GUF_CAT(GUF_CNT_NAME, _try_get_next_capacity)(ptrdiff_t old_cap, guf_err *err) { GUF_ASSERT_RELEASE(old_cap >= 0); size_t new_cap = 0; if (old_cap == 0) { new_cap = GUF_DBUF_INITIAL_CAP; } else if (old_cap < 8) { new_cap = (size_t)old_cap * 2ull; } else { #ifdef GUF_DBUF_USE_GROWTH_FAC_ONE_POINT_FIVE new_cap = (size_t)old_cap * 3ull / 2ull; #else new_cap = (size_t)old_cap * 2ull; #endif } new_cap = GUF_MIN(new_cap, PTRDIFF_MAX); if (new_cap <= (size_t)old_cap) { // Detect overflow. guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, GUF_ERR_MSG("in function dbuf_try_get_next_capacity: next capacity would overflow ptrdiff_t")); return -1; } else { guf_err_set_if_not_null(err, GUF_ERR_NONE); return new_cap; } } GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_grow_if_full)(GUF_CNT_NAME *dbuf, guf_err *err) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); GUF_ASSERT_RELEASE(dbuf->capacity >= 0 && dbuf->size >= 0); if (dbuf->size == dbuf->capacity) { ptrdiff_t next_cap = GUF_CAT(GUF_CNT_NAME, _try_get_next_capacity)(dbuf->capacity, err); if (err && *err != GUF_ERR_NONE) { return; } GUF_ASSERT(next_cap > 0); GUF_CAT(GUF_CNT_NAME, _try_reserve)(dbuf, next_cap, err); if (err && *err != GUF_ERR_NONE) { return; } } GUF_ASSERT_RELEASE(dbuf->size < dbuf->capacity); guf_err_set_if_not_null(err, GUF_ERR_NONE); } static inline bool GUF_CAT(GUF_CNT_NAME, _copy_opt_available)(guf_cpy_opt cpy_opt) { if (cpy_opt == GUF_CPY_DEEP) { #ifdef GUF_T_COPY return true; #else return false; #endif } else if (cpy_opt == GUF_CPY_MOVE) { #ifdef GUF_T_MOVE return true; #else return false; #endif } else if (cpy_opt == GUF_CPY_VALUE) { return true; } else { return false; } } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_insert)(GUF_CNT_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt, guf_err *err) { GUF_ASSERT(GUF_CAT(GUF_CNT_NAME,_valid)(dbuf)); if (idx < 0 || idx > dbuf->size) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_insert")); return NULL; } GUF_CAT(GUF_CNT_NAME, _try_grow_if_full)(dbuf, err); if (err && *err != GUF_ERR_NONE) { return NULL; } for (ptrdiff_t free_idx = dbuf->size; free_idx > idx; --free_idx) { // Shift (Make space by moving elements to the right, or do nothing if idx == dbuf->size) GUF_ASSERT(free_idx >= 1 && free_idx < dbuf->capacity); dbuf->data[free_idx] = dbuf->data[free_idx - 1]; } GUF_T *dst = dbuf->data + idx; if (!GUF_CAT(GUF_CNT_NAME, _copy_opt_available)(cpy_opt)) { guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in function " GUF_STRINGIFY(GUF_CAT(GUF_CNT_NAME, _copy_opt_available)) ": cpy_opt unavailable")); return NULL; } else if (cpy_opt == GUF_CPY_DEEP) { #ifdef GUF_T_COPY dst = GUF_T_COPY(dst, elem, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); #else GUF_ASSERT_RELEASE(false); #endif } else if (cpy_opt == GUF_CPY_MOVE) { #ifdef GUF_T_MOVE dst = GUF_T_MOVE(dst, elem, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); #else GUF_ASSERT_RELEASE(false); #endif } else { GUF_ASSERT_RELEASE(cpy_opt == GUF_CPY_VALUE); *dst = *elem; } if (!dst) { guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_insert: Failed to copy elem")); for (ptrdiff_t free_idx = idx; free_idx < dbuf->size; ++free_idx) { // Undo shift to restore dbuf's state before the failed insert. GUF_ASSERT(free_idx + 1 < dbuf->capacity); dbuf->data[free_idx] = dbuf->data[free_idx + 1]; } return NULL; } else { dbuf->size++; guf_err_set_if_not_null(err, GUF_ERR_NONE); return dst; } } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _insert)(GUF_CNT_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt) { return GUF_CAT(GUF_CNT_NAME, _try_insert)(dbuf, elem, idx, cpy_opt, NULL); } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_insert_val)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx, guf_err *err) { return GUF_CAT(GUF_CNT_NAME, _try_insert)(dbuf, &elem, idx, GUF_CPY_VALUE, err); } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _insert_val)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx) { return GUF_CAT(GUF_CNT_NAME, _try_insert_val)(dbuf, elem, idx, NULL); } #ifdef GUF_T_COPY GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_insert_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx, guf_err *err) { return GUF_CAT(GUF_CNT_NAME, _try_insert)(dbuf, &elem, idx, GUF_CPY_DEEP, err); } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _insert_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx) { return GUF_CAT(GUF_CNT_NAME, _try_insert_val_cpy)(dbuf, elem, idx, NULL); } #endif GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_push)(GUF_CNT_NAME *dbuf, GUF_T *elem, guf_cpy_opt cpy_opt, guf_err *err) { GUF_ASSERT(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); return GUF_CAT(GUF_CNT_NAME, _try_insert)(dbuf, elem, dbuf->size, cpy_opt, err); } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _push)(GUF_CNT_NAME *dbuf, GUF_T *elem, guf_cpy_opt cpy_opt) { return GUF_CAT(GUF_CNT_NAME, _try_push)(dbuf, elem, cpy_opt, NULL); } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_push_val)(GUF_CNT_NAME *dbuf, GUF_T elem, guf_err *err) { return GUF_CAT(GUF_CNT_NAME, _try_push)(dbuf, &elem, GUF_CPY_VALUE, err); } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _push_val)(GUF_CNT_NAME *dbuf, GUF_T elem) { return GUF_CAT(GUF_CNT_NAME, _try_push_val)(dbuf, elem, NULL); } #ifdef GUF_T_COPY GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_push_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem, guf_err *err) { return GUF_CAT(GUF_CNT_NAME, _try_push)(dbuf, &elem, GUF_CPY_DEEP, err); } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _push_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem) { return GUF_CAT(GUF_CNT_NAME, _try_push_val_cpy)(dbuf, elem, NULL); } #endif GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _try_erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx, guf_err *err) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->size == 0) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_erase: cannot erase from empty buffer")); return false; } if (idx < 0 || idx >= dbuf->size) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_erase")); return false; } #ifdef GUF_T_FREE GUF_T_FREE(dbuf->data + idx, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); #endif for (ptrdiff_t free_idx = idx; free_idx < dbuf->size - 1; ++free_idx) { // Make space by moving elements to the left if necessary. GUF_ASSERT(free_idx + 1 < dbuf->size); dbuf->data[free_idx] = dbuf->data[free_idx + 1]; } dbuf->size--; guf_err_set_if_not_null(err, GUF_ERR_NONE); return true; } GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx) { GUF_CAT(GUF_CNT_NAME, _try_erase)(dbuf, idx, NULL); } GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _try_pop)(GUF_CNT_NAME *dbuf, guf_err *err) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->size == 0) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_pop: Cannot pop from empty dbuf")); return false; } #ifdef GUF_T_FREE GUF_T *popped = dbuf->data + --dbuf->size; GUF_T_FREE(popped, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); #else --dbuf->size; #endif guf_err_set_if_not_null(err, GUF_ERR_NONE); return true; } GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _pop)(GUF_CNT_NAME *dbuf) { GUF_CAT(GUF_CNT_NAME, _try_pop)(dbuf, NULL); } GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _try_pop_cpy)(GUF_CNT_NAME *dbuf, guf_cpy_opt cpy_opt, guf_err *err) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->size == 0) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_pop_cpy: Cannot pop from empty dbuf")); GUF_T dummy; memset(&dummy, 0, sizeof(GUF_T)); return dummy; } GUF_T *popped = dbuf->data + (dbuf->size - 1); GUF_T popped_val; GUF_T *dst = &popped_val; if (!GUF_CAT(GUF_CNT_NAME, _copy_opt_available)(cpy_opt)) { guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in function " GUF_STRINGIFY(GUF_CAT(GUF_CNT_NAME, _copy_opt_available)) ": cpy_opt unavailable")); memset(&popped_val, 0, sizeof(popped_val)); return popped_val; } else if (cpy_opt == GUF_CPY_DEEP) { #ifdef GUF_T_COPY dst = GUF_T_COPY(dst, popped, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); #else GUF_ASSERT_RELEASE(false); #endif } else if (cpy_opt == GUF_CPY_MOVE) { #ifdef GUF_T_MOVE dst = GUF_T_MOVE(dst, popped, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); #else GUF_ASSERT_RELEASE(false); #endif } else { GUF_ASSERT_RELEASE(cpy_opt == GUF_CPY_VALUE); *dst = *popped; } if (!dst) { guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_pop_cpy: Failed on copy")); memset(&popped_val, 0, sizeof(GUF_T)); return popped_val; // Return a dummy value in case the copy failed (as we have to return something). } else { #ifdef GUF_T_FREE if (cpy_opt == GUF_CPY_DEEP) { GUF_T_FREE(popped, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); } #endif dbuf->size--; guf_err_set_if_not_null(err, GUF_ERR_NONE); return popped_val; } } GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _pop_cpy)(GUF_CNT_NAME *dbuf, guf_cpy_opt cpy_opt) { return GUF_CAT(GUF_CNT_NAME, _try_pop_cpy)(dbuf, cpy_opt, NULL); } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx, guf_err *err) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->size == 0) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_at: dbuf is empty")); return NULL; } if (idx < 0 || idx >= dbuf->size) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_at")); return NULL; } GUF_ASSERT(dbuf->data); guf_err_set_if_not_null(err, GUF_ERR_NONE); return dbuf->data + idx; } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx) { return GUF_CAT(GUF_CNT_NAME, _try_at)(dbuf, idx, NULL); } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_front)(GUF_CNT_NAME *dbuf, guf_err *err) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->size == 0) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_front: dbuf is empty")); return NULL; } return dbuf->data + 0; } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _front)(GUF_CNT_NAME *dbuf) { return GUF_CAT(GUF_CNT_NAME, _try_front)(dbuf, NULL); } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_back)(GUF_CNT_NAME *dbuf, guf_err *err) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->size == 0) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_back: dbuf is empty")); return NULL; } guf_err_set_if_not_null(err, GUF_ERR_NONE); return dbuf->data + (dbuf->size - 1); } GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _back)(GUF_CNT_NAME *dbuf) { return GUF_CAT(GUF_CNT_NAME, _try_back)(dbuf, NULL); } GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_shrink_to_fit)(GUF_CNT_NAME *dbuf, guf_err *err) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); const ptrdiff_t new_capacity = dbuf->size; if (new_capacity == dbuf->capacity || (!dbuf->data && !dbuf->capacity)) { guf_err_set_if_not_null(err, GUF_ERR_NONE); return; } GUF_ASSERT_RELEASE(new_capacity < dbuf->capacity); GUF_ASSERT_RELEASE(dbuf->data); if (new_capacity == 0) { dbuf->allocator->free(dbuf->data, sizeof(GUF_T) * dbuf->capacity, dbuf->allocator->ctx); return; } GUF_T *data = dbuf->allocator->realloc(dbuf->data, new_capacity, sizeof(GUF_T), dbuf->allocator->ctx); if (!data) { guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function _try_shrink_to_fit")); return; } dbuf->data = data; dbuf->capacity = new_capacity; guf_err_set_if_not_null(err, GUF_ERR_NONE); } /* Iterator functions */ #define ITER_PTR(it) !it.reverse ? (it.iter.ptr) : (it.rev_iter.ptr - 1) GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _begin)(const GUF_CNT_NAME* dbuf) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); return (GUF_CAT(GUF_CNT_NAME, _iter)) { .ptr = dbuf->data, .base = NULL }; } GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _end)(const GUF_CNT_NAME* dbuf) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); return (GUF_CAT(GUF_CNT_NAME, _iter)) { .ptr = dbuf->data && dbuf->size ? dbuf->data + dbuf->size : NULL, .base = NULL }; } GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _rbegin)(const GUF_CNT_NAME* dbuf) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); return (GUF_CAT(GUF_CNT_NAME, _iter)) { .base = dbuf->data && dbuf->size ? dbuf->data + dbuf->size : NULL, .ptr = dbuf->data && dbuf->size ? dbuf->data + (dbuf->size - 1) : NULL }; } GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _rend)(const GUF_CNT_NAME* dbuf) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); return (GUF_CAT(GUF_CNT_NAME, _iter)) { .base = dbuf->data && dbuf->size ? dbuf->data : NULL, .ptr = NULL }; } GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _iter_at_idx)(const GUF_CNT_NAME* dbuf, ptrdiff_t idx) { GUF_ASSERT(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); GUF_CAT(GUF_CNT_NAME, _iter) it; it.base = NULL; if (!dbuf->data || !dbuf->size) { it.ptr = NULL; return it; } if (idx < 0) { it.ptr = dbuf->data; } else if (idx > dbuf->size) { it.ptr = dbuf->data + dbuf->size; } else { it.ptr = dbuf->data + idx; } return it; } GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _reverse_iter_at_idx)(const GUF_CNT_NAME* dbuf, ptrdiff_t idx) { GUF_CAT(GUF_CNT_NAME, _iter) it = GUF_CAT(GUF_CNT_NAME, _iter_at_idx)(dbuf, idx); if (!dbuf->data || !dbuf->size) { it.base = NULL; it.ptr = NULL; return it; } if (idx < 0) { it.base = dbuf->data; it.ptr = NULL; } else if (idx >= dbuf->size) { it.base = dbuf->data + dbuf->size; it.ptr = it.base - 1; } else { it.base = dbuf->data + idx + 1; it.ptr = it.base - 1; } return it; } static const ptrdiff_t GUF_CAT(GUF_CNT_NAME, _npos) = PTRDIFF_MIN; GUF_FN_KEYWORDS ptrdiff_t GUF_CAT(GUF_CNT_NAME, _iter_to_idx)(const GUF_CNT_NAME* dbuf, GUF_CAT(GUF_CNT_NAME, _iter) it) { GUF_ASSERT(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if ((!it.ptr && !it.base) || !dbuf->data || !dbuf->size) { return GUF_CAT(GUF_CNT_NAME, _npos); } bool is_reverse_it = it.base != NULL; if (is_reverse_it) { return (ptrdiff_t)(it.base - dbuf->data) - 1; } else { return (ptrdiff_t)(it.ptr - dbuf->data); } } GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _iter_next)(const GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) it, ptrdiff_t step) { GUF_ASSERT(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (!dbuf->size || !dbuf->data || (!it.base && !it.ptr)) { it.ptr = NULL; it.base = NULL; return it; } const bool is_reverse_it = it.base != NULL; if (is_reverse_it) { if (step < 0) { GUF_ASSERT_RELEASE(step > PTRDIFF_MIN); // Catch overflow. } step = -step; } if (is_reverse_it) { ptrdiff_t idx = (ptrdiff_t)(it.base - dbuf->data) + step; idx = GUF_CLAMP(idx, 0, dbuf->size); it.base = dbuf->data + idx; it.ptr = idx == 0 ? NULL : it.base - 1; } else { GUF_ASSERT(it.ptr); ptrdiff_t idx = (ptrdiff_t)(it.ptr - dbuf->data) + step; idx = GUF_CLAMP(idx, 0, dbuf->size); it.ptr = dbuf->data + idx; } return it; } #if defined(GUF_T_IS_INTEGRAL_TYPE) || defined(GUF_T_EQ) GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find)(GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) begin, GUF_CAT(GUF_CNT_NAME, _iter) end, const GUF_T *needle) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); GUF_ASSERT_RELEASE(needle); const bool is_reverse_it = begin.base != NULL; GUF_ASSERT_RELEASE(is_reverse_it == (end.base != NULL)); // begin and end must be the same iterator type. const GUF_CAT(GUF_CNT_NAME, _iter) dbuf_end_it = is_reverse_it ? GUF_CAT(GUF_CNT_NAME, _rend)(dbuf) : GUF_CAT(GUF_CNT_NAME, _end)(dbuf); if (!dbuf->data || !dbuf->size || (!begin.ptr && !begin.base) || (!end.ptr && !end.base)) { return dbuf_end_it; } if ((begin.ptr == dbuf_end_it.ptr) || (!is_reverse_it && begin.ptr >= end.ptr) || (is_reverse_it && begin.base <= end.base)) { return dbuf_end_it; } for (GUF_CAT(GUF_CNT_NAME, _iter) it = begin; it.ptr != end.ptr; it = GUF_CAT(GUF_CNT_NAME, _iter_next)(dbuf, it, 1)) { #ifdef GUF_T_EQ if (GUF_T_EQ(it.ptr, needle)) { return it; } #else if (*it.ptr == *needle) { return it; } #endif } return GUF_CAT(GUF_CNT_NAME, _end)(dbuf); } GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find_val)(GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) begin, GUF_CAT(GUF_CNT_NAME, _iter) end, GUF_T needle_val) { return GUF_CAT(GUF_CNT_NAME, _find)(dbuf, begin, end, &needle_val); } GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _contains)(GUF_CNT_NAME *dbuf, const GUF_T *needle) { GUF_CAT(GUF_CNT_NAME, _iter) beg = GUF_CAT(GUF_CNT_NAME, _begin)(dbuf); GUF_CAT(GUF_CNT_NAME, _iter) end = GUF_CAT(GUF_CNT_NAME, _end)(dbuf); return GUF_CAT(GUF_CNT_NAME, _find)(dbuf, beg, end, needle).ptr != end.ptr; } GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _contains_val)(GUF_CNT_NAME *dbuf, GUF_T needle) { return GUF_CAT(GUF_CNT_NAME, _contains)(dbuf, &needle); } GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find_if)(GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) begin, GUF_CAT(GUF_CNT_NAME, _iter) end, bool (*predicate)(const GUF_T *)) { GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); GUF_ASSERT_RELEASE(predicate); const bool is_reverse_it = begin.base != NULL; GUF_ASSERT_RELEASE(is_reverse_it == (end.base != NULL)); // begin and end must be the same iterator type. const GUF_CAT(GUF_CNT_NAME, _iter) dbuf_end_it = is_reverse_it ? GUF_CAT(GUF_CNT_NAME, _rend)(dbuf) : GUF_CAT(GUF_CNT_NAME, _end)(dbuf); if (!dbuf->data || !dbuf->size || (!begin.ptr && !begin.base) || (!end.ptr && !end.base)) { return dbuf_end_it; } if ((begin.ptr == dbuf_end_it.ptr) || (!is_reverse_it && begin.ptr >= end.ptr) || (is_reverse_it && begin.base <= end.base)) { return dbuf_end_it; } for (GUF_CAT(GUF_CNT_NAME, _iter) it = begin; it.ptr != end.ptr; GUF_CAT(GUF_CNT_NAME, _iter_next)(dbuf, it, 1)) { if (predicate(it.ptr)) { return it; } } return GUF_CAT(GUF_CNT_NAME, _end)(dbuf); } #endif #endif /* end #ifdef GUF_IMPL */ #undef GUF_DBUF_INITIAL_CAP #undef GUF_DBUF_USE_GROWTH_FAC_ONE_POINT_FIVE #undef GUF_CNT_NAME #undef GUF_CNT_WITH_ELEM_CTX #undef GUF_T #undef GUF_T_COPY #undef GUF_T_MOVE #undef GUF_T_FREE #undef GUF_T_EQ #undef GUF_T_IS_INTEGRAL_TYPE #undef GUF_FN_KEYWORDS #undef GUF_IMPL #undef GUF_IMPL_STATIC