From c9b18a220ef785919d51395ad91feaf87121dd0a Mon Sep 17 00:00:00 2001 From: jun <83899451+zeichensystem@users.noreply.github.com> Date: Mon, 27 Jan 2025 08:42:21 +0100 Subject: [PATCH] Rewrite dbuf --- src/guf_alloc.h | 91 +++++ src/guf_assert.h | 105 +++--- src/guf_common.h | 42 ++- src/guf_common_utils.h | 12 +- src/guf_cstr.h | 67 +--- src/guf_dbuf.h | 748 ++++++++++++++++++++++------------------- src/guf_int.h | 242 +------------ src/guf_obj.h | 54 +-- src/guf_sort.h | 160 +++++++++ src/guf_str.h | 51 +-- src/guf_test.c | 152 +++++---- 11 files changed, 885 insertions(+), 839 deletions(-) create mode 100644 src/guf_alloc.h create mode 100644 src/guf_sort.h diff --git a/src/guf_alloc.h b/src/guf_alloc.h new file mode 100644 index 0000000..bbec982 --- /dev/null +++ b/src/guf_alloc.h @@ -0,0 +1,91 @@ +#ifndef GUF_ALLOC_H +#define GUF_ALLOC_H +#include +#include +#include "guf_common.h" + +// A Custom allocator interface as described in https://nullprogram.com/blog/2023/12/17/ (last-retrieved 2025-01-25) +typedef struct guf_allocator { + void *(*alloc)(ptrdiff_t size, void *ctx); + void *(*realloc)(void *ptr, ptrdiff_t old_size, ptrdiff_t new_size, void *ctx); + void (*free)(void *ptr, ptrdiff_t size, void *ctx); + void *ctx; +} guf_allocator; + +// register_alloc_type(const char *typestr) (starting from 1) +// lookup_alloc_typename(ptrdiff_t type_id) + +// pass CNT_T_TYPE_ID to containers + +// type_id = 0 "other" +// keeping track of (heap?) allocs: alloc_map[type_id] = {.size_alloced, .size_freed} + +#define GUF_ALLOC_ENABLE_TRACKING + +typedef struct guf_alloc_libc_ctx { + bool zero_init; + size_t alloc_id; + // dbuf_alloc_info +} guf_alloc_libc_ctx; + +static inline void *guf_alloc_libc(ptrdiff_t size, void *ctx) +{ + GUF_ASSERT_RELEASE(size >= 0); + guf_alloc_libc_ctx *alloc_ctx = (guf_alloc_libc_ctx*)ctx; + if (size == 0) { + return NULL; + } else if (alloc_ctx && alloc_ctx->zero_init) { + return calloc(size, 1); + } else { + return malloc(size); + } +} + +static inline void *guf_realloc_libc(void *ptr, ptrdiff_t old_size, ptrdiff_t new_size, void *ctx) +{ + GUF_ASSERT_RELEASE(ptr); + GUF_ASSERT_RELEASE(old_size >= 0 && new_size >= 0); + guf_alloc_libc_ctx *alloc_ctx = (guf_alloc_libc_ctx*)ctx; + + if (old_size == new_size) { + return ptr; + } + + void *new_ptr = realloc(ptr, new_size); + if (!new_ptr || new_size == 0) { + return NULL; + } else if (alloc_ctx && alloc_ctx->zero_init && new_size > old_size) { + ptrdiff_t len = new_size - old_size; + GUF_ASSERT(len > 0); + GUF_ASSERT(old_size + len == new_size); + memset((char*)ptr + old_size, 0, len); // TODO: sketchy + } + return new_ptr; +} + +static inline void guf_free_libc(void *ptr, ptrdiff_t size, void *ctx) +{ + (void)ctx; + (void)size; + free(ptr); +} + +static guf_allocator guf_allocator_libc = { + .alloc = guf_alloc_libc, + .realloc = guf_realloc_libc, + .free = guf_free_libc, + .ctx = NULL +}; + +// typedef struct guf_alloc_info { +// const char *typename; +// ptrdiff_t allocated_bytes, freed_bytes; +// ptrdiff_t alloc_count, free_count; +// } guf_alloc_info; + +// // define in other file +// typedef struct guf_alloc_tracker_ctx { +// // dbuf_alloc_info alloc_info_buf; +// } guf_alloc_tracker_ctx; + +#endif \ No newline at end of file diff --git a/src/guf_assert.h b/src/guf_assert.h index ec7a76e..ed59f71 100644 --- a/src/guf_assert.h +++ b/src/guf_assert.h @@ -5,7 +5,7 @@ #include #include "guf_common_utils.h" -typedef enum guf_err_type { +typedef enum guf_err { GUF_ERR_NONE = 0, GUF_ERR_UNSPECIFIED, GUF_ERR_ALLOC_FAIL, @@ -17,18 +17,12 @@ typedef enum guf_err_type { GUF_ERR_INVALID_ARG, GUF_ERR_RUNTIME, GUF_ERR_LOGIC, - GUF_ERR_RELEASE_ASSERT, + GUF_ERR_ASSERT_FAIL, GUF_ERR_TYPES_NUM -} guf_err_type; - -typedef struct guf_err { - guf_err_type type; - const char *msg; } guf_err; -static const guf_err GUF_SUCCESS = (guf_err){.type = GUF_ERR_NONE, .msg = NULL}; -typedef void(*guf_panic_handler_fn)(const guf_err *err); +typedef void(*guf_panic_handler_fn)(guf_err err, const char *msg); extern guf_panic_handler_fn guf_global_panic_handler; extern const char *guf_err_type_str[GUF_ERR_TYPES_NUM]; @@ -38,6 +32,10 @@ static inline void guf_set_global_panic_handler(guf_panic_handler_fn panic_handl guf_global_panic_handler = panic_handler; } +#define GUF_FILE_LINE_STR() "file '" __FILE__ "' line " GUF_STRINGIFY(__LINE__) +#define GUF_ERR_MSG(msg) msg " (" GUF_FILE_LINE_STR() ")" +#define GUF_ERR_MSG_EMPTY() "(" GUF_FILE_LINE_STR() ")" + // Break on debug, cf. https://nullprogram.com/blog/2022/06/26/ (last retrieved 2025-01-07) #if __GNUC__ || __clang__ #define GUF_DEBUG_BREAK_CODE __builtin_trap() @@ -50,8 +48,7 @@ static inline void guf_set_global_panic_handler(guf_panic_handler_fn panic_handl #ifndef NDEBUG #define GUF_ASSERT(COND) do { \ if (!(COND)) { \ - fputs("libguf assertion '" #COND "' failed (" GUF_FILE_LINE_STR() ")\n", stderr); \ - GUF_DEBUG_BREAK_CODE; \ + guf_global_panic_handler(GUF_ERR_ASSERT_FAIL, "(assertion '" #COND "' " GUF_FILE_LINE_STR() ")"); \ abort(); \ }\ } while(0); @@ -61,43 +58,67 @@ static inline void guf_set_global_panic_handler(guf_panic_handler_fn panic_handl #define GUF_ASSERT_RELEASE(COND) do { \ if (!(COND)) { \ - guf_err guf_assert_err = (guf_err){.type = GUF_ERR_RELEASE_ASSERT, .msg = "(assertion '" #COND "' in file " __FILE__ " on line " GUF_STRINGIFY(__LINE__) ")"};\ - guf_global_panic_handler(&guf_assert_err);\ + guf_global_panic_handler(GUF_ERR_ASSERT_FAIL, "(release assertion '" #COND "' " GUF_FILE_LINE_STR() ")"); \ + abort();\ } \ } while (0); -#define GUF_FILE_LINE_STR() "file '" __FILE__ "' line " GUF_STRINGIFY(__LINE__) -#define GUF_ERR_MSG(msg) msg " (" GUF_FILE_LINE_STR() ")" +#define GUF_PANIC(err) do {guf_global_panic_handler(err, "(" GUF_FILE_LINE_STR() ")")} while(0); -#define GUF_PANIC(error_type) do {guf_err guf_panic_err = (guf_err){.type = (error_type), .msg = "(" GUF_FILE_LINE_STR() ")"}; guf_global_panic_handler(&guf_panic_err)} while(0); - -static inline bool guf_is_err(const guf_err *err) +static inline void guf_panic_handler_default(guf_err err, const char *msg) { - GUF_ASSERT_RELEASE(err); - GUF_ASSERT_RELEASE(err->type >= 0 && err->type < GUF_ERR_TYPES_NUM); - return err->type != GUF_ERR_NONE; + fputs("libguf panic", stderr); + + if (err > 0 && err < GUF_ERR_TYPES_NUM) { + fputs(": ", stderr); + GUF_ASSERT(GUF_STATIC_BUF_SIZE(guf_err_type_str) == GUF_ERR_TYPES_NUM); + fputs(guf_err_type_str[err], stderr); + fputc(' ', stderr); + if (msg && err != GUF_ERR_NONE) { + fputs(msg, stderr); + } + } else { + fputs(" (note: err passed to panic is invalid)", stderr); + } + fputc('\n', stderr); + + + #ifndef NDEBUG + GUF_DEBUG_BREAK_CODE; + #endif + + abort(); } -static inline void guf_panic_handler_default(const guf_err *err) +static inline void guf_panic(guf_err err, const char *msg) { - if (!err) { - fputs("libguf panic!", stderr); + GUF_ASSERT(guf_global_panic_handler); + if (!guf_global_panic_handler) { + fputs("libguf panic (note: guf_global_panic_handler is NULL)\n", stderr); + if (msg) { + fputs(": ", stderr); + fputs(msg, stderr); + } abort(); } + guf_global_panic_handler(err, msg); + abort(); // Just in case... +} - fputs("libguf panic: ", stderr); - - if (err->type > 0 || err->type < GUF_ERR_TYPES_NUM) { - GUF_ASSERT(GUF_STATIC_BUF_SIZE(guf_err_type_str) == GUF_ERR_TYPES_NUM); - fputs(guf_err_type_str[err->type], stderr); - fputc(' ', stderr); - if (err->msg && err->type != GUF_ERR_NONE) { - fputs(err->msg, stderr); - } +static inline void guf_err_set_or_panic(guf_err *err, guf_err err_val, const char *panic_msg) +{ + if (!err) { + guf_panic(err_val, panic_msg); + } else { + *err = err_val; } +} - fputc('\n', stderr); - abort(); +static inline void guf_err_set_if_not_null(guf_err *err, guf_err err_val) +{ + if (err) { + *err = err_val; + } } #ifdef GUF_IMPLEMENTATION @@ -115,20 +136,8 @@ static inline void guf_panic_handler_default(const guf_err *err) [GUF_ERR_INVALID_ARG] = "Invalid argument", [GUF_ERR_RUNTIME] = "Runtime error", [GUF_ERR_LOGIC] = "Logic error", - [GUF_ERR_RELEASE_ASSERT] = "Release assertion failed" + [GUF_ERR_ASSERT_FAIL] = "Assertion failed" }; #endif -static inline void guf_panic(const guf_err *err) -{ - GUF_ASSERT(guf_global_panic_handler); - if (!guf_global_panic_handler) { - fputs("libguf panic (note: guf_global_panic_handler is NULL)\n", stderr); - abort(); - } - - guf_global_panic_handler(err); - abort(); // Just in case... -} - #endif \ No newline at end of file diff --git a/src/guf_common.h b/src/guf_common.h index deec97c..b4d0b3a 100644 --- a/src/guf_common.h +++ b/src/guf_common.h @@ -6,10 +6,8 @@ #include #include -#include "guf_common_utils.h" #include "guf_assert.h" -#include "guf_int.h" -#include "guf_hash.h" +#include "guf_common_utils.h" typedef enum guf_obj_cpy_opt { GUF_CPY_VALUE = 0, @@ -22,11 +20,37 @@ typedef enum guf_sort_opt { GUF_SORT_DESCENDING = 1 } guf_sort_opt; -// bool guf_alloc_init(void); -// void *guf_malloc(size_t size, const char *name); -// void *guf_calloc(size_t count, size_t size, const char *name); -// void *guf_realloc(void *ptr, size_t size, const char *name); -// void guf_free(void *ptr, const char *name); -// void guf_alloc_print(void); +static inline bool guf_is_mul_overflow_size_t(size_t a, size_t b) +{ + size_t c = a * b; + return a != 0 && ((c / a) != b); +} + +static inline size_t guf_safe_mul_size_t(size_t a, size_t b) +{ + GUF_ASSERT_RELEASE(!guf_is_mul_overflow_size_t(a, b)); + return a * b; +} + +static inline bool guf_is_safe_size_calc(ptrdiff_t count, ptrdiff_t sizeof_elem) +{ + if (count < 0 || sizeof_elem <= 0) { + return false; + } + if (guf_is_mul_overflow_size_t(count, sizeof_elem)) { + return false; + } + size_t size = (size_t)count * (size_t)sizeof_elem; + return size <= PTRDIFF_MAX; +} + +static inline ptrdiff_t guf_safe_size_calc(ptrdiff_t count, ptrdiff_t sizeof_elem) +{ + GUF_ASSERT_RELEASE(count >= 0); + GUF_ASSERT_RELEASE(sizeof_elem > 0); + size_t size = guf_safe_mul_size_t(count, sizeof_elem); + GUF_ASSERT_RELEASE(size <= PTRDIFF_MAX); + return size; +} #endif diff --git a/src/guf_common_utils.h b/src/guf_common_utils.h index a6fd456..bcb2905 100644 --- a/src/guf_common_utils.h +++ b/src/guf_common_utils.h @@ -2,6 +2,13 @@ #define GUF_COMMON_UTILS_H #include +#define GUF_SWAP(TYPE, val_a, val_b) do {TYPE guf_swap_tmp = val_a; val_a = val_b; val_b = guf_swap_tmp;} while (0); +#define GUF_STATIC_BUF_SIZE(BUF) (sizeof((BUF)) / (sizeof((BUF)[0]))) + +#define GUF_MIN(X, Y) ((X) <= (Y) ? (X) : (Y)) +#define GUF_MAX(X, Y) ((X) >= (Y) ? (X) : (Y)) +#define GUF_CLAMP(X, MIN, MAX) GUF_MAX(GUF_MIN((X), (MAX)), (MIN)) + // The GUFCAT/GUF_TOK_CAT indirection is necessary because the ## operation alone does not evaluate the macro arguments. #define GUF_TOK_CAT(a, b) a##b #define GUFCAT(a, b) GUF_TOK_CAT(a, b) @@ -9,9 +16,8 @@ #define GUF_TOK_STRINGIFY(x) #x #define GUF_STRINGIFY(x) GUF_TOK_STRINGIFY(x) -#define GUF_FOREACH(CNT_PTR, CNT_TYPE, IT_NAME) for (GUFCAT(CNT_TYPE, _iter) IT_NAME = GUFCAT(CNT_TYPE, _begin)(CNT_PTR); IT_NAME.cur != IT_NAME.end; IT_NAME = IT_NAME.next(&IT_NAME, 1)) - -#define GUF_STATIC_BUF_SIZE(BUF) (sizeof((BUF)) / (sizeof((BUF)[0]))) +#define GUF_CNT_FOREACH(CNT_PTR, CNT_TYPE, IT_NAME) for (GUFCAT(CNT_TYPE, _iter) IT_NAME = GUFCAT(CNT_TYPE, _begin)(CNT_PTR); IT_NAME.ptr != GUFCAT(CNT_TYPE, _end)(CNT_PTR).ptr; it = GUFCAT(CNT_TYPE, _iter_next)(CNT_PTR, IT_NAME, 1)) +#define GUF_CNT_FOREACH_REVERSE(CNT_PTR, CNT_TYPE, IT_NAME) for (GUFCAT(CNT_TYPE, _iter) IT_NAME = GUFCAT(CNT_TYPE, _rbegin)(CNT_PTR); IT_NAME.ptr != GUFCAT(CNT_TYPE, _rend)(CNT_PTR).ptr; it = GUFCAT(CNT_TYPE, _iter_next)(CNT_PTR, IT_NAME, 1)) #define GUF_LIFETIME_BLOCK(obj_init_code, obj_name, free_fn, code) do { \ obj_init_code; \ diff --git a/src/guf_cstr.h b/src/guf_cstr.h index 065ad4b..7dfb872 100644 --- a/src/guf_cstr.h +++ b/src/guf_cstr.h @@ -4,15 +4,25 @@ #include "guf_common.h" typedef const char* guf_const_cstr; + +static inline int guf_const_cstr_cmp(const guf_const_cstr *a, const guf_const_cstr *b) +{ + GUF_ASSERT_RELEASE(a && b); + GUF_ASSERT_RELEASE(*a && *b); + return strcmp(*a, *b); +} + +static inline bool guf_const_cstr_eq(const guf_const_cstr *a, const guf_const_cstr *b) +{ + GUF_ASSERT_RELEASE(a && b); + GUF_ASSERT_RELEASE(*a && *b); + return 0 == strcmp(*a, *b); +} + + typedef char* guf_heap_cstr; -#define GUF_OBJ_TYPE guf_const_cstr -#include "guf_obj.h" - -#define GUF_OBJ_TYPE guf_heap_cstr -#include "guf_obj.h" - -static inline guf_heap_cstr *guf_heap_cstr_cpy_init(guf_heap_cstr *dst, const guf_heap_cstr *src) +static inline guf_heap_cstr *guf_heap_cstr_copy_construct(guf_heap_cstr *dst, const guf_heap_cstr *src) { GUF_ASSERT_RELEASE(dst && src); if (*src == NULL) { @@ -24,7 +34,7 @@ static inline guf_heap_cstr *guf_heap_cstr_cpy_init(guf_heap_cstr *dst, const gu return dst; } -static inline guf_heap_cstr *guf_heap_cstr_move_init(guf_heap_cstr *dst, guf_heap_cstr *src) +static inline guf_heap_cstr *guf_heap_cstr_move_construct(guf_heap_cstr *dst, guf_heap_cstr *src) { GUF_ASSERT_RELEASE(dst && src); *dst = *src; @@ -53,45 +63,4 @@ static inline bool guf_heap_cstr_eq(const guf_heap_cstr *a, const guf_heap_cstr return 0 == strcmp(*a, *b); } -static inline GUF_OBJ_OPS_DEFINE_CMP_VOID(guf_heap_cstr, guf_heap_cstr_cmp) -static inline GUF_OBJ_OPS_DEFINE_CMP_VOID_INV(guf_heap_cstr, guf_heap_cstr_cmp) - -static guf_heap_cstr_ops_type guf_heap_cstr_ops = { - .copy_init = guf_heap_cstr_cpy_init, - .move_init = guf_heap_cstr_move_init, - .free = guf_heap_cstr_free, - .eq = guf_heap_cstr_eq, - .cmp = guf_heap_cstr_cmp, - .cmp_void = guf_heap_cstr_cmp_void, - .cmp_void_inv = guf_heap_cstr_cmp_void_inv -}; - -static inline int guf_const_cstr_cmp(const guf_const_cstr *a, const guf_const_cstr *b) -{ - GUF_ASSERT_RELEASE(a && b); - GUF_ASSERT_RELEASE(*a && *b); - return strcmp(*a, *b); -} - -static inline bool guf_const_cstr_eq(const guf_const_cstr *a, const guf_const_cstr *b) -{ - GUF_ASSERT_RELEASE(a && b); - GUF_ASSERT_RELEASE(*a && *b); - return 0 == strcmp(*a, *b); -} - -static inline GUF_OBJ_OPS_DEFINE_CMP_VOID(guf_const_cstr, guf_const_cstr_cmp) -static inline GUF_OBJ_OPS_DEFINE_CMP_VOID_INV(guf_const_cstr, guf_const_cstr_cmp) - -static guf_const_cstr_ops_type guf_const_cstr_ops = { - .default_value = NULL, - .copy_init = NULL, - .move_init = NULL, - .free = NULL, - .eq = guf_const_cstr_eq, - .cmp = guf_const_cstr_cmp, - .cmp_void = guf_const_cstr_cmp_void, - .cmp_void_inv = guf_const_cstr_cmp_void_inv -}; - #endif \ No newline at end of file diff --git a/src/guf_dbuf.h b/src/guf_dbuf.h index cd62cb4..67d4a9b 100644 --- a/src/guf_dbuf.h +++ b/src/guf_dbuf.h @@ -6,17 +6,23 @@ #include #include "guf_assert.h" #include "guf_common.h" + #include "guf_alloc.h" #define GUF_DBUF_FOREACH(DBUF, ELEM_TYPE, ELEM_PTR_NAME) for (ELEM_TYPE *ELEM_PTR_NAME = (DBUF).data, *end = (DBUF).data ? (DBUF).data + (DBUF).size : NULL; ELEM_PTR_NAME != end; ++ELEM_PTR_NAME) #endif /* - Template parameters: - dbuf_type - + Template parameters: - GUF_CNT_T: The value type stored in the container. - - GUF_CNT_T_OPS: The operations struct (cf. guf_obj.h) of the container (default: empty operations struct corresponding to a basic value type such as int) - GUF_CNT_NAME: The typename of the resulting container (default: dbuf_GUF_CNT_T) + - GUF_CNT_T_IS_INTEGRAL_TYPE: whether the type is an integral type (if defined, must not define COPY, MOVE, FREE and EQ) + + - GUF_CNT_COPY: cpy function with signature GUF_CNT_T *copy(GUF_CNT_T *dst, const GUF_CNT_T *src) (default: copy by value) + - GUF_CNT_MOVE: move function with signature GUF_CNT_T *move(GUF_CNT_T *dst, GUF_CNT_T *src) (default: undefined) + - GUF_CNT_FREE: free function with signature void free(GUF_CNT_T *a) (default: undefined) + - GUF_CNT_EQ: equality function with signature bool eq(const GUF_CNT_T *a, const GUF_CNT_T *a) (default: undefined, or equality by value if GUF_CNT_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) */ // Used for the first growth if dbuf->capacity is zero. @@ -28,226 +34,309 @@ #error "Undefined container template GUF_CONTAINER_T" #endif -#ifndef GUF_CNT_T_OPS - #error "Undefined container template GUF_CNT_T_OPS" -#endif - #ifndef GUF_CNT_NAME #define GUF_CNT_NAME GUF_CAT(dbuf_, GUF_CNT_T) #endif +// #ifndef GUF_CNT_T_COPY +// #error "Undefined container template GUF_OBJ_COPY" +// #endif + +// #ifndef GUF_CNT_T_MOVE +// #error "Undefined container template GUF_OBJ_MOVE" +// #endif + + +#if (defined(GUF_CNT_T_COPY) || defined(GUF_CNT_T_MOVE)) && !defined(GUF_CNT_T_FREE) + #error "Must define GUF_CNT_T_FREE because GUF_CNT_T_COPY or GUF_CNT_T_MOVE is defined" +#endif + +#if defined(GUF_CNT_T_IS_INTEGRAL_TYPE) && (defined(GUF_CNT_T_COPY) || defined(GUF_CNT_MOVE) || defined(GUF_CNT_FREE) || defined(GUF_CNT_T_EQ)) + #error "Integral types do not need COPY, MOVE, FREE or EQ functions" +#endif + +// #ifndef GUF_CNT_T_FREE +// #error "Undefined container template GUF_OBJ_FREE" +// #endif + +// #ifndef GUF_CNT_T_EQ +// #error "Undefined container template GUF_OBJ_CPY" +// #endif + +// #define GUF_CNT_T_IS_INTEGRAL_TYPE + typedef struct GUF_CNT_NAME { + guf_allocator *allocator; // Must not be NULL. GUF_CNT_T *data; ptrdiff_t size, capacity; } GUF_CNT_NAME; -static inline bool GUFCAT(GUF_CNT_NAME, _valid)(const GUF_CNT_NAME* dbuf) +bool GUFCAT(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); - return valid_data_ptr && dbuf->capacity >= 0 && dbuf->size >= 0 && dbuf->size <= 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; } -bool GUFCAT(GUF_CNT_NAME, _try_reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity, guf_err *err) +void GUFCAT(GUF_CNT_NAME, _try_reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity, guf_err *err) { GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); GUF_ASSERT_RELEASE(min_capacity >= 0); - GUF_ASSERT_RELEASE(err); if (min_capacity <= dbuf->capacity) { - err->type = GUF_ERR_NONE; - err->msg = NULL; - return true; + guf_err_set_if_not_null(err, GUF_ERR_NONE); + return; } + GUF_ASSERT(min_capacity > 0); if (!dbuf->data) { - GUF_CNT_T *data = calloc(min_capacity, sizeof(GUF_CNT_T)); + if (!guf_is_safe_size_calc(min_capacity, sizeof(GUF_CNT_T))) { + guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_reserve: overflow of ptrdiff_t")); + return; + } + GUF_CNT_T *data = dbuf->allocator->alloc(min_capacity * sizeof(GUF_CNT_T), dbuf->allocator->ctx); if (!data) { - err->type = GUF_ERR_ALLOC_FAIL; - err->msg = GUF_ERR_MSG("in function dbuf_reserve: Failed to allocate buffer"); - return false; + guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_reserve: failed to allocate initial buffer data")); + return; } dbuf->data = data; } else { - GUF_CNT_T *data = realloc(dbuf->data, guf_safe_size_calc(min_capacity, sizeof(GUF_CNT_T))); + GUF_CNT_T *data = dbuf->allocator->realloc(dbuf->data, dbuf->capacity * sizeof(GUF_CNT_T), min_capacity * sizeof(GUF_CNT_T), dbuf->allocator->ctx); if (!data) { - err->type = GUF_ERR_ALLOC_FAIL; - err->msg = GUF_ERR_MSG("in function dbuf_reserve: Failed to re-allocate buffer"); - return false; + guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_reserv: failed to reallocate buffer data")); + return; } dbuf->data = data; } + + guf_err_set_if_not_null(err, GUF_ERR_NONE); dbuf->capacity = min_capacity; - err->type = GUF_ERR_NONE; - err->msg = NULL; - return true; + return; } void GUFCAT(GUF_CNT_NAME, _reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity) { - guf_err err; - GUFCAT(GUF_CNT_NAME, _try_reserve)(dbuf, min_capacity, &err); - if (guf_is_err(&err)) { - guf_panic(&err); - } + GUFCAT(GUF_CNT_NAME, _try_reserve)(dbuf, min_capacity, NULL); } -static inline bool GUFCAT(GUF_CNT_NAME, _try_init)(GUF_CNT_NAME *dbuf, ptrdiff_t start_cap, guf_err *err) +void GUFCAT(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(err); - GUF_ASSERT_RELEASE(start_cap >= 0); + GUF_ASSERT_RELEASE(capacity >= 0); if (dbuf->size != 0 || dbuf->capacity != 0 || dbuf->data) { - err->type = GUF_ERR_INVALID_ARG; - err->msg = GUF_ERR_MSG("in function dbuf_try_init: dbuf might be already initialised. Set *dbuf to (dbuf){0} before calling this function."); - return false; + 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; - if (start_cap == 0) { - dbuf->data = NULL; - *err = GUF_SUCCESS; - return true; + 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 { - return GUFCAT(GUF_CNT_NAME, _try_reserve)(dbuf, start_cap, err); + dbuf->allocator = allocator; } + if (capacity == 0) { + dbuf->data = NULL; + guf_err_set_if_not_null(err, GUF_ERR_NONE); + } else { + GUFCAT(GUF_CNT_NAME, _try_reserve)(dbuf, capacity, err); + } } -static inline void GUFCAT(GUF_CNT_NAME, _init)(GUF_CNT_NAME *dbuf, ptrdiff_t start_cap) +void GUFCAT(GUF_CNT_NAME, _init)(GUF_CNT_NAME *dbuf, ptrdiff_t start_cap, guf_allocator *allocator) { - guf_err err; - GUFCAT(GUF_CNT_NAME, _try_init)(dbuf, start_cap, &err); - if (guf_is_err(&err)) { - guf_panic(&err); - } + GUFCAT(GUF_CNT_NAME, _try_init)(dbuf, start_cap, allocator, NULL); } -GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _new)(void) +GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _new)(guf_allocator *allocator) { GUF_CNT_NAME dbuf = {0}; - GUFCAT(GUF_CNT_NAME, _init)(&dbuf, 0); + GUFCAT(GUF_CNT_NAME, _init)(&dbuf, 0, allocator); GUF_ASSERT(dbuf.size == 0 && dbuf.capacity == 0); return dbuf; } -GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _try_new_with_capacity)(guf_err *err, ptrdiff_t capacity) +GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _try_new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator, guf_err *err) { - GUF_ASSERT_RELEASE(err); GUF_CNT_NAME dbuf = {0}; - GUFCAT(GUF_CNT_NAME, _try_init)(&dbuf, capacity, err); - if (guf_is_err(err)) { + + GUFCAT(GUF_CNT_NAME, _try_init)(&dbuf, capacity, allocator, err); + if (err && *err != GUF_ERR_NONE) { return (GUF_CNT_NAME){0}; - } - *err = GUF_SUCCESS; + } + guf_err_set_if_not_null(err, GUF_ERR_NONE); return dbuf; } -GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _new_with_capacity)(ptrdiff_t capacity) +GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator) { - guf_err err; - GUF_CNT_NAME new_cnt = GUFCAT(GUF_CNT_NAME, _try_new_with_capacity)(&err, capacity); - if (guf_is_err(&err)) { - guf_panic(&err); - } - return new_cnt; + return GUFCAT(GUF_CNT_NAME, _try_new_with_capacity)(capacity, allocator, NULL); } -static inline ptrdiff_t GUFCAT(GUF_CNT_NAME, _next_capacity)(ptrdiff_t old_cap, guf_err *err) +ptrdiff_t GUFCAT(GUF_CNT_NAME, _try_next_capacity)(ptrdiff_t old_cap, guf_err *err) { GUF_ASSERT_RELEASE(old_cap >= 0); - GUF_ASSERT_RELEASE(err); 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 { - new_cap = (size_t)old_cap * 3ull / 2ull; + #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); + GUF_ASSERT(new_cap <= PTRDIFF_MAX); - if (new_cap <= (size_t)old_cap || new_cap > PTRDIFF_MAX) { // Detect overflow. - err->type = GUF_ERR_INT_OVERFLOW; - err->msg = GUF_ERR_MSG("in function dbuf_next_capacity: the next capacity would overflow ptrdiff_t"); + 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_next_capacity: next capacity would overflow ptrdiff_t")); return -1; } else { - err->type = GUF_ERR_NONE; - err->msg = NULL; + guf_err_set_if_not_null(err, GUF_ERR_NONE); return new_cap; } } -static inline bool GUFCAT(GUF_CNT_NAME, _grow_if_full)(GUF_CNT_NAME *dbuf, guf_err *err) +void GUFCAT(GUF_CNT_NAME, _try_grow_if_full)(GUF_CNT_NAME *dbuf, guf_err *err) { + GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); GUF_ASSERT_RELEASE(dbuf->capacity >= 0 && dbuf->size >= 0); - GUF_ASSERT_RELEASE(err); if (dbuf->size == dbuf->capacity) { - ptrdiff_t next_cap = GUFCAT(GUF_CNT_NAME, _next_capacity)(dbuf->capacity, err); - if (guf_is_err(err)) { - return false; + ptrdiff_t next_cap = GUFCAT(GUF_CNT_NAME, _try_next_capacity)(dbuf->capacity, err); + if (err && *err != GUF_ERR_NONE) { + return; } GUF_ASSERT(next_cap > 0); GUFCAT(GUF_CNT_NAME, _try_reserve)(dbuf, next_cap, err); - if (guf_is_err(err)) { - return false; + if (err && *err != GUF_ERR_NONE) { + return; } } GUF_ASSERT_RELEASE(dbuf->size < dbuf->capacity); - err->type = GUF_ERR_NONE; - err->msg = NULL; - return true; + guf_err_set_if_not_null(err, GUF_ERR_NONE); } -GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_push)(GUF_CNT_NAME *dbuf, GUF_CNT_T *elem, guf_obj_cpy_opt cpy_opt, guf_err *err) +static inline bool GUFCAT(GUF_CNT_NAME, _copy_opt_available)(guf_obj_cpy_opt cpy_opt) { - GUF_ASSERT(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - GUF_ASSERT_RELEASE(err); + if (cpy_opt == GUF_CPY_DEEP) { + #ifdef GUF_CNT_T_COPY + return true; + #else + return false; + #endif + } else if (cpy_opt == GUF_CPY_MOVE) { + #ifdef GUF_CNT_T_MOVE + return true; + #else + return false; + #endif + } else if (cpy_opt == GUF_CPY_VALUE) { + return true; + } else { + return false; + } +} - if (cpy_opt == GUF_CPY_VALUE && GUF_CNT_T_OPS.free) { // Don't allow copy by value if there's a free operator - if (err) { - err->type = GUF_ERR_INVALID_ARG; - err->msg = GUF_ERR_MSG("in function dbuf_try_push: Refusing to copy elem by value (since dbuf has a non-null 'free' operator)"); - } +GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_insert)(GUF_CNT_NAME *dbuf, GUF_CNT_T *elem, ptrdiff_t idx, guf_obj_cpy_opt cpy_opt, guf_err *err) +{ + GUF_ASSERT(GUFCAT(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; } + + GUFCAT(GUF_CNT_NAME, _try_grow_if_full)(dbuf, err); + if (err && *err != GUF_ERR_NONE) { + return NULL; + } + + // printf("size %td, cap %td\n", dbuf->size, dbuf->capacity); + + 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]; + } - GUFCAT(GUF_CNT_NAME, _grow_if_full)(dbuf, err); - if (guf_is_err(err)) { - return NULL; - } + GUF_CNT_T *dst = dbuf->data + idx; - GUF_CNT_T *dst = dbuf->data + dbuf->size++; - bool cpy_success = false; - GUF_OBJ_CPY(GUF_CNT_T, GUF_CNT_T_OPS, dst, elem, cpy_opt, &cpy_success); - if (!cpy_success) { - err->type = GUF_ERR_ALLOC_FAIL; - err->msg = GUF_ERR_MSG("in function dbuf_try_push: Failed to copy elem"); + if (!GUFCAT(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(GUFCAT(GUF_CNT_NAME, _copy_opt_available)) ": cpy_opt unavailable")); + return NULL; + } else if (cpy_opt == GUF_CPY_DEEP) { + #ifdef GUF_CNT_T_COPY + dst = GUF_CNT_T_COPY(dst, elem); + #endif + } else if (cpy_opt == GUF_CPY_MOVE) { + #ifdef GUF_CNT_T_MOVE + dst = GUF_CNT_T_MOVE(dst, elem); + #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 { - err->type = GUF_ERR_NONE; - err->msg = NULL; - GUF_ASSERT(dst); + dbuf->size++; + guf_err_set_if_not_null(err, GUF_ERR_NONE); return dst; } } +GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _insert)(GUF_CNT_NAME *dbuf, GUF_CNT_T *elem, ptrdiff_t idx, guf_obj_cpy_opt cpy_opt) +{ + return GUFCAT(GUF_CNT_NAME, _try_insert)(dbuf, elem, idx, cpy_opt, NULL); +} + +GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_insert_val)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem, ptrdiff_t idx, guf_err *err) +{ + return GUFCAT(GUF_CNT_NAME, _try_insert)(dbuf, &elem, idx, GUF_CPY_VALUE, err); +} + +GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _insert_val)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem, ptrdiff_t idx) +{ + return GUFCAT(GUF_CNT_NAME, _try_insert_val)(dbuf, elem, idx, NULL); +} + +GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_insert_val_cpy)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem, ptrdiff_t idx, guf_err *err) +{ + return GUFCAT(GUF_CNT_NAME, _try_insert)(dbuf, &elem, idx, GUF_CPY_DEEP, err); +} + +GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _insert_val_cpy)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem, ptrdiff_t idx) +{ + return GUFCAT(GUF_CNT_NAME, _try_insert_val_cpy)(dbuf, elem, idx, NULL); +} + + +GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_push)(GUF_CNT_NAME *dbuf, GUF_CNT_T *elem, guf_obj_cpy_opt cpy_opt, guf_err *err) +{ + GUF_ASSERT(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + return GUFCAT(GUF_CNT_NAME, _try_insert)(dbuf, elem, dbuf->size, cpy_opt, err); +} GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _push)(GUF_CNT_NAME *dbuf, GUF_CNT_T *elem, guf_obj_cpy_opt cpy_opt) { - GUF_ASSERT(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - guf_err err; - GUF_CNT_T *pushed = GUFCAT(GUF_CNT_NAME, _try_push)(dbuf, elem, cpy_opt, &err); - if (guf_is_err(&err)) { - guf_panic(&err); - } - GUF_ASSERT(pushed); - return pushed; + return GUFCAT(GUF_CNT_NAME, _try_push)(dbuf, elem, cpy_opt, NULL); } GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_push_val)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem, guf_err *err) @@ -257,9 +346,10 @@ GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_push_val)(GUF_CNT_NAME *dbuf, GUF_CNT_T ele GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _push_val)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem) { - return GUFCAT(GUF_CNT_NAME, _push)(dbuf, &elem, GUF_CPY_VALUE); + return GUFCAT(GUF_CNT_NAME, _try_push_val)(dbuf, elem, NULL); } +#ifdef GUF_CNT_T_COPY GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_push_val_cpy)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem, guf_err *err) { return GUFCAT(GUF_CNT_NAME, _try_push)(dbuf, &elem, GUF_CPY_DEEP, err); @@ -267,231 +357,154 @@ GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_push_val_cpy)(GUF_CNT_NAME *dbuf, GUF_CNT_T GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _push_val_cpy)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem) { - return GUFCAT(GUF_CNT_NAME, _push)(dbuf, &elem, GUF_CPY_DEEP); + return GUFCAT(GUF_CNT_NAME, _try_push_val_cpy)(dbuf, elem, NULL); } +#endif -GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_insert)(GUF_CNT_NAME *dbuf, GUF_CNT_T *elem, ptrdiff_t idx, guf_obj_cpy_opt cpy_opt, guf_err *err) -{ - GUF_ASSERT_RELEASE(err); - GUF_ASSERT(GUFCAT(GUF_CNT_NAME,_valid)(dbuf)); - - if (idx < 0 || idx > dbuf->size) { - if (err) { - err->type = GUF_ERR_IDX_RANGE; - err->msg = GUF_ERR_MSG("in function dbuf_try_insert"); - } - return NULL; - } - - if (cpy_opt == GUF_CPY_VALUE && GUF_CNT_T_OPS.free) { // Don't allow copy by value if there's a free operation. - if (err) { - err->type = GUF_ERR_INVALID_ARG; - err->msg = GUF_ERR_MSG("in function dbuf_try_insert: Refusing to copy elem by value (since dbuf has a non-null 'free' operator)"); - } - return NULL; - } - - if (idx == dbuf->size) { - return GUFCAT(GUF_CNT_NAME, _try_push)(dbuf, elem, cpy_opt, err); - } - GUF_ASSERT(idx < dbuf->size); - - GUFCAT(GUF_CNT_NAME, _grow_if_full)(dbuf, err); - if (guf_is_err(err)) { - return NULL; - } - - for (ptrdiff_t free_idx = dbuf->size++; free_idx > idx; --free_idx) { - GUF_CNT_T *dst = dbuf->data + free_idx; - GUF_CNT_T *src = dst - 1; - *dst = *src; - } - - GUF_CNT_T *dst = dbuf->data + idx; - bool cpy_success = false; - GUF_OBJ_CPY(GUF_CNT_T, GUF_CNT_T_OPS, dst, elem, cpy_opt, &cpy_success) - - if (!cpy_success) { - if (err) { - err->type = GUF_ERR_ALLOC_FAIL; - err->msg = GUF_ERR_MSG("in function dbuf_try_insert: Failed to copy elem"); - } - return NULL; - } - - if (err) { - err->type = GUF_ERR_NONE; - err->msg = NULL; - } - GUF_ASSERT(dst != NULL) - return dst; -} - -GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _insert)(GUF_CNT_NAME *dbuf, GUF_CNT_T *elem, ptrdiff_t idx, guf_obj_cpy_opt cpy_opt) -{ - guf_err err; - GUF_CNT_T *inserted = GUFCAT(GUF_CNT_NAME, _try_insert)(dbuf, elem, idx, cpy_opt, &err); - if (guf_is_err(&err)) { - guf_panic(&err); - } - GUF_ASSERT(inserted); - return inserted; -} bool GUFCAT(GUF_CNT_NAME, _try_erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx, guf_err *err) { GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - GUF_ASSERT_RELEASE(err); if (dbuf->size == 0) { - err->type = GUF_ERR_IDX_RANGE, - err->msg = GUF_ERR_MSG("in function dbuf_try_erase: cannot erase from empty buffer"); + 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) { - err->type = GUF_ERR_IDX_RANGE; - err->msg = GUF_ERR_MSG("in function dbuf_try_erase"); + guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_erase")); return false; } - if (GUF_CNT_T_OPS.free) { - GUF_CNT_T_OPS.free(dbuf->data + idx); + #ifdef GUF_CNT_T_FREE + GUF_CNT_T_FREE(dbuf->data + idx); + #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]; } - - for (ptrdiff_t free_idx = idx; free_idx < dbuf->size - 1; ++free_idx) { - GUF_CNT_T *dst = dbuf->data + free_idx; - GUF_CNT_T *src = dst + 1; - *dst = *src; - } - - *err = GUF_SUCCESS; + dbuf->size--; + guf_err_set_if_not_null(err, GUF_ERR_NONE); return true; } void GUFCAT(GUF_CNT_NAME, _erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx) { - guf_err err; - GUFCAT(GUF_CNT_NAME, _try_erase)(dbuf, idx, &err); - if (guf_is_err(&err)) { - guf_panic(&err); - } - + GUFCAT(GUF_CNT_NAME, _try_erase)(dbuf, idx, NULL); } bool GUFCAT(GUF_CNT_NAME, _try_pop)(GUF_CNT_NAME *dbuf, guf_err *err) { GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - GUF_ASSERT_RELEASE(err); if (dbuf->size == 0) { - err->type = GUF_ERR_IDX_RANGE; - err->msg = GUF_ERR_MSG("in function dbuf_try_pop: Cannot pop from empty dbuf"); + 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; } - GUF_CNT_T *popped = dbuf->data + --dbuf->size; - if (GUF_CNT_T_OPS.free) { - GUF_CNT_T_OPS.free(popped); - } + #ifdef GUF_CNT_T_FREE + GUF_CNT_T *popped = dbuf->data + --dbuf->size; + GUF_CNT_T_FREE(popped); + #else + --dbuf->size; + #endif - *err = GUF_SUCCESS; + guf_err_set_if_not_null(err, GUF_ERR_NONE); return true; } void GUFCAT(GUF_CNT_NAME, _pop)(GUF_CNT_NAME *dbuf) { - guf_err err; - GUFCAT(GUF_CNT_NAME, _try_pop)(dbuf, &err); - if (guf_is_err(&err)) { - guf_panic(&err); - } + GUFCAT(GUF_CNT_NAME, _try_pop)(dbuf, NULL); } GUF_CNT_T GUFCAT(GUF_CNT_NAME, _try_pop_cpy)(GUF_CNT_NAME *dbuf, guf_obj_cpy_opt cpy_opt, guf_err *err) { GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - GUF_ASSERT_RELEASE(err); if (dbuf->size == 0) { - err->type = GUF_ERR_IDX_RANGE; - err->msg = GUF_ERR_MSG("in function dbuf_try_pop_cpy: Cannot pop from empty dbuf"); - GUF_CNT_T dummy = GUF_CNT_T_OPS.default_value; + 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_CNT_T dummy; + memset(&dummy, 0, sizeof(GUF_CNT_T)); return dummy; } GUF_CNT_T *popped = dbuf->data + (dbuf->size - 1); GUF_CNT_T popped_val; - - bool cpy_success = false; - GUF_OBJ_CPY(GUF_CNT_T, GUF_CNT_T_OPS, &popped_val, popped, cpy_opt, &cpy_success); - if (!cpy_success) { - err->type = GUF_ERR_ALLOC_FAIL; - err->msg = GUF_ERR_MSG("in function dbuf_try_pop_cpy: Failed on copy"); - GUF_CNT_T dummy = GUF_CNT_T_OPS.default_value; - return dummy; + GUF_CNT_T *dst = &popped_val; + + if (!GUFCAT(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(GUFCAT(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_CNT_T_COPY + dst = GUF_CNT_T_COPY(dst, popped); + #else + GUF_ASSERT_RELEASE(false); + #endif + } else if (cpy_opt == GUF_CPY_MOVE) { + #ifdef GUF_CNT_T_MOVE + dst = GUF_CNT_T_MOVE(dst, popped); + #else + GUF_ASSERT_RELEASE(false); + #endif } else { - dbuf->size -= 1; + GUF_ASSERT_RELEASE(cpy_opt == GUF_CPY_VALUE); + *dst = *popped; } - if (cpy_opt == GUF_CPY_DEEP && GUF_CNT_T_OPS.free) { - GUF_CNT_T_OPS.free(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_CNT_T)); + return popped_val; // Return a dummy value in case the copy failed (as we have to return something). + } else { + #ifdef GUF_CNT_T_FREE + if (cpy_opt == GUF_CPY_DEEP) { + GUF_CNT_T_FREE(popped); + } + #endif + dbuf->size--; + guf_err_set_if_not_null(err, GUF_ERR_NONE); + return popped_val; } - - *err = GUF_SUCCESS; - return popped_val; } GUF_CNT_T GUFCAT(GUF_CNT_NAME, _pop_cpy)(GUF_CNT_NAME *dbuf, guf_obj_cpy_opt cpy_opt) { - guf_err err; - GUF_CNT_T popped = GUFCAT(GUF_CNT_NAME, _try_pop_cpy)(dbuf, cpy_opt, &err); - if (guf_is_err(&err)) { - guf_panic(&err); - } - return popped; + return GUFCAT(GUF_CNT_NAME, _try_pop_cpy)(dbuf, cpy_opt, NULL); } GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx, guf_err *err) { GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - GUF_ASSERT(err); + if (dbuf->size == 0) { - err->type = GUF_ERR_IDX_RANGE; - err->msg = GUF_ERR_MSG("in function dbuf_try_at: dbuf is empty"); + 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) { - err->type = GUF_ERR_IDX_RANGE; - err->msg = GUF_ERR_MSG("in function dbuf_try_at"); + guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_at")); return NULL; } GUF_ASSERT(dbuf->data); - *err = GUF_SUCCESS; + guf_err_set_if_not_null(err, GUF_ERR_NONE); return dbuf->data + idx; } GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx) { - guf_err err; - GUF_CNT_T *res = GUFCAT(GUF_CNT_NAME, _try_at)(dbuf, idx, &err); - if (guf_is_err(&err)) { - guf_panic(&err); - } - GUF_ASSERT(res); - return res; + return GUFCAT(GUF_CNT_NAME, _try_at)(dbuf, idx, NULL); } GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_front)(GUF_CNT_NAME *dbuf, guf_err *err) { GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - GUF_ASSERT_RELEASE(err); if (dbuf->size == 0) { - err->type = GUF_ERR_IDX_RANGE; - err->msg = GUF_ERR_MSG("in function dbuf_front: dbuf is empty"); + 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; @@ -499,61 +512,54 @@ GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_front)(GUF_CNT_NAME *dbuf, guf_err *err) GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _front)(GUF_CNT_NAME *dbuf) { - guf_err err; - GUF_CNT_T *res = GUFCAT(GUF_CNT_NAME, _try_front)(dbuf, &err); - if (guf_is_err(&err)) { - guf_panic(&err); - } - GUF_ASSERT(res); - return res; + return GUFCAT(GUF_CNT_NAME, _try_front)(dbuf, NULL); } + GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_back)(GUF_CNT_NAME *dbuf, guf_err *err) { GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - GUF_ASSERT_RELEASE(err); if (dbuf->size == 0) { - err->type = GUF_ERR_IDX_RANGE; - err->msg = GUF_ERR_MSG("in function dbuf_try_back: dbuf is empty"); + guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_back: dbuf is empty")); return NULL; } - *err = GUF_SUCCESS; + guf_err_set_if_not_null(err, GUF_ERR_NONE); return dbuf->data + (dbuf->size - 1); - } GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _back)(GUF_CNT_NAME *dbuf) { - guf_err err; - GUF_CNT_T *res = GUFCAT(GUF_CNT_NAME, _try_back)(dbuf, &err); - if (guf_is_err(&err)) { - guf_panic(&err); - } - GUF_ASSERT(res); - return res; + return GUFCAT(GUF_CNT_NAME, _try_back)(dbuf, NULL); } -bool GUFCAT(GUF_CNT_NAME, _try_shrink_to_fit)(GUF_CNT_NAME *dbuf) +void GUFCAT(GUF_CNT_NAME, _try_shrink_to_fit)(GUF_CNT_NAME *dbuf, guf_err *err) { GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); const ptrdiff_t new_capacity = dbuf->size; - if (new_capacity == dbuf->capacity) { - return true; + 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); - GUF_CNT_T *data = realloc(dbuf->data, guf_safe_size_calc(new_capacity, sizeof(GUF_CNT_T))); - GUF_ASSERT(data); + if (new_capacity == 0) { + dbuf->allocator->free(dbuf->data, sizeof(GUF_CNT_T) * dbuf->capacity, dbuf->allocator->ctx); + return; + } + + GUF_CNT_T *data = dbuf->allocator->realloc(dbuf->data, new_capacity, sizeof(GUF_CNT_T), dbuf->allocator->ctx); if (!data) { - return false; + 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; - return true; + guf_err_set_if_not_null(err, GUF_ERR_NONE); } + void GUFCAT(GUF_CNT_NAME, _free)(GUF_CNT_NAME *dbuf) { GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); @@ -563,90 +569,146 @@ void GUFCAT(GUF_CNT_NAME, _free)(GUF_CNT_NAME *dbuf) return; } GUF_ASSERT_RELEASE(dbuf->data); - if (GUF_CNT_T_OPS.free) { + + #ifdef GUF_CNT_T_FREE for (ptrdiff_t idx = 0; idx < dbuf->size; ++idx) { - GUF_CNT_T_OPS.free(dbuf->data + idx); + GUF_CNT_T_FREE(dbuf->data + idx); } - } - free(dbuf->data); + #endif + + dbuf->allocator->free(dbuf->data, sizeof(GUF_CNT_T) * dbuf->capacity, dbuf->allocator->ctx); dbuf->data = NULL; dbuf->capacity = dbuf->size = 0; } -void GUFCAT(GUF_CNT_NAME, _sort)(GUF_CNT_NAME *dbuf, guf_sort_opt sort_opt) -{ - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - - if (dbuf->size == 0) { - return; - } - - if (sort_opt == GUF_SORT_ASCENDING) { - GUF_ASSERT_RELEASE(GUF_CNT_T_OPS.cmp_void); - qsort(dbuf->data, dbuf->size, sizeof(GUF_CNT_T), GUF_CNT_T_OPS.cmp_void); - } else if (sort_opt == GUF_SORT_DESCENDING) { - GUF_ASSERT_RELEASE(GUF_CNT_T_OPS.cmp_void_inv); - qsort(dbuf->data, dbuf->size, sizeof(GUF_CNT_T), GUF_CNT_T_OPS.cmp_void_inv); - } else { - GUF_ASSERT_RELEASE(false); - } -} typedef struct GUFCAT(GUF_CNT_NAME, _iter) { - GUF_CNT_T *cur, *end, *begin; - struct GUFCAT(GUF_CNT_NAME, _iter) (*next)(const struct GUFCAT(GUF_CNT_NAME, _iter) *it, ptrdiff_t step); + GUF_CNT_T *ptr; + bool reverse; + } GUFCAT(GUF_CNT_NAME, _iter); -static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _iter_next)(const GUFCAT(GUF_CNT_NAME, _iter) *it, ptrdiff_t step) -{ - GUF_ASSERT_RELEASE(it); - if (it->begin == NULL && it->end == NULL) { - return *it; - } - GUF_ASSERT_RELEASE(it->begin != NULL && it->end != NULL); - GUF_ASSERT_RELEASE(it->cur < it->end); - - GUFCAT(GUF_CNT_NAME, _iter) next_it = *it; - intptr_t next_ptr = (intptr_t)it->cur + step * sizeof(GUF_CNT_T); - - if (next_ptr >= (intptr_t)it->end) { - next_it.cur = it->end; - } else if (next_ptr < (intptr_t)it->begin) { // TODO: different end for begin - next_it.cur = it->end; - } else { - GUF_ASSERT(next_ptr >= (intptr_t)it->begin); - GUF_ASSERT(next_ptr <= (intptr_t)it->end); - GUF_ASSERT(it->cur + step >= it->begin && it->cur + step <= it->end); - next_it.cur = it->cur + step; - } - return next_it; -} - static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _begin)(const GUF_CNT_NAME* dbuf) { - GUFCAT(GUF_CNT_NAME, _valid)(dbuf); + GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); return (GUFCAT(GUF_CNT_NAME, _iter)) { - .cur = dbuf->data, - .begin = dbuf->data, - .end = dbuf->data ? dbuf->data + dbuf->size : NULL, - .next = GUFCAT(GUF_CNT_NAME, _iter_next) + .ptr = dbuf->data, + .reverse = false }; } static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _end)(const GUF_CNT_NAME* dbuf) { - GUFCAT(GUF_CNT_NAME, _valid)(dbuf); + GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); return (GUFCAT(GUF_CNT_NAME, _iter)) { - .cur = dbuf->data ? dbuf->data + dbuf->size : NULL, - .begin = dbuf->data, - .end = dbuf->data ? dbuf->data + dbuf->size : NULL, - .next = GUFCAT(GUF_CNT_NAME, _iter_next) + .ptr = dbuf->data ? dbuf->data + dbuf->size : NULL, + .reverse = false }; } -#undef GUF_DBUF_INITIAL_CAP +static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _rbegin)(const GUF_CNT_NAME* dbuf) +{ + GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + return (GUFCAT(GUF_CNT_NAME, _iter)) { + .ptr = dbuf->data ? dbuf->data + (dbuf->size > 0 ? dbuf->size - 1 : 0) : NULL, + .reverse = true + }; +} +static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _rend)(const GUF_CNT_NAME* dbuf) +{ + GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + return (GUFCAT(GUF_CNT_NAME, _iter)) { + .ptr = dbuf->data ? dbuf->data + dbuf->size : NULL, + .reverse = true + }; +} + +static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _iter_next)(const GUF_CNT_NAME *dbuf, GUFCAT(GUF_CNT_NAME, _iter) it, ptrdiff_t step) +{ + GUF_ASSERT(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + + GUF_CNT_T *begin_ptr = dbuf->data; + GUF_CNT_T *end_ptr = dbuf->data ? dbuf->data + dbuf->size : NULL; + + if (!dbuf->size || !dbuf->data || !it.ptr) { + it.ptr = end_ptr; + return it; + } + + if (it.reverse) { + if (step < 0) { + GUF_ASSERT_RELEASE(step > PTRDIFF_MIN); // Catch overflow. + } + step = -step; + } + + intptr_t next_ptr; + if (it.ptr == end_ptr && it.reverse) { // Handle rend + next_ptr = (intptr_t)dbuf->data + (step - 1) * sizeof(GUF_CNT_T); + } else { + next_ptr = (intptr_t)it.ptr + step * sizeof(GUF_CNT_T); + } + + if (next_ptr >= (intptr_t)end_ptr) { + it.ptr = end_ptr; + } else if (next_ptr < (intptr_t)dbuf->data) { + it.ptr = end_ptr; + } else { + GUF_ASSERT(next_ptr >= (intptr_t)begin_ptr); + GUF_ASSERT(next_ptr <= (intptr_t)end_ptr); + GUF_ASSERT(it.ptr + step >= begin_ptr && it.ptr + step <= end_ptr); + it.ptr = it.ptr + step; + } + return it; +} + +#if defined(GUF_CNT_T_IS_INTEGRAL_TYPE) || defined(GUF_CNT_T_EQ) +GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _find)(GUF_CNT_NAME *dbuf, GUFCAT(GUF_CNT_NAME, _iter) begin, GUFCAT(GUF_CNT_NAME, _iter) end, const GUF_CNT_T *needle) +{ + GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + GUF_ASSERT_RELEASE(needle); + + for (GUFCAT(GUF_CNT_NAME, _iter) it = begin; it.ptr != end.ptr; it = GUFCAT(GUF_CNT_NAME, _iter_next)(dbuf, it, 1)) { + #ifdef GUF_CNT_T_EQ + if (GUF_CNT_T_EQ(it.ptr, needle)) { + return it; + } + #else + if (*it.ptr == *needle) { + return it; + } + #endif + } + return GUFCAT(GUF_CNT_NAME, _end)(dbuf); +} + +GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _find_val)(GUF_CNT_NAME *dbuf, GUFCAT(GUF_CNT_NAME, _iter) begin, GUFCAT(GUF_CNT_NAME, _iter) end, GUF_CNT_T needle_val) +{ + return GUFCAT(GUF_CNT_NAME, _find)(dbuf, begin, end, &needle_val); +} + +GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _find_if)(GUF_CNT_NAME *dbuf, GUFCAT(GUF_CNT_NAME, _iter) *begin, GUFCAT(GUF_CNT_NAME, _iter) *end, bool (*predicate)(const GUF_CNT_T *)) +{ + GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + GUF_ASSERT_RELEASE(begin && end && predicate); + + for (GUFCAT(GUF_CNT_NAME, _iter) it = *begin; it.ptr != end->ptr; GUFCAT(GUF_CNT_NAME, _iter_next)(dbuf, it, 1)) { + if (predicate(it.ptr)) { + return it; + } + } + return GUFCAT(GUF_CNT_NAME, _end)(dbuf); +} +#endif + +#undef GUF_DBUF_INITIAL_CAP +#undef GUF_DBUF_USE_GROWTH_FAC_ONE_POINT_FIVE + +#undef GUF_CNT_NAME #undef GUF_CNT_T -#undef GUF_CNT_T_OPS -#undef GUF_CNT_T_OPS_CPY -#undef GUF_CNT_NAME \ No newline at end of file +#undef GUF_CNT_T_COPY +#undef GUF_CNT_T_MOVE +#undef GUF_CNT_T_FREE +#undef GUF_CNT_T_EQ +#undef GUF_CNT_T_IS_INTEGRAL_TYPE \ No newline at end of file diff --git a/src/guf_int.h b/src/guf_int.h index 0056a96..7961a01 100644 --- a/src/guf_int.h +++ b/src/guf_int.h @@ -2,15 +2,12 @@ #define GUF_INT_H #include "guf_common.h" -#define GUF_MIN(X, Y) ((X) <= (Y) ? (X) : (Y)) -#define GUF_MAX(X, Y) ((X) >= (Y) ? (X) : (Y)) -#define GUF_CLAMP(X, MIN, MAX) GUF_MAX(GUF_MIN((X), (MAX)), (MIN)) - #define GUF_DEFINE_MIN_MAX_CLAMP(int_type, int_type_name)\ static inline int_type GUFCAT(guf_min_, int_type_name)(int_type a, int_type b) {return a >= b ? a : b;}\ static inline int_type GUFCAT(guf_max_, int_type_name)(int_type a, int_type b) {return a >= b ? a : b;}\ static inline int_type GUFCAT(guf_clamp_, int_type_name)(int_type x, int_type min, int_type max) {return GUFCAT(guf_max_, int_type_name)(GUFCAT(guf_min_, int_type_name)(x, max), min);} +GUF_DEFINE_MIN_MAX_CLAMP(char, char) GUF_DEFINE_MIN_MAX_CLAMP(int, int) GUF_DEFINE_MIN_MAX_CLAMP(short, short) GUF_DEFINE_MIN_MAX_CLAMP(long, long) @@ -19,22 +16,22 @@ GUF_DEFINE_MIN_MAX_CLAMP(int8_t, i8) GUF_DEFINE_MIN_MAX_CLAMP(int16_t, i16) GUF_DEFINE_MIN_MAX_CLAMP(int32_t, i32) GUF_DEFINE_MIN_MAX_CLAMP(int64_t, i64) +GUF_DEFINE_MIN_MAX_CLAMP(ptrdiff_t, ptrdiff_t) +GUF_DEFINE_MIN_MAX_CLAMP(unsigned char, unsigned_char) GUF_DEFINE_MIN_MAX_CLAMP(unsigned, unsigned) -GUF_DEFINE_MIN_MAX_CLAMP(unsigned short, unsigned_short) +GUF_DEFINE_MIN_MAX_CLAMP(unsigned short, u_short) GUF_DEFINE_MIN_MAX_CLAMP(unsigned long, unsigned_long) GUF_DEFINE_MIN_MAX_CLAMP(unsigned long long, unsigned_long_long) GUF_DEFINE_MIN_MAX_CLAMP(uint8_t, u8) GUF_DEFINE_MIN_MAX_CLAMP(uint16_t, u16) GUF_DEFINE_MIN_MAX_CLAMP(uint32_t, u32) GUF_DEFINE_MIN_MAX_CLAMP(uint64_t, u64) - -GUF_DEFINE_MIN_MAX_CLAMP(char, guf_char) -GUF_DEFINE_MIN_MAX_CLAMP(unsigned char, guf_unsigned_char) +GUF_DEFINE_MIN_MAX_CLAMP(size_t, size_t) #undef GUF_DEFINE_MIN_MAX_CLAMP -static inline int guf_int(int x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT_MIN ); return -x;} +static inline int guf_abs_int(int x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT_MIN ); return -x;} // I would not drink that... static inline long guf_abs_long(long x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > LONG_MIN ); return -x;} static inline long long guf_abs_long_long(long long x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > LLONG_MIN ); return -x;} static inline int8_t guf_abs_i8 (int8_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT8_MIN ); return -x;} @@ -43,231 +40,4 @@ static inline int32_t guf_abs_i32(int32_t x) {if (x >= 0) {return x;} static inline int64_t guf_abs_i64(int64_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT64_MIN); return -x;} static inline ptrdiff_t guf_abs_ptrdiff(ptrdiff_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > PTRDIFF_MIN); return -x;} -static inline bool guf_is_mul_overflow_size_t(size_t a, size_t b) -{ - size_t c = a * b; - return a != 0 && ((c / a) != b); -} - -static inline size_t guf_safe_mul_size_t(size_t a, size_t b) -{ - GUF_ASSERT_RELEASE(!guf_is_mul_overflow_size_t(a, b)); - return a * b; -} - -static inline bool guf_is_safe_size_calc(ptrdiff_t count, ptrdiff_t sizeof_elem) -{ - if (count < 0 || sizeof_elem <= 0) { - return false; - } - if (guf_is_mul_overflow_size_t(count, sizeof_elem)) { - return false; - } - size_t size = (size_t)count * (size_t)sizeof_elem; - return size <= PTRDIFF_MAX; -} - -static inline ptrdiff_t guf_safe_size_calc(ptrdiff_t count, ptrdiff_t sizeof_elem) -{ - GUF_ASSERT_RELEASE(count >= 0); - GUF_ASSERT_RELEASE(sizeof_elem > 0); - size_t size = guf_safe_mul_size_t(count, sizeof_elem); - GUF_ASSERT_RELEASE(size <= PTRDIFF_MAX); - return size; -} - -// Signed integer types: - -#define GUF_OBJ_TYPE int -#define GUF_OBJ_OPS_TYPENAME guf_int_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE short -#define GUF_OBJ_OPS_TYPENAME guf_short_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE long -#define GUF_OBJ_OPS_TYPENAME guf_long_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE long long -#define GUF_OBJ_OPS_TYPENAME guf_long_long_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE int8_t -#define GUF_OBJ_OPS_TYPENAME guf_int8_t_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE int16_t -#define GUF_OBJ_OPS_TYPENAME guf_int16_t_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE int32_t -#define GUF_OBJ_OPS_TYPENAME guf_int32_t_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE int64_t -#define GUF_OBJ_OPS_TYPENAME guf_int64_t_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE ptrdiff_t -#define GUF_OBJ_OPS_TYPENAME guf_ptrdiff_t_ops_type -#include "guf_obj.h" - -// Unsigned integer types: - -#define GUF_OBJ_TYPE unsigned -#define GUF_OBJ_OPS_TYPENAME guf_unsigned_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE unsigned short -#define GUF_OBJ_OPS_TYPENAME guf_unsigned_short_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE unsigned long -#define GUF_OBJ_OPS_TYPENAME guf_unsigned_long_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE unsigned long long -#define GUF_OBJ_OPS_TYPENAME guf_unsigned_long_long_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE uint8_t -#define GUF_OBJ_OPS_TYPENAME guf_uint8_t_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE uint16_t -#define GUF_OBJ_OPS_TYPENAME guf_uint16_t_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE uint32_t -#define GUF_OBJ_OPS_TYPENAME guf_uint32_t_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE uint64_t -#define GUF_OBJ_OPS_TYPENAME guf_uint64_t_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE size_t -#define GUF_OBJ_OPS_TYPENAME guf_size_t_ops_type -#include "guf_obj.h" - -// Signed/Unsigned character types: - -#define GUF_OBJ_TYPE char -#define GUF_OBJ_OPS_TYPENAME guf_char_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE unsigned char -#define GUF_OBJ_OPS_TYPENAME guf_unsigned_char_ops_type -#include "guf_obj.h" - -// Floating point: - -#define GUF_OBJ_TYPE float -#define GUF_OBJ_OPS_TYPENAME guf_float_ops_type -#include "guf_obj.h" - -#define GUF_OBJ_TYPE double -#define GUF_OBJ_OPS_TYPENAME guf_double_ops_type -#include "guf_obj.h" - -extern const guf_int_ops_type guf_int_ops; -extern const guf_short_ops_type guf_short_ops; -extern const guf_long_ops_type guf_long_ops; -extern const guf_long_long_ops_type guf_long_long_ops; -extern const guf_ptrdiff_t_ops_type guf_ptrdiff_t_ops; -extern const guf_int8_t_ops_type guf_int8_t_ops; -extern const guf_int16_t_ops_type guf_int16_t_ops; -extern const guf_int32_t_ops_type guf_int32_t_ops; -extern const guf_int64_t_ops_type guf_int64_t_ops; - -extern const guf_unsigned_ops_type guf_unsigned_ops; -extern const guf_unsigned_short_ops_type guf_unsigned_short_ops; -extern const guf_unsigned_long_ops_type guf_unsigned_long_ops; -extern const guf_unsigned_long_long_ops_type guf_unsigned_long_long_ops; -extern const guf_size_t_ops_type guf_size_t_ops; -extern const guf_uint8_t_ops_type guf_uint8_t_ops; -extern const guf_uint16_t_ops_type guf_uint16_t_ops; -extern const guf_uint32_t_ops_type guf_uint32_t_ops; -extern const guf_uint64_t_ops_type guf_uint64_t_ops; - -extern const guf_char_ops_type guf_char_ops; -extern const guf_unsigned_char_ops_type guf_unsigned_char_ops; - -extern const guf_float_ops_type guf_float_ops; -extern const guf_double_ops_type guf_double_ops; - -#ifdef GUF_IMPLEMENTATION - -#define GUF_DEFINE_INTEGER_CMP(int_type, fn_name) \ - int fn_name(const int_type *a, const int_type *b) {GUF_ASSERT(a && b); return (*a == *b ? 0 : *a < *b ? -1 : 1);} \ - GUF_OBJ_OPS_DEFINE_CMP_VOID(int_type, fn_name) \ - GUF_OBJ_OPS_DEFINE_CMP_VOID_INV(int_type, fn_name) - -#define GUF_DEFINE_INTEGER_OPS(int_type)\ - const GUFCAT(int_type, _ops_type) GUFCAT(int_type, _ops) = {\ - .cmp = GUFCAT(int_type, _cmp),\ - .cmp_void = GUFCAT(int_type, _cmp_void), \ - .cmp_void_inv = GUFCAT(int_type, _cmp_void_inv), \ - .default_value = 0,\ - .eq = NULL, \ - .copy_init = NULL,\ - .move_init = NULL,\ - .free = NULL\ - };\ - -GUF_DEFINE_INTEGER_CMP(int, guf_int_cmp) -GUF_DEFINE_INTEGER_CMP(short, guf_short_cmp) -GUF_DEFINE_INTEGER_CMP(long, guf_long_cmp) -GUF_DEFINE_INTEGER_CMP(long long, guf_long_long_cmp) -GUF_DEFINE_INTEGER_CMP(int8_t, guf_int8_t_cmp) -GUF_DEFINE_INTEGER_CMP(int16_t, guf_int16_t_cmp) -GUF_DEFINE_INTEGER_CMP(int32_t, guf_int32_t_cmp) -GUF_DEFINE_INTEGER_CMP(int64_t, guf_int64_t_cmp) -GUF_DEFINE_INTEGER_CMP(ptrdiff_t, guf_ptrdiff_t_cmp) - -GUF_DEFINE_INTEGER_CMP(unsigned, guf_unsigned_cmp) -GUF_DEFINE_INTEGER_CMP(unsigned short, guf_unsigned_short_cmp) -GUF_DEFINE_INTEGER_CMP(unsigned long, guf_unsigned_long_cmp) -GUF_DEFINE_INTEGER_CMP(unsigned long long, guf_unsigned_long_long_cmp) -GUF_DEFINE_INTEGER_CMP(uint8_t, guf_uint8_t_cmp) -GUF_DEFINE_INTEGER_CMP(uint16_t, guf_uint16_t_cmp) -GUF_DEFINE_INTEGER_CMP(uint32_t, guf_uint32_t_cmp) -GUF_DEFINE_INTEGER_CMP(uint64_t, guf_uint64_t_cmp) -GUF_DEFINE_INTEGER_CMP(size_t, guf_size_t_cmp) - -GUF_DEFINE_INTEGER_CMP(char, guf_char_cmp) -GUF_DEFINE_INTEGER_CMP(unsigned char, guf_unsigned_char_cmp) - -GUF_DEFINE_INTEGER_CMP(float, guf_float_cmp) -GUF_DEFINE_INTEGER_CMP(double, guf_double_cmp) - -GUF_DEFINE_INTEGER_OPS(guf_int) -GUF_DEFINE_INTEGER_OPS(guf_short) -GUF_DEFINE_INTEGER_OPS(guf_long_long) -GUF_DEFINE_INTEGER_OPS(guf_int8_t) -GUF_DEFINE_INTEGER_OPS(guf_int16_t) -GUF_DEFINE_INTEGER_OPS(guf_int32_t) -GUF_DEFINE_INTEGER_OPS(guf_int64_t) - -GUF_DEFINE_INTEGER_OPS(guf_unsigned) -GUF_DEFINE_INTEGER_OPS(guf_unsigned_short) -GUF_DEFINE_INTEGER_OPS(guf_unsigned_long_long) -GUF_DEFINE_INTEGER_OPS(guf_uint8_t) -GUF_DEFINE_INTEGER_OPS(guf_uint16_t) -GUF_DEFINE_INTEGER_OPS(guf_uint32_t) -GUF_DEFINE_INTEGER_OPS(guf_uint64_t) - -GUF_DEFINE_INTEGER_OPS(guf_char) -GUF_DEFINE_INTEGER_OPS(guf_unsigned_char) - -GUF_DEFINE_INTEGER_OPS(guf_float) -GUF_DEFINE_INTEGER_OPS(guf_double) - -#undef GUF_DEFINE_INTEGER_OPS -#undef GUF_DEFINE_INTEGER_CMP - -#endif - #endif \ No newline at end of file diff --git a/src/guf_obj.h b/src/guf_obj.h index 2a0bbf3..c8e76c4 100644 --- a/src/guf_obj.h +++ b/src/guf_obj.h @@ -2,30 +2,6 @@ #define GUF_OBJ_H #include #include "guf_common.h" - - #define GUF_OBJ_OPS_INTEGRAL_TYPE(obj_ops_type, eq, cmp, cmp_void, cmp_void_inv) (obj_ops_type){.copy_init = NULL, .move_init = NULL, .free = NULL, .eq = eq, .cmp = cmp, .cmp_void = cmp_void, .cmp_void_inv = cmp_void_inv} - #define GUF_OBJ_OPS_EMPTY(obj_ops_type) (obj_ops_type){.copy_init = NULL, .move_init = NULL, .free = NULL, .eq = NULL, .cmp = NULL, .cmp_void = NULL, .cmp_void_inv = NULL} - - #define GUF_OBJ_OPS_DEFINE_CMP_VOID(obj_type, cmp_fn) int GUFCAT(cmp_fn, _void)(const void *a, const void *b) {return cmp_fn((const obj_type*)a, (const obj_type*)b);} - #define GUF_OBJ_OPS_DEFINE_CMP_VOID_INV(obj_type, cmp_fn) int GUFCAT(cmp_fn, _void_inv)(const void *a, const void *b) {return -cmp_fn((const obj_type*)a, (const obj_type*)b);} - - #define GUF_OBJ_CPY(obj_type, obj_ops, dst_ptr, src_ptr, cpy_opts, success_bool_ptr) do { \ - obj_type *obj_cpy_dst_ptr = dst_ptr;\ - obj_type *obj_cpy_src_ptr = src_ptr;\ - if (cpy_opt == GUF_CPY_VALUE) {\ - *obj_cpy_dst_ptr = *obj_cpy_src_ptr;\ - *success_bool_ptr = true;\ - } else if (cpy_opt == GUF_CPY_DEEP) {\ - GUF_ASSERT_RELEASE(obj_ops.copy_init);\ - if (obj_ops.copy_init(obj_cpy_dst_ptr, obj_cpy_src_ptr)) {*success_bool_ptr = true;} else {*success_bool_ptr = false;}\ - } else if (cpy_opt == GUF_CPY_MOVE) {\ - GUF_ASSERT_RELEASE(obj_ops.move_init);\ - if (obj_ops.move_init(obj_cpy_dst_ptr, obj_cpy_src_ptr)) {*success_bool_ptr = true;} else {*success_bool_ptr = false;}\ - } else {\ - GUF_ASSERT_RELEASE(false);\ - }\ - } while (0);\ - #endif /* @@ -36,28 +12,26 @@ - GUF_OBJ_OPS_TYPENAME: The typename of the operations struct for the object (default: GUFCAT(GUF_OBJ_TYPE, _ops)) */ -#ifndef GUF_OBJ_TYPE - #error "GUF_OBJ_TYPE not set" -#endif +// #ifndef GUF_OBJ_TYPE +// #error "GUF_OBJ_TYPE not set" +// #endif -#ifndef GUF_OBJ_OPS_TYPENAME - #define GUF_OBJ_OPS_TYPENAME GUFCAT(GUF_OBJ_TYPE, _ops_type) -#endif +// #ifndef GUF_OBJ_OPS_TYPENAME +// #define GUF_OBJ_OPS_TYPENAME GUFCAT(GUF_OBJ_TYPE, _ops_type) +// #endif // #ifndef GUF_OBJ_OPS_NAME // #define GUF_OBJ_OPS_TYPENAME GUFCAT(GUF_OBJ_TYPE, _ops) // #endif -typedef struct GUF_OBJ_OPS_TYPENAME { - GUF_OBJ_TYPE default_value; - GUF_OBJ_TYPE *(*copy_init)(GUF_OBJ_TYPE *dst, const GUF_OBJ_TYPE *src); - GUF_OBJ_TYPE *(*move_init)(GUF_OBJ_TYPE *dst, GUF_OBJ_TYPE *src); - void (*free)(GUF_OBJ_TYPE *obj); - bool (*eq)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b); - int (*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b); - int (*cmp_void)(const void *a, const void *b); - int (*cmp_void_inv)(const void *a, const void *b); -} GUF_OBJ_OPS_TYPENAME; +// typedef struct GUF_OBJ_OPS_TYPENAME { +// GUF_OBJ_TYPE *(*copy)(GUF_OBJ_TYPE *dst, const GUF_OBJ_TYPE *src); +// GUF_OBJ_TYPE *(*move)(GUF_OBJ_TYPE *dst, GUF_OBJ_TYPE *src); +// void (*free)(GUF_OBJ_TYPE *obj); +// bool (*eq)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b); +// int (*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b); // a < b -> -1; a == b -> 0; a > b -> 1 +// guf_hash_size_t (*hash)(const GUF_OBJ_TYPE *obj); +// } GUF_OBJ_OPS_TYPENAME; #undef GUF_OBJ_TYPE #undef GUF_OBJ_OPS_TYPENAME \ No newline at end of file diff --git a/src/guf_sort.h b/src/guf_sort.h new file mode 100644 index 0000000..ffc8800 --- /dev/null +++ b/src/guf_sort.h @@ -0,0 +1,160 @@ +#ifndef GUF_SORT_H +#define GUF_SORT_H +#include +#include "guf_common.h" +#endif + +#ifndef GUF_OBJ_TYPE + #error "GUF_OBJ_TYPE not set" +#endif + +#ifndef GUF_OBJ_TYPE_NAME + #define GUF_OBJ_TYPE_NAME GUF_OBJ_TYPE +#endif + +#define GUF_FN_KEYWORDS static inline + +#ifdef GUF_DECLARATIONS_ONLY +GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _insertion_sort)(GUF_OBJ_TYPE *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b)); +GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _merge_sort)(GUF_OBJ_TYPE *arr, GUF_OBJ_TYPE *arr_tmp, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b)); +GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _qsort)(GUF_OBJ_TYPE *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b)) +#else + +#define guf_before(a_ptr, b_ptr) (cmp ? (sort_opt == GUF_SORT_ASCENDING ? 1 : -1) * cmp(a_ptr, b_ptr) == -1 : (sort_opt == GUF_SORT_ASCENDING) ? *(a_ptr) < *(b_ptr) : *(a_ptr) > *(b_ptr)) +#define guf_before_or_equal(a_ptr, b_ptr) (cmp ? (sort_opt == GUF_SORT_ASCENDING ? 1 : -1) * cmp(a_ptr, b_ptr) <= 0 : (sort_opt == GUF_SORT_ASCENDING) ? *(a_ptr) <= *(b_ptr) : *(a_ptr) >= *(b_ptr)) +/* + Insertion sort. + - stable: yes + - time: O(n^2) (worst, average, and best) + - space: O(1) +*/ +GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _insertion_sort)(GUF_OBJ_TYPE *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b)) +{ + GUF_ASSERT_RELEASE(arr); + GUF_ASSERT_RELEASE(n >= 0); + GUF_ASSERT_RELEASE(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING); + for (ptrdiff_t i = 1; i < n; ++i) { // Range [0, i) is sorted. + ptrdiff_t j = i; + while (j > 0 && guf_before(&arr[j], &arr[j - 1])) { + GUF_SWAP(GUF_OBJ_TYPE, arr[j], arr[j - 1]); + j = j - 1; + } + } + return arr; +} + +/* + Iterative bottom-up merge-sort: cf. https://en.wikipedia.org/wiki/Merge_sort#Bottom-up_implementation (last-retrieved: 2025-01-26) + - stable: yes + - time: O(n * log n) (worst, average, and best) + - space: always O(n) (for arr_tmp) +*/ +GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _merge_sort)(GUF_OBJ_TYPE *arr, GUF_OBJ_TYPE *arr_tmp, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b)) +{ + GUF_ASSERT_RELEASE(arr); + GUF_ASSERT_RELEASE(n >= 0); + GUF_ASSERT_RELEASE(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING); + + GUF_OBJ_TYPE *in = arr; + GUF_OBJ_TYPE *out = arr_tmp; + size_t arr_len = n; + for (size_t len = 1; len < arr_len; len = (len * 2 > len) ? 2 * len : SIZE_T_MAX) { // Subarray len 1, 2, 4, 8, ... + for (size_t i = 0; i < arr_len; i = ((i + 2 * len) > i) ? (i + 2 * len) : SIZE_T_MAX) { // For each pair of subarrays of length len: + const size_t left_begin = i; // left subarray: [left_begin, right_begin) + const size_t right_begin = GUF_MIN(i + len, arr_len), right_end = GUF_MIN(i + 2 * len, arr_len); // right subarray [right_begin, right_end) + size_t left_idx = left_begin, right_idx = right_begin; + for (size_t idx = left_begin; idx < right_end; ++idx) { // Merge the left and right subarrays into arr_tmp. + if (left_idx < right_begin && (right_idx >= right_end || guf_before_or_equal(&in[left_idx], &in[right_idx]))) { + out[idx] = in[left_idx++]; + } else { + GUF_ASSERT(right_idx < right_end); + out[idx] = in[right_idx++]; + } + } + } + GUF_SWAP(GUF_OBJ_TYPE*, in, out); // Avoid copying memory by switching pointers. + } + + if (in != arr) { + memcpy(arr, in, sizeof(GUF_OBJ_TYPE) * arr_len); + } + return arr; +} + +/* + Quicksort implementation with "Median-of-3 partition" (pp. 188-189) as described in CLRS (plus potential bugs introduced by me). + cf. Cormen; Leiserson; Riverst; Stein (2009). "II.7 Quicksort". In "Introduction to Algorithms" (3rd ed, pp. 170-190). MIT Press + - stable: no + - time: worst O(n^2), average O(n * log n), best O(n * log n) + - space: worst-case O(n * log n) (recursion uses stack space for the function parameters etc.) +*/ +static inline GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _qsort_range)(GUF_OBJ_TYPE *arr, ptrdiff_t first_idx, ptrdiff_t last_idx, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b)) +{ + GUF_ASSERT(arr); + GUF_ASSERT(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING); + + while (first_idx >= 0 && first_idx < last_idx) { + const ptrdiff_t mid_idx = first_idx + ((last_idx - first_idx) / 2); // Equivalent to (first_idx + last_idx) / 2, cf. https://research.google/blog/extra-extra-read-all-about-it-nearly-all-binary-searches-and-mergesorts-are-broken/ (last-retrieved 2025-01-24) + GUF_ASSERT(mid_idx >= 0 && mid_idx <= last_idx); + // 1.) Partition + ptrdiff_t pivot_idx = last_idx; + if (guf_before(&arr[mid_idx], &arr[first_idx])) { + GUF_SWAP(GUF_OBJ_TYPE, arr[first_idx], arr[mid_idx]); + } + if (guf_before(&arr[last_idx], &arr[first_idx])) { + GUF_SWAP(GUF_OBJ_TYPE, arr[first_idx], arr[last_idx]); + } + if (guf_before(&arr[mid_idx], &arr[last_idx])) { + GUF_SWAP(GUF_OBJ_TYPE, arr[mid_idx], arr[last_idx]); + } + ptrdiff_t i = first_idx - 1; // i: end idx of the subarray with elements <= pivot + for (ptrdiff_t j = first_idx; j < last_idx; ++j) { // j: end idx of the subarray with elements > pivot + if (guf_before_or_equal(&arr[j], &arr[pivot_idx])) { + ++i; + GUF_ASSERT(i >= 0 && i < last_idx); + GUF_SWAP(GUF_OBJ_TYPE, arr[i], arr[j]); + } + } + GUF_ASSERT(i + 1 >= 0 && i + 1 < last_idx); + GUF_SWAP(GUF_OBJ_TYPE, arr[i + 1], arr[last_idx]); // The pivot element is always <= itself (or >= for descending sorts). + pivot_idx = i + 1; + + // 2.) Sort the two partitions [first_idx, pivot_idx) and (pivot_idx, last_idx] recursively. + /* + "To make sure at most O(log n) space is used, recur first into the smaller side of the partition, + then use a tail call to recur into the other, or update the parameters to no longer include the now sorted smaller side, + and iterate to sort the larger side.", cf. https://en.wikipedia.org/wiki/Quicksort#Optimizations (last-retrieved 2025-01-25) + + (Solution to exercise "II.7.4c. Stack depth for quicksort". In "Introduction to Algorithms" (3rd ed, p. 188)) + */ + if (pivot_idx <= mid_idx) { // a.) Left subarray is smaller or equal than the right subarray -> recur into the smaller left subarray first. + GUFCAT(GUF_OBJ_TYPE_NAME, _qsort_range)(arr, first_idx, pivot_idx - 1, sort_opt, cmp); + first_idx = pivot_idx + 1; + } else { // b.) Right subarray is smaller than the left subarray -> recur into the smaller right subarray first. + GUFCAT(GUF_OBJ_TYPE_NAME, _qsort_range)(arr, pivot_idx + 1, last_idx, sort_opt, cmp); + last_idx = pivot_idx - 1; + } + } + return arr; +} + +GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _qsort)(GUF_OBJ_TYPE *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b)) +{ + GUF_ASSERT_RELEASE(arr); + GUF_ASSERT_RELEASE(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING); + GUF_ASSERT_RELEASE(n >= 0); + if (n <= 1) { + return arr; + } else if (n <= 4) { + return GUFCAT(GUF_OBJ_TYPE_NAME, _insertion_sort)(arr, n, sort_opt, cmp); + } else { + return GUFCAT(GUF_OBJ_TYPE_NAME, _qsort_range)(arr, 0, n - 1, sort_opt, cmp); + } +} + +#undef guf_before +#undef guf_before_or_equal +#endif + +#undef GUF_OBJ_TYPE +#undef GUF_OBJ_TYPE_NAME \ No newline at end of file diff --git a/src/guf_str.h b/src/guf_str.h index c7659ed..74da0a6 100644 --- a/src/guf_str.h +++ b/src/guf_str.h @@ -5,7 +5,11 @@ #include #include "guf_common.h" -// #include "guf_obj.h" +#include "guf_alloc.h" + +#define GUF_CNT_T char +#define GUF_CNT_NAME guf_dbuf_char +#include "guf_dbuf.h" #define GUF_STR_ABORT_ON_ALLOC_FAILURE 1 @@ -14,7 +18,8 @@ typedef enum guf_str_state { GUF_STR_STATE_INIT = 0, GUF_STR_STATE_SHORT = 1, - GUF_STR_STATE_ALLOC_ERR = 2 + GUF_STR_STATE_READONLY = 2, + GUF_STR_STATE_ALLOC_ERR = 4 } guf_str_state; typedef struct guf_str { @@ -23,6 +28,7 @@ typedef struct guf_str { struct heap { char *c_str; size_t len, capacity; // len and capacity do not include the null-terminator. + guf_dbuf_char chars; // TODO } heap; struct stack { // Short-string optimisation. #define GUF_STR_SSO_BUFSIZE (sizeof(struct heap) - sizeof(unsigned char)) @@ -53,6 +59,8 @@ guf_str *guf_str_init_from_cstr(guf_str *str, const char* c_str); guf_str *guf_str_init_empty_with_capacity(guf_str *str, size_t capacity); // guf_str_new functions return GUF_DICT_UNINITIALISED or GUF_STR_UNINITIALISED_FAILED_ALLOC on failure (can be checked with guf_str_alloc_success) guf_str guf_str_new(guf_str_view str_view); +guf_str guf_str_new_substr(guf_str_view str_view, ptrdiff_t pos, ptrdiff_t len); + guf_str guf_str_new_from_cstr(const char *c_str); guf_str guf_str_new_empty_with_capacity(size_t capacity); @@ -61,7 +69,7 @@ void guf_str_free(guf_str *str); // Modification: guf_str *guf_str_append(guf_str *str, guf_str_view to_append); -guf_str *guf_str_append_cstr(guf_str *str, const char *cstr_to_append); +guf_str *guf_str_append_cstr(guf_str *str, const char *cstr_to_append); // Not necessary guf_str *guf_str_substr(guf_str* str, size_t pos, size_t count); guf_str *guf_str_reserve(guf_str *str, size_t bufsize); @@ -71,7 +79,7 @@ char guf_str_pop_back(guf_str *str); char guf_str_pop_front(guf_str *str); // Copying and viewing: -guf_str guf_str_substr_cpy(guf_str_view str, size_t pos, size_t count); +guf_str guf_str_substr_cpy(guf_str_view str, size_t pos, size_t count); // not necessary guf_str_view guf_str_substr_view(guf_str_view str, size_t pos, size_t count); // Indexing: @@ -97,40 +105,5 @@ int guf_str_view_cmp(const void *str_view_a, const void *str_view_b); // For qso // UTF-8 operations. bool guf_str_char_is_ascii(char c); bool guf_str_is_ascii(const guf_str *str); - -// TODO: -// typedef struct guf_str_pool_elem { -// guf_str str; -// bool in_use; -// struct guf_str_pool_elem *next_free; -// } guf_str_pool_elem; - -// typedef struct guf_str_pool { -// guf_str_pool_elem *elems; -// size_t capacity, num_in_use; -// size_t min_str_bufsize; -// guf_str_pool_elem *first_free; -// } guf_str_pool; - -// void guf_str_pool_init(guf_str_pool *pool, size_t capacity, size_t str_initial_size) -// { -// if (capacity < 1) { -// capacity = 1; -// } -// pool->num_in_use = 0; -// pool->capacity = capacity; -// pool->elems = calloc(capacity, sizeof(guf_str_pool_elem)); -// pool->min_str_bufsize = str_initial_size; -// GUF_ASSERT_RELEASE(pool->elems); - -// pool->first_free = pool->elems + 0; -// for (size_t i = 0; i < pool->capacity; ++i) { -// pool->elems[i].in_use = false; -// pool->elems[i].str = guf_str_new_empty_with_capacity(pool->min_str_bufsize); -// pool->elems[i].next_free = i + 1 < pool->capacity ? pool->elems + i + 1 : NULL; -// } -// } - -// find_free and find_free_with_cap #endif \ No newline at end of file diff --git a/src/guf_test.c b/src/guf_test.c index eab4c10..c2e2465 100644 --- a/src/guf_test.c +++ b/src/guf_test.c @@ -4,77 +4,107 @@ #define GUF_IMPLEMENTATION #include "guf_common.h" +#undef GUF_IMPLEMENTATION #include "guf_cstr.h" #define GUF_CNT_NAME dbuf_int #define GUF_CNT_T int -#define GUF_CNT_T_OPS guf_int_ops -#include "guf_dbuf.h" - -#define GUF_CNT_NAME dbuf_char -#define GUF_CNT_T char -#define GUF_CNT_T_OPS guf_char_ops +#define GUF_CNT_T_IS_INTEGRAL_TYPE #include "guf_dbuf.h" #define GUF_CNT_NAME dbuf_float #define GUF_CNT_T float -#define GUF_CNT_T_OPS guf_float_ops +#define GUF_CNT_T_IS_INTEGRAL_TYPE #include "guf_dbuf.h" -#define GUF_CNT_NAME dbuf_const_cstr -#define GUF_CNT_T guf_const_cstr -#define GUF_CNT_T_OPS guf_const_cstr_ops -#include "guf_dbuf.h" +#define GUF_OBJ_TYPE float +#include "guf_sort.h" -#define GUF_CNT_NAME dbuf_heap_cstr #define GUF_CNT_T guf_heap_cstr -#define GUF_CNT_T_OPS guf_heap_cstr_ops +#define GUF_CNT_NAME dbuf_heap_cstr +#define GUF_CNT_T_COPY guf_heap_cstr_copy_construct +#define GUF_CNT_T_MOVE guf_heap_cstr_move_construct +#define GUF_CNT_T_FREE guf_heap_cstr_free +#define GUF_CNT_T_EQ guf_heap_cstr_eq #include "guf_dbuf.h" -// typedef struct guf_test { -// const char *name, *expected_output; -// char *output; -// void (*test_fn)(struct guf_test *test); -// uint64_t runtime_ms; -// bool passed; -// } guf_test; +#define GUF_CNT_T guf_const_cstr +#define GUF_CNT_NAME dbuf_const_cstr +#define GUF_CNT_T_EQ guf_const_cstr_eq +#include "guf_dbuf.h" int main(void) { bool success = true; - GUF_LIFETIME_BLOCK(dbuf_float floats = dbuf_float_new(), floats, dbuf_float_free, { - for (int i = 0; i < 10; ++i) { + GUF_LIFETIME_BLOCK(dbuf_float floats = dbuf_float_new(&guf_allocator_libc), floats, dbuf_float_free, { + for (int i = 0; i < 16; ++i) { dbuf_float_push_val(&floats, i % 2 ? i * 2.718f : i * -2.718f); } - GUF_FOREACH(&floats, dbuf_float, it) { - printf("float: %f\n", *it.cur); - } + // float *tmp = calloc(floats.size, sizeof(float)); + // float *res = float_merge_sort(floats.data, tmp, floats.size, GUF_SORT_DESCENDING, NULL); + // free(tmp); + // GUF_ASSERT_RELEASE(res == floats.data); - dbuf_float_sort(&floats, GUF_SORT_ASCENDING); - printf("Sorted.\n"); - GUF_FOREACH(&floats, dbuf_float, it) { - printf("float: %f\n", *it.cur); + // float_qsort(floats.data, floats.size, GUF_SORT_ASCENDING, NULL); + + float_insertion_sort(floats.data, floats.size, GUF_SORT_ASCENDING, NULL); + + GUF_CNT_FOREACH(&floats, dbuf_float, it) { + printf("float: %f\n", *it.ptr); } }) - dbuf_char str = dbuf_char_new(); - dbuf_char_push_val(&str, 'H'); - dbuf_char_push_val(&str, 'e'); - dbuf_char_push_val(&str, 'j'); - dbuf_char_push_val(&str, '\0'); - printf("str.data = %s\n", str.data); - dbuf_char_free(&str); + dbuf_heap_cstr strings = dbuf_heap_cstr_new(&guf_allocator_libc); + dbuf_heap_cstr_push_val_cpy(&strings, "Foo 1"); + dbuf_heap_cstr_push_val_cpy(&strings, "Bar 2"); + char *move_me = strdup("Baz 3"); + dbuf_heap_cstr_push(&strings, &move_me, GUF_CPY_MOVE); + GUF_ASSERT_RELEASE(move_me == NULL); - dbuf_int integers = dbuf_int_new(); + char *findme = "Baz 3"; + + dbuf_heap_cstr_iter fnd_it = dbuf_heap_cstr_find(&strings, dbuf_heap_cstr_begin(&strings), dbuf_heap_cstr_end(&strings), &findme); + if (fnd_it.ptr != dbuf_heap_cstr_end(&strings).ptr) { + guf_heap_cstr_free(fnd_it.ptr); + *fnd_it.ptr = strdup("Found!"); + } + + GUF_CNT_FOREACH(&strings, dbuf_heap_cstr, it) { + printf("%s\n", *it.ptr); + } + dbuf_heap_cstr_free(&strings); + + dbuf_const_cstr const_strings = dbuf_const_cstr_new(&guf_allocator_libc); + dbuf_const_cstr_push_val(&const_strings, "Const 1"); + dbuf_const_cstr_push_val(&const_strings, "Const 2"); + const char *foo = "Const 3"; + dbuf_const_cstr_push(&const_strings, &foo, GUF_CPY_VALUE); + + dbuf_const_cstr_iter found_it = dbuf_const_cstr_find(&const_strings, dbuf_const_cstr_begin(&const_strings), dbuf_const_cstr_end(&const_strings), &foo); + if (found_it.ptr != dbuf_const_cstr_end(&const_strings).ptr) { + *found_it.ptr = "Found!"; + } + + GUF_CNT_FOREACH(&const_strings, dbuf_const_cstr, it) { + printf("%s\n", *it.ptr); + } + dbuf_const_cstr_free(&const_strings); + + dbuf_int integers = dbuf_int_new(&guf_allocator_libc); dbuf_int_push_val(&integers, 420); printf("initial cap %td\n", integers.capacity); dbuf_int_push_val(&integers, 520); dbuf_int_push_val(&integers, 620); dbuf_int_push_val(&integers, 720); + dbuf_int_push_val(&integers, 820); - printf("%d\n", *dbuf_int_at(&integers, 4)); + guf_err err; + dbuf_int_try_at(&integers, 16, &err); + if (err) { + printf("%s %s\n", guf_err_type_str[err], GUF_ERR_MSG_EMPTY()); + } int i = 0; GUF_DBUF_FOREACH(integers, int, elem) { @@ -82,51 +112,29 @@ int main(void) ++i; } - GUF_FOREACH(&integers, dbuf_int, it) { - printf("it-elem: %d", *it.cur); - if (it.next(&it, 1).cur != it.end) { - printf(", it-next: %d", *it.next(&it, 1).cur); + GUF_CNT_FOREACH(&integers, dbuf_int, it) { + printf("it-elem: %d", *it.ptr); + if (dbuf_int_iter_next(&integers, it, 1).ptr != dbuf_int_end(&integers).ptr) { + printf(", it-next: %d", *dbuf_int_iter_next(&integers, it, 1).ptr); } - if (it.next(&it, -1).cur != it.end) { - printf(", it-prev: %d", *it.next(&it, -1).cur); + if (dbuf_int_iter_next(&integers, it, -1).ptr != dbuf_int_end(&integers).ptr) { + printf(", it-prev: %d", *dbuf_int_iter_next(&integers, it, -1).ptr); } printf("\n"); } - for (dbuf_int_iter it = dbuf_int_begin(&integers); it.cur != it.end; it = it.next(&it, 2)) { - printf("every other: %d\n", *it.cur); + for (dbuf_int_iter it = dbuf_int_begin(&integers); it.ptr != dbuf_int_end(&integers).ptr; it = dbuf_int_iter_next(&integers, it, 2)) { + printf("every other: %d\n", *it.ptr); } - dbuf_const_cstr strings = dbuf_const_cstr_new(); - dbuf_const_cstr_push_val(&strings, "First"); - printf("initial cap %td\n", strings.capacity); - - dbuf_const_cstr_push_val(&strings, "Second"); - guf_const_cstr foo = "Hello, World!"; - dbuf_const_cstr_push(&strings, &foo, GUF_CPY_VALUE); - - GUF_FOREACH(&strings, dbuf_const_cstr, it) { - printf("str: %s\n", *it.cur); + for (dbuf_int_iter it = dbuf_int_rbegin(&integers); it.ptr != dbuf_int_rend(&integers).ptr; it = dbuf_int_iter_next(&integers, it, 1)) { + printf("reverse: %d\n", *it.ptr); } - dbuf_heap_cstr mut_strings = dbuf_heap_cstr_new(); - dbuf_heap_cstr_push_val_cpy(&mut_strings, "1"); - dbuf_heap_cstr_push_val_cpy(&mut_strings, "2"); - - char *move_me_pls = calloc(128, sizeof(char)); - strcpy(move_me_pls, "3"); - dbuf_heap_cstr_push(&mut_strings, &move_me_pls, GUF_CPY_MOVE); - GUF_ASSERT_RELEASE(move_me_pls == NULL); - - dbuf_heap_cstr_sort(&mut_strings, GUF_SORT_DESCENDING); - - GUF_FOREACH(&mut_strings, dbuf_heap_cstr, it) { - printf("str: %s\n", *it.cur); + for (dbuf_int_iter it = dbuf_int_rbegin(&integers); it.ptr != dbuf_int_rend(&integers).ptr; it = dbuf_int_iter_next(&integers, it, 2)) { + printf("every other reverse: %d\n", *it.ptr); } - dbuf_heap_cstr_free(&mut_strings); - dbuf_const_cstr_free(&strings); dbuf_int_free(&integers); - return success ? EXIT_SUCCESS : EXIT_FAILURE; } \ No newline at end of file