aoc-2025/libguf/guf_dbuf.h
2025-12-23 21:13:41 +01:00

1061 lines
39 KiB
C
Executable File

/*
is parametrized: yes
*/
#if defined(GUF_DBUF_STATIC_IMPL)
#define GUF_DBUF_KWRDS static
#else
#define GUF_DBUF_KWRDS
#endif
#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_DBUF_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_DBUF_NAME
#define GUF_DBUF_NAME GUF_CAT(dbuf_, GUF_T)
#endif
#ifdef GUF_DBUF_ONLY_TYPES
typedef struct GUF_DBUF_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_DBUF_NAME;
typedef struct GUF_CAT(GUF_DBUF_NAME, _iter) {
GUF_T *ptr;
GUF_T *base; // Not NULL For reverse iterators (unless dbuf->size == 0, then ptr and base are NULL for both iterator types)
} GUF_CAT(GUF_DBUF_NAME, _iter);
#else
// 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
#if !defined(GUF_DBUF_IMPL) && !defined(GUF_DBUF_WITHOUT_TYPES)
typedef struct GUF_DBUF_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_DBUF_NAME;
/*
- Regular iterator: base is always NULL
- Reverse iterator: base points the element after ptr (unless dbuf->size == 0, then ptr and base are NULL for both iterator types)
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_DBUF_NAME, _iter) {
GUF_T *ptr;
GUF_T *base; // Not NULL For reverse iterators (unless dbuf->size == 0, then ptr and base are NULL for both iterator types)
} GUF_CAT(GUF_DBUF_NAME, _iter);
#endif
GUF_DBUF_KWRDS bool GUF_CAT(GUF_DBUF_NAME, _valid)(const GUF_DBUF_NAME* dbuf);
GUF_DBUF_KWRDS ptrdiff_t GUF_CAT(GUF_DBUF_NAME, _max_capacity)(void);
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _try_reserve)(GUF_DBUF_NAME *dbuf, ptrdiff_t min_capacity, guf_err *err);
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _reserve)(GUF_DBUF_NAME *dbuf, ptrdiff_t min_capacity);
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _try_init)(GUF_DBUF_NAME *dbuf, ptrdiff_t capacity, guf_allocator *allocator, guf_err *err);
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _init)(GUF_DBUF_NAME *dbuf, ptrdiff_t start_cap, guf_allocator *allocator);
GUF_DBUF_KWRDS GUF_DBUF_NAME GUF_CAT(GUF_DBUF_NAME, _new)(guf_allocator *allocator);
GUF_DBUF_KWRDS GUF_DBUF_NAME GUF_CAT(GUF_DBUF_NAME, _try_new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator, guf_err *err);
GUF_DBUF_KWRDS GUF_DBUF_NAME GUF_CAT(GUF_DBUF_NAME, _new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator);
#ifdef GUF_CNT_WITH_ELEM_CTX
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _set_elem_ctx)(GUF_DBUF_NAME *dbuf, void *elem_ctx);
#endif
GUF_DBUF_KWRDS void *GUF_CAT(GUF_DBUF_NAME, _get_elem_ctx)(const GUF_DBUF_NAME *dbuf); // Always returns NULL if GUF_CNT_WITH_ELEM_CTX is not defined
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _free)(GUF_DBUF_NAME *dbuf, void *ctx);
GUF_DBUF_KWRDS GUF_DBUF_NAME *GUF_CAT(GUF_DBUF_NAME, _copy)(GUF_DBUF_NAME *dst, const GUF_DBUF_NAME *src, void *ctx);
GUF_DBUF_KWRDS GUF_DBUF_NAME *GUF_CAT(GUF_DBUF_NAME, _move)(GUF_DBUF_NAME *dst, GUF_DBUF_NAME *src, void *ctx);
GUF_DBUF_KWRDS ptrdiff_t GUF_CAT(GUF_DBUF_NAME, _try_get_next_capacity)(ptrdiff_t old_cap, guf_err *err);
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _try_grow_if_full)(GUF_DBUF_NAME *dbuf, guf_err *err);
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _try_shrink_to_fit)(GUF_DBUF_NAME *dbuf, guf_err *err);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_insert)(GUF_DBUF_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt, guf_err *err);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _insert)(GUF_DBUF_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_insert_val)(GUF_DBUF_NAME *dbuf, GUF_T elem, ptrdiff_t idx, guf_err *err);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _insert_val)(GUF_DBUF_NAME *dbuf, GUF_T elem, ptrdiff_t idx);
#ifdef GUF_T_COPY
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_insert_val_cpy)(GUF_DBUF_NAME *dbuf, GUF_T elem, ptrdiff_t idx, guf_err *err);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _insert_val_cpy)(GUF_DBUF_NAME *dbuf, GUF_T elem, ptrdiff_t idx);
#endif
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_push)(GUF_DBUF_NAME *dbuf, GUF_T *elem, guf_cpy_opt cpy_opt, guf_err *err);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _push)(GUF_DBUF_NAME *dbuf, GUF_T *elem, guf_cpy_opt cpy_opt);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_push_val)(GUF_DBUF_NAME *dbuf, GUF_T elem, guf_err *err);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _push_val)(GUF_DBUF_NAME *dbuf, GUF_T elem);
#ifdef GUF_T_COPY
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_push_val_cpy)(GUF_DBUF_NAME *dbuf, GUF_T elem, guf_err *err);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _push_val_cpy)(GUF_DBUF_NAME *dbuf, GUF_T elem);
#endif
GUF_DBUF_KWRDS bool GUF_CAT(GUF_DBUF_NAME, _try_erase)(GUF_DBUF_NAME *dbuf, ptrdiff_t idx, guf_err *err);
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _erase)(GUF_DBUF_NAME *dbuf, ptrdiff_t idx);
GUF_DBUF_KWRDS bool GUF_CAT(GUF_DBUF_NAME, _try_pop)(GUF_DBUF_NAME *dbuf, guf_err *err);
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _pop)(GUF_DBUF_NAME *dbuf);
GUF_DBUF_KWRDS GUF_T GUF_CAT(GUF_DBUF_NAME, _try_pop_move)(GUF_DBUF_NAME *dbuf, guf_err *err);
GUF_DBUF_KWRDS GUF_T GUF_CAT(GUF_DBUF_NAME, _pop_move)(GUF_DBUF_NAME *dbuf);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_at)(GUF_DBUF_NAME *dbuf, ptrdiff_t idx, guf_err *err);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _at)(GUF_DBUF_NAME *dbuf, ptrdiff_t idx);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_front)(GUF_DBUF_NAME *dbuf, guf_err *err);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _front)(GUF_DBUF_NAME *dbuf);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_back)(GUF_DBUF_NAME *dbuf, guf_err *err);
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _back)(GUF_DBUF_NAME *dbuf);
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _begin)(const GUF_DBUF_NAME* dbuf);
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _end)(const GUF_DBUF_NAME* dbuf);
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _rbegin)(const GUF_DBUF_NAME* dbuf);
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _rend)(const GUF_DBUF_NAME* dbuf);
GUF_DBUF_KWRDS bool GUF_CAT(GUF_DBUF_NAME, _iter_is_end)(const GUF_DBUF_NAME* dbuf, GUF_CAT(GUF_DBUF_NAME, _iter) it);
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _iter_next)(const GUF_DBUF_NAME *dbuf, GUF_CAT(GUF_DBUF_NAME, _iter) it, ptrdiff_t step);
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _iter_at_idx)(const GUF_DBUF_NAME* dbuf, ptrdiff_t idx);
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _reverse_iter_at_idx)(const GUF_DBUF_NAME* dbuf, ptrdiff_t idx);
GUF_DBUF_KWRDS ptrdiff_t GUF_CAT(GUF_DBUF_NAME, _iter_to_idx)(const GUF_DBUF_NAME* dbuf, GUF_CAT(GUF_DBUF_NAME, _iter) it);
#if defined(GUF_T_IS_INTEGRAL_TYPE) || defined(GUF_T_EQ)
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _find)(GUF_DBUF_NAME *dbuf, GUF_CAT(GUF_DBUF_NAME, _iter) begin, GUF_CAT(GUF_DBUF_NAME, _iter) end, const GUF_T *needle);
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _find_val)(GUF_DBUF_NAME *dbuf, GUF_CAT(GUF_DBUF_NAME, _iter) begin, GUF_CAT(GUF_DBUF_NAME, _iter) end, GUF_T needle_val);
GUF_DBUF_KWRDS bool GUF_CAT(GUF_DBUF_NAME, _contains)(GUF_DBUF_NAME *dbuf, const GUF_T *needle);
GUF_DBUF_KWRDS bool GUF_CAT(GUF_DBUF_NAME, _contains_val)(GUF_DBUF_NAME *dbuf, GUF_T needle);
#endif
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _find_if)(GUF_DBUF_NAME *dbuf, GUF_CAT(GUF_DBUF_NAME, _iter) begin, GUF_CAT(GUF_DBUF_NAME, _iter) end, bool (*predicate)(const GUF_T *));
#if defined(GUF_DBUF_IMPL) || defined(GUF_DBUF_IMPL_STATIC)
#include "guf_math.h"
GUF_DBUF_KWRDS bool GUF_CAT(GUF_DBUF_NAME, _valid)(const GUF_DBUF_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_DBUF_KWRDS ptrdiff_t GUF_CAT(GUF_DBUF_NAME, _max_capacity)(void)
{
ptrdiff_t DBUF_MAX_BYTES = GUF_ALLOC_MAX_BYTES(GUF_T);
GUF_ASSERT((DBUF_MAX_BYTES % sizeof(GUF_T)) == 0);
(void)DBUF_MAX_BYTES;
const ptrdiff_t DBUF_MAX_CAP = GUF_ALLOC_MAX_CAPACITY(GUF_T);
GUF_ASSERT(DBUF_MAX_CAP <= PTRDIFF_MAX);
return DBUF_MAX_CAP;
}
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _try_reserve)(GUF_DBUF_NAME *dbuf, ptrdiff_t min_capacity, guf_err *err)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_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);
const ptrdiff_t DBUF_MAX_CAP = GUF_CAT(GUF_DBUF_NAME, _max_capacity)();
min_capacity = GUF_MIN(min_capacity, DBUF_MAX_CAP);
if (min_capacity <= dbuf->capacity) {
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_reserve: already at max capacity"));
return;
}
ptrdiff_t new_alloc_bytes = -1;
if (!guf_size_calc_safe(min_capacity, sizeof(GUF_T), &new_alloc_bytes)) {
GUF_ASSERT_RELEASE(false);
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_reserve: overflow in size calculation (BUG: this should never happen...)"));
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_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _reserve)(GUF_DBUF_NAME *dbuf, ptrdiff_t min_capacity)
{
GUF_CAT(GUF_DBUF_NAME, _try_reserve)(dbuf, min_capacity, NULL);
}
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _try_init)(GUF_DBUF_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_DBUF_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_DBUF_NAME, _try_reserve)(dbuf, capacity, err);
}
}
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _init)(GUF_DBUF_NAME *dbuf, ptrdiff_t start_cap, guf_allocator *allocator)
{
GUF_CAT(GUF_DBUF_NAME, _try_init)(dbuf, start_cap, allocator, NULL);
}
GUF_DBUF_KWRDS GUF_DBUF_NAME GUF_CAT(GUF_DBUF_NAME, _new)(guf_allocator *allocator)
{
GUF_DBUF_NAME dbuf = {0};
GUF_CAT(GUF_DBUF_NAME, _init)(&dbuf, 0, allocator);
GUF_ASSERT(dbuf.size == 0 && dbuf.capacity == 0);
return dbuf;
}
GUF_DBUF_KWRDS GUF_DBUF_NAME GUF_CAT(GUF_DBUF_NAME, _try_new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator, guf_err *err)
{
GUF_DBUF_NAME dbuf = {0};
GUF_CAT(GUF_DBUF_NAME, _try_init)(&dbuf, capacity, allocator, err);
if (err && *err != GUF_ERR_NONE) {
return (GUF_DBUF_NAME){0};
}
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return dbuf;
}
GUF_DBUF_KWRDS GUF_DBUF_NAME GUF_CAT(GUF_DBUF_NAME, _new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator)
{
return GUF_CAT(GUF_DBUF_NAME, _try_new_with_capacity)(capacity, allocator, NULL);
}
#ifdef GUF_CNT_WITH_ELEM_CTX
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _set_elem_ctx)(GUF_DBUF_NAME *dbuf, void *elem_ctx)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_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_DBUF_KWRDS void *GUF_CAT(GUF_DBUF_NAME, _get_elem_ctx)(const GUF_DBUF_NAME *dbuf)
{
#ifdef GUF_CNT_WITH_ELEM_CTX
return dbuf->elem_ctx;
#else
(void)dbuf;
return NULL;
#endif
}
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _free)(GUF_DBUF_NAME *dbuf, void *ctx)
{
(void)ctx;
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_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_DBUF_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_DBUF_KWRDS GUF_DBUF_NAME *GUF_CAT(GUF_DBUF_NAME, _copy)(GUF_DBUF_NAME *dst, const GUF_DBUF_NAME *src, void *ctx)
{
(void)ctx;
if (!dst || !src || GUF_CAT(GUF_DBUF_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_DBUF_NAME, _get_elem_ctx)(src));
if (!cpy_success) {
dst->size = i;
return NULL;
}
#else
dst->data[i] = src->data[i];
#endif
}
return dst;
}
}
GUF_DBUF_KWRDS GUF_DBUF_NAME *GUF_CAT(GUF_DBUF_NAME, _move)(GUF_DBUF_NAME *dst, GUF_DBUF_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_DBUF_KWRDS ptrdiff_t GUF_CAT(GUF_DBUF_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 * 2u;
} else {
#ifdef GUF_DBUF_USE_GROWTH_FAC_ONE_POINT_FIVE
if (guf_mul_is_overflow_size_t(old_cap, 3)) {
if (guf_mul_is_overflow_size_t((size_t)old_cap / 2u, 3)) { // Try (old_cap / 2) * 3
new_cap = PTRDIFF_MAX;
} else {
new_cap = ((size_t)old_cap / 2u) * 3u;
}
} else {
new_cap = (size_t)old_cap * 3u / 2u;
}
#else
if (guf_mul_is_overflow_size_t(old_cap, 2)) {
new_cap = PTRDIFF_MAX;
} else {
new_cap = (size_t)old_cap * 2u;
}
#endif
}
const size_t DBUF_MAX_CAP = (size_t)GUF_CAT(GUF_DBUF_NAME, _max_capacity)();
new_cap = GUF_MIN(new_cap, DBUF_MAX_CAP);
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: reached max capacity"));
return -1;
} else {
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return new_cap;
}
}
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _try_grow_if_full)(GUF_DBUF_NAME *dbuf, guf_err *err)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_NAME, _valid)(dbuf));
GUF_ASSERT_RELEASE(dbuf->capacity >= 0 && dbuf->size >= 0);
if (dbuf->size == dbuf->capacity) {
ptrdiff_t next_cap = GUF_CAT(GUF_DBUF_NAME, _try_get_next_capacity)(dbuf->capacity, err);
if (err && *err != GUF_ERR_NONE) {
return;
}
GUF_ASSERT(next_cap > 0);
GUF_CAT(GUF_DBUF_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_DBUF_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_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_insert)(GUF_DBUF_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt, guf_err *err)
{
GUF_ASSERT(GUF_CAT(GUF_DBUF_NAME,_valid)(dbuf));
GUF_ASSERT(elem);
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_DBUF_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_DBUF_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_DBUF_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_DBUF_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_DBUF_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_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _insert)(GUF_DBUF_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt)
{
return GUF_CAT(GUF_DBUF_NAME, _try_insert)(dbuf, elem, idx, cpy_opt, NULL);
}
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_insert_val)(GUF_DBUF_NAME *dbuf, GUF_T elem, ptrdiff_t idx, guf_err *err)
{
return GUF_CAT(GUF_DBUF_NAME, _try_insert)(dbuf, &elem, idx, GUF_CPY_VALUE, err);
}
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _insert_val)(GUF_DBUF_NAME *dbuf, GUF_T elem, ptrdiff_t idx)
{
return GUF_CAT(GUF_DBUF_NAME, _try_insert_val)(dbuf, elem, idx, NULL);
}
#ifdef GUF_T_COPY
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_insert_val_cpy)(GUF_DBUF_NAME *dbuf, GUF_T elem, ptrdiff_t idx, guf_err *err)
{
return GUF_CAT(GUF_DBUF_NAME, _try_insert)(dbuf, &elem, idx, GUF_CPY_DEEP, err);
}
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _insert_val_cpy)(GUF_DBUF_NAME *dbuf, GUF_T elem, ptrdiff_t idx)
{
return GUF_CAT(GUF_DBUF_NAME, _try_insert_val_cpy)(dbuf, elem, idx, NULL);
}
#endif
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_push)(GUF_DBUF_NAME *dbuf, GUF_T *elem, guf_cpy_opt cpy_opt, guf_err *err)
{
GUF_ASSERT(GUF_CAT(GUF_DBUF_NAME, _valid)(dbuf));
return GUF_CAT(GUF_DBUF_NAME, _try_insert)(dbuf, elem, dbuf->size, cpy_opt, err);
}
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _push)(GUF_DBUF_NAME *dbuf, GUF_T *elem, guf_cpy_opt cpy_opt)
{
return GUF_CAT(GUF_DBUF_NAME, _try_push)(dbuf, elem, cpy_opt, NULL);
}
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_push_val)(GUF_DBUF_NAME *dbuf, GUF_T elem, guf_err *err)
{
return GUF_CAT(GUF_DBUF_NAME, _try_push)(dbuf, &elem, GUF_CPY_VALUE, err);
}
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _push_val)(GUF_DBUF_NAME *dbuf, GUF_T elem)
{
return GUF_CAT(GUF_DBUF_NAME, _try_push_val)(dbuf, elem, NULL);
}
#ifdef GUF_T_COPY
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_push_val_cpy)(GUF_DBUF_NAME *dbuf, GUF_T elem, guf_err *err)
{
return GUF_CAT(GUF_DBUF_NAME, _try_push)(dbuf, &elem, GUF_CPY_DEEP, err);
}
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _push_val_cpy)(GUF_DBUF_NAME *dbuf, GUF_T elem)
{
return GUF_CAT(GUF_DBUF_NAME, _try_push_val_cpy)(dbuf, elem, NULL);
}
#endif
GUF_DBUF_KWRDS bool GUF_CAT(GUF_DBUF_NAME, _try_erase)(GUF_DBUF_NAME *dbuf, ptrdiff_t idx, guf_err *err)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_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_DBUF_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_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _erase)(GUF_DBUF_NAME *dbuf, ptrdiff_t idx)
{
GUF_CAT(GUF_DBUF_NAME, _try_erase)(dbuf, idx, NULL);
}
GUF_DBUF_KWRDS bool GUF_CAT(GUF_DBUF_NAME, _try_pop)(GUF_DBUF_NAME *dbuf, guf_err *err)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_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_DBUF_NAME, _get_elem_ctx)(dbuf));
#else
--dbuf->size;
#endif
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return true;
}
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _pop)(GUF_DBUF_NAME *dbuf)
{
GUF_CAT(GUF_DBUF_NAME, _try_pop)(dbuf, NULL);
}
GUF_DBUF_KWRDS GUF_T GUF_CAT(GUF_DBUF_NAME, _try_pop_move)(GUF_DBUF_NAME *dbuf, guf_err *err)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_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_move: 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 defined(GUF_T_MOVE)
dst = GUF_T_MOVE(dst, popped, GUF_CAT(GUF_DBUF_NAME, _get_elem_ctx)(dbuf));
#else
*dst = *popped;
memset(popped, 0, sizeof(GUF_T));
#endif
if (!dst) {
guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_pop_move: Failed on move"));
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 {
dbuf->size--;
guf_err_set_if_not_null(err, GUF_ERR_NONE);
return popped_val;
}
}
GUF_DBUF_KWRDS GUF_T GUF_CAT(GUF_DBUF_NAME, _pop_move)(GUF_DBUF_NAME *dbuf)
{
return GUF_CAT(GUF_DBUF_NAME, _try_pop_move)(dbuf, NULL);
}
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_at)(GUF_DBUF_NAME *dbuf, ptrdiff_t idx, guf_err *err)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_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_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _at)(GUF_DBUF_NAME *dbuf, ptrdiff_t idx)
{
return GUF_CAT(GUF_DBUF_NAME, _try_at)(dbuf, idx, NULL);
}
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_front)(GUF_DBUF_NAME *dbuf, guf_err *err)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_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_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _front)(GUF_DBUF_NAME *dbuf)
{
return GUF_CAT(GUF_DBUF_NAME, _try_front)(dbuf, NULL);
}
GUF_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _try_back)(GUF_DBUF_NAME *dbuf, guf_err *err)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_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_DBUF_KWRDS GUF_T *GUF_CAT(GUF_DBUF_NAME, _back)(GUF_DBUF_NAME *dbuf)
{
return GUF_CAT(GUF_DBUF_NAME, _try_back)(dbuf, NULL);
}
GUF_DBUF_KWRDS void GUF_CAT(GUF_DBUF_NAME, _try_shrink_to_fit)(GUF_DBUF_NAME *dbuf, guf_err *err)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_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 */
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _begin)(const GUF_DBUF_NAME* dbuf)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_NAME, _valid)(dbuf));
return (GUF_CAT(GUF_DBUF_NAME, _iter)) {
.ptr = dbuf->data && dbuf->size ? dbuf->data : NULL,
.base = NULL
};
}
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _end)(const GUF_DBUF_NAME* dbuf)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_NAME, _valid)(dbuf));
return (GUF_CAT(GUF_DBUF_NAME, _iter)) {
.ptr = dbuf->data && dbuf->size ? dbuf->data + dbuf->size : NULL,
.base = NULL
};
}
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _rbegin)(const GUF_DBUF_NAME* dbuf)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_NAME, _valid)(dbuf));
return (GUF_CAT(GUF_DBUF_NAME, _iter)) {
.base = dbuf->data && dbuf->size ? dbuf->data + dbuf->size : NULL,
.ptr = dbuf->data && dbuf->size ? dbuf->data + (dbuf->size - 1) : NULL
};
}
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _rend)(const GUF_DBUF_NAME* dbuf)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_NAME, _valid)(dbuf));
return (GUF_CAT(GUF_DBUF_NAME, _iter)) {
.base = dbuf->data && dbuf->size ? dbuf->data : NULL,
.ptr = NULL
};
}
GUF_DBUF_KWRDS bool GUF_CAT(GUF_DBUF_NAME, _iter_is_end)(const GUF_DBUF_NAME* dbuf, GUF_CAT(GUF_DBUF_NAME, _iter) it)
{
const bool is_reverse_it = it.base != NULL;
const GUF_CAT(GUF_DBUF_NAME, _iter) dbuf_end_it = is_reverse_it ? GUF_CAT(GUF_DBUF_NAME, _rend)(dbuf) : GUF_CAT(GUF_DBUF_NAME, _end)(dbuf);
return it.ptr == dbuf_end_it.ptr;
}
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _iter_at_idx)(const GUF_DBUF_NAME* dbuf, ptrdiff_t idx)
{
GUF_ASSERT(GUF_CAT(GUF_DBUF_NAME, _valid)(dbuf));
GUF_CAT(GUF_DBUF_NAME, _iter) it;
it.base = NULL;
if (!dbuf->data || !dbuf->size) {
it.ptr = NULL;
return it;
}
if (idx <= 0) { // begin()
it.ptr = dbuf->data;
} else if (idx >= dbuf->size) { // end()
it.ptr = dbuf->data + dbuf->size;
} else {
it.ptr = dbuf->data + idx;
}
return it;
}
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _reverse_iter_at_idx)(const GUF_DBUF_NAME* dbuf, ptrdiff_t idx)
{
GUF_CAT(GUF_DBUF_NAME, _iter) it = GUF_CAT(GUF_DBUF_NAME, _iter_at_idx)(dbuf, idx);
if (!dbuf->data || !dbuf->size) {
it.base = NULL;
it.ptr = NULL;
return it;
}
if (idx < 0) { // rend()
it.base = dbuf->data;
it.ptr = NULL;
} else if (idx >= dbuf->size) { // rbegin()
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;
}
GUF_DBUF_KWRDS ptrdiff_t GUF_CAT(GUF_DBUF_NAME, _iter_to_idx)(const GUF_DBUF_NAME* dbuf, GUF_CAT(GUF_DBUF_NAME, _iter) it)
{
GUF_ASSERT(GUF_CAT(GUF_DBUF_NAME, _valid)(dbuf));
if ((!it.ptr && !it.base) || !dbuf->data || !dbuf->size) {
return GUF_CNT_NPOS;
}
const bool is_reverse_it = it.base != NULL;
const GUF_CAT(GUF_DBUF_NAME, _iter) end_it = is_reverse_it ? GUF_CAT(GUF_DBUF_NAME, _rend)(dbuf) : GUF_CAT(GUF_DBUF_NAME, _end)(dbuf);
if (it.ptr == end_it.ptr) {
return is_reverse_it ? -1 : dbuf->size;
}
if (is_reverse_it) {
return (ptrdiff_t)(it.base - dbuf->data) - 1;
} else {
return (ptrdiff_t)(it.ptr - dbuf->data);
}
}
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _iter_next)(const GUF_DBUF_NAME *dbuf, GUF_CAT(GUF_DBUF_NAME, _iter) it, ptrdiff_t step)
{
GUF_ASSERT(GUF_CAT(GUF_DBUF_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(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_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _find)(GUF_DBUF_NAME *dbuf, GUF_CAT(GUF_DBUF_NAME, _iter) begin, GUF_CAT(GUF_DBUF_NAME, _iter) end, const GUF_T *needle)
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_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_DBUF_NAME, _iter) dbuf_end_it = is_reverse_it ? GUF_CAT(GUF_DBUF_NAME, _rend)(dbuf) : GUF_CAT(GUF_DBUF_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_DBUF_NAME, _iter) it = begin; it.ptr != end.ptr && it.ptr != NULL; it = GUF_CAT(GUF_DBUF_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 dbuf_end_it;
}
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _find_val)(GUF_DBUF_NAME *dbuf, GUF_CAT(GUF_DBUF_NAME, _iter) begin, GUF_CAT(GUF_DBUF_NAME, _iter) end, GUF_T needle_val)
{
return GUF_CAT(GUF_DBUF_NAME, _find)(dbuf, begin, end, &needle_val);
}
GUF_DBUF_KWRDS bool GUF_CAT(GUF_DBUF_NAME, _contains)(GUF_DBUF_NAME *dbuf, const GUF_T *needle)
{
GUF_CAT(GUF_DBUF_NAME, _iter) beg = GUF_CAT(GUF_DBUF_NAME, _begin)(dbuf);
GUF_CAT(GUF_DBUF_NAME, _iter) end = GUF_CAT(GUF_DBUF_NAME, _end)(dbuf);
return GUF_CAT(GUF_DBUF_NAME, _find)(dbuf, beg, end, needle).ptr != end.ptr;
}
GUF_DBUF_KWRDS bool GUF_CAT(GUF_DBUF_NAME, _contains_val)(GUF_DBUF_NAME *dbuf, GUF_T needle)
{
return GUF_CAT(GUF_DBUF_NAME, _contains)(dbuf, &needle);
}
#endif
GUF_DBUF_KWRDS GUF_CAT(GUF_DBUF_NAME, _iter) GUF_CAT(GUF_DBUF_NAME, _find_if)(GUF_DBUF_NAME *dbuf, GUF_CAT(GUF_DBUF_NAME, _iter) begin, GUF_CAT(GUF_DBUF_NAME, _iter) end, bool (*predicate)(const GUF_T *))
{
GUF_ASSERT_RELEASE(GUF_CAT(GUF_DBUF_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_DBUF_NAME, _iter) dbuf_end_it = is_reverse_it ? GUF_CAT(GUF_DBUF_NAME, _rend)(dbuf) : GUF_CAT(GUF_DBUF_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_DBUF_NAME, _iter) it = begin; it.ptr != end.ptr && it.ptr != NULL; GUF_CAT(GUF_DBUF_NAME, _iter_next)(dbuf, it, 1)) {
if (predicate(it.ptr)) {
return it;
}
}
return GUF_CAT(GUF_DBUF_NAME, _end)(dbuf);
}
#endif /* end #ifdef GUF_IMPL */
#endif /* end GUF_DBUF_ONLY_TYPES */
#undef GUF_DBUF_INITIAL_CAP
#undef GUF_DBUF_USE_GROWTH_FAC_ONE_POINT_FIVE
#undef GUF_DBUF_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_DBUF_KWRDS
#undef GUF_DBUF_IMPL
#undef GUF_DBUF_IMPL_STATIC
#undef GUF_DBUF_WITHOUT_TYPES
#undef GUF_DBUF_ONLY_TYPES