libguf/src/guf_dbuf.h
2025-02-01 13:31:15 +01:00

1004 lines
37 KiB
C

#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