From be2daf72fd6834a76661b013774ab3aee364f28c Mon Sep 17 00:00:00 2001 From: jun <83899451+zeichensystem@users.noreply.github.com> Date: Wed, 8 Jan 2025 23:32:55 +0100 Subject: [PATCH] Add better error handling --- CMakeLists.txt | 2 +- src/guf_assert.h | 125 +++++++++++- src/guf_common.h | 2 +- src/guf_common_utils.h | 4 +- src/guf_cstr.h | 1 + src/guf_dbuf.h | 438 ++++++++++++++++++++++++++++++++--------- src/guf_int.c | 65 ------ src/guf_int.h | 203 ++++++++++++------- src/guf_obj.h | 21 +- src/guf_test.c | 8 +- 10 files changed, 619 insertions(+), 250 deletions(-) delete mode 100644 src/guf_int.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 18818e1..d48b747 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.12) set(PROJECT_NAME libguf) project(${PROJECT_NAME}) -set(SOURCES src/guf_str.c src/guf_dict.c src/guf_int.c) +set(SOURCES src/guf_str.c src/guf_dict.c) add_library(${PROJECT_NAME} STATIC ${SOURCES}) # target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}}/lib/guf) diff --git a/src/guf_assert.h b/src/guf_assert.h index 65caca4..ec7a76e 100644 --- a/src/guf_assert.h +++ b/src/guf_assert.h @@ -3,13 +3,132 @@ #include #include #include +#include "guf_common_utils.h" + +typedef enum guf_err_type { + GUF_ERR_NONE = 0, + GUF_ERR_UNSPECIFIED, + GUF_ERR_ALLOC_FAIL, + GUF_ERR_NULL_PTR, + GUF_ERR_INT_OVERFLOW, + GUF_ERR_DIV_BY_ZERO, + GUF_ERR_DOMAIN, + GUF_ERR_IDX_RANGE, + GUF_ERR_INVALID_ARG, + GUF_ERR_RUNTIME, + GUF_ERR_LOGIC, + GUF_ERR_RELEASE_ASSERT, + 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); + +extern guf_panic_handler_fn guf_global_panic_handler; +extern const char *guf_err_type_str[GUF_ERR_TYPES_NUM]; + +static inline void guf_set_global_panic_handler(guf_panic_handler_fn panic_handler) +{ + guf_global_panic_handler = panic_handler; +} + +// 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() +#elif _MSC_VER + #define GUF_DEBUG_BREAK_CODE __debugbreak() +#else + #define GUF_DEBUG_BREAK_CODE +#endif + +#ifndef NDEBUG + #define GUF_ASSERT(COND) do { \ + if (!(COND)) { \ + fputs("libguf assertion '" #COND "' failed (" GUF_FILE_LINE_STR() ")\n", stderr); \ + GUF_DEBUG_BREAK_CODE; \ + abort(); \ + }\ + } while(0); +#else + #define GUF_ASSERT(COND) +#endif -#define GUF_ASSERT(COND) assert(COND) #define GUF_ASSERT_RELEASE(COND) do { \ if (!(COND)) { \ - fprintf(stderr, "libguf release assertion failed: " #COND ", file " __FILE__ ", line %d\n", __LINE__); \ - exit(EXIT_FAILURE); \ + 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);\ } \ } 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(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) +{ + GUF_ASSERT_RELEASE(err); + GUF_ASSERT_RELEASE(err->type >= 0 && err->type < GUF_ERR_TYPES_NUM); + return err->type != GUF_ERR_NONE; +} + +static inline void guf_panic_handler_default(const guf_err *err) +{ + if (!err) { + fputs("libguf panic!", stderr); + abort(); + } + + 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); + } + } + + fputc('\n', stderr); + abort(); +} + +#ifdef GUF_IMPLEMENTATION + guf_panic_handler_fn guf_global_panic_handler = guf_panic_handler_default; + + const char *guf_err_type_str[] = { + [GUF_ERR_NONE] = "Not an error", + [GUF_ERR_UNSPECIFIED] = "Error", + [GUF_ERR_ALLOC_FAIL] = "Allocation error", + [GUF_ERR_NULL_PTR] = "Null pointer dereference", + [GUF_ERR_DOMAIN] = "Domain error", + [GUF_ERR_INT_OVERFLOW] = "Integer overflow", + [GUF_ERR_DIV_BY_ZERO] = "Division by zero", + [GUF_ERR_IDX_RANGE] = "Index out of range", + [GUF_ERR_INVALID_ARG] = "Invalid argument", + [GUF_ERR_RUNTIME] = "Runtime error", + [GUF_ERR_LOGIC] = "Logic error", + [GUF_ERR_RELEASE_ASSERT] = "Release 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 d9f79a4..deec97c 100644 --- a/src/guf_common.h +++ b/src/guf_common.h @@ -7,9 +7,9 @@ #include #include "guf_common_utils.h" +#include "guf_assert.h" #include "guf_int.h" #include "guf_hash.h" -#include "guf_assert.h" typedef enum guf_obj_cpy_opt { GUF_CPY_VALUE = 0, diff --git a/src/guf_common_utils.h b/src/guf_common_utils.h index 38dc18d..a6fd456 100644 --- a/src/guf_common_utils.h +++ b/src/guf_common_utils.h @@ -1,12 +1,14 @@ #ifndef GUF_COMMON_UTILS_H #define GUF_COMMON_UTILS_H -#include "guf_assert.h" #include // 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) +#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]))) diff --git a/src/guf_cstr.h b/src/guf_cstr.h index aaac741..065ad4b 100644 --- a/src/guf_cstr.h +++ b/src/guf_cstr.h @@ -84,6 +84,7 @@ 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, diff --git a/src/guf_dbuf.h b/src/guf_dbuf.h index 1300faf..cd62cb4 100644 --- a/src/guf_dbuf.h +++ b/src/guf_dbuf.h @@ -36,93 +36,131 @@ #define GUF_CNT_NAME GUF_CAT(dbuf_, GUF_CNT_T) #endif -// TODO: does not work with custom obj_ops_typename -// #define GUF_CNT_T_OPS_CPY GUFCAT(GUFCAT(GUF_CNT_T, _ops_type), _cpy) - typedef struct GUF_CNT_NAME { GUF_CNT_T *data; ptrdiff_t size, capacity; } GUF_CNT_NAME; -static inline bool GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(const GUF_CNT_NAME* dbuf) +static inline bool GUFCAT(GUF_CNT_NAME, _valid)(const GUF_CNT_NAME* dbuf) { - return dbuf && dbuf->data && dbuf->capacity > 0 && dbuf->size > 0 && dbuf->size <= dbuf->capacity; + 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; } -static inline bool GUFCAT(GUF_CNT_NAME, _valid_and_maybe_empty)(const GUF_CNT_NAME* dbuf) +bool GUFCAT(GUF_CNT_NAME, _try_reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity, guf_err *err) { - GUF_ASSERT_RELEASE((!dbuf->data && !dbuf->capacity) || (dbuf->data && dbuf->capacity)); - return dbuf && dbuf->capacity >= 0 && dbuf->size >= 0 && dbuf->size <= dbuf->capacity; -} - -bool GUFCAT(GUF_CNT_NAME, _reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity) -{ - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_maybe_empty)(dbuf)); + 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; } if (!dbuf->data) { GUF_CNT_T *data = calloc(min_capacity, sizeof(GUF_CNT_T)); - GUF_ASSERT(data); if (!data) { + err->type = GUF_ERR_ALLOC_FAIL; + err->msg = GUF_ERR_MSG("in function dbuf_reserve: Failed to allocate buffer"); return false; } dbuf->data = data; } else { GUF_CNT_T *data = realloc(dbuf->data, guf_safe_size_calc(min_capacity, sizeof(GUF_CNT_T))); - GUF_ASSERT(data); if (!data) { + err->type = GUF_ERR_ALLOC_FAIL; + err->msg = GUF_ERR_MSG("in function dbuf_reserve: Failed to re-allocate buffer"); return false; } dbuf->data = data; } dbuf->capacity = min_capacity; + err->type = GUF_ERR_NONE; + err->msg = NULL; return true; } -static inline bool GUFCAT(GUF_CNT_NAME, _init)(GUF_CNT_NAME *dbuf, ptrdiff_t start_cap) +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); + } +} + +static inline bool GUFCAT(GUF_CNT_NAME, _try_init)(GUF_CNT_NAME *dbuf, ptrdiff_t start_cap, guf_err *err) { GUF_ASSERT_RELEASE(dbuf); + GUF_ASSERT_RELEASE(err); GUF_ASSERT_RELEASE(start_cap >= 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; + } + dbuf->size = dbuf->capacity = 0; if (start_cap == 0) { dbuf->data = NULL; + *err = GUF_SUCCESS; return true; + } else { + return GUFCAT(GUF_CNT_NAME, _try_reserve)(dbuf, start_cap, err); } - bool success = GUFCAT(GUF_CNT_NAME, _reserve)(dbuf, start_cap); - if (success) { - dbuf->capacity = start_cap; - } - GUF_ASSERT(success); - return success; } -static inline GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _new_with_capacity)(ptrdiff_t start_cap) +static inline void GUFCAT(GUF_CNT_NAME, _init)(GUF_CNT_NAME *dbuf, ptrdiff_t start_cap) { - GUF_ASSERT_RELEASE(start_cap >= 0); - GUF_CNT_NAME dbuf = {0}; - bool success = GUFCAT(GUF_CNT_NAME, _init)(&dbuf, start_cap); - GUF_ASSERT_RELEASE(success); - return dbuf; + guf_err err; + GUFCAT(GUF_CNT_NAME, _try_init)(dbuf, start_cap, &err); + if (guf_is_err(&err)) { + guf_panic(&err); + } } GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _new)(void) { GUF_CNT_NAME dbuf = {0}; - bool success = GUFCAT(GUF_CNT_NAME, _init)(&dbuf, 0); - GUF_ASSERT_RELEASE(success); + GUFCAT(GUF_CNT_NAME, _init)(&dbuf, 0); + GUF_ASSERT(dbuf.size == 0 && dbuf.capacity == 0); return dbuf; } -static inline ptrdiff_t GUFCAT(GUF_CNT_NAME, _next_capacity)(ptrdiff_t old_cap) +GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _try_new_with_capacity)(guf_err *err, ptrdiff_t capacity) +{ + GUF_ASSERT_RELEASE(err); + GUF_CNT_NAME dbuf = {0}; + GUFCAT(GUF_CNT_NAME, _try_init)(&dbuf, capacity, err); + if (guf_is_err(err)) { + return (GUF_CNT_NAME){0}; + } + *err = GUF_SUCCESS; + return dbuf; +} + +GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _new_with_capacity)(ptrdiff_t capacity) +{ + 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; +} + +static inline ptrdiff_t GUFCAT(GUF_CNT_NAME, _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; @@ -131,72 +169,135 @@ static inline ptrdiff_t GUFCAT(GUF_CNT_NAME, _next_capacity)(ptrdiff_t old_cap) } else { new_cap = (size_t)old_cap * 3ull / 2ull; } - GUF_ASSERT_RELEASE(new_cap > (size_t)old_cap); // Fail on overflow. - GUF_ASSERT_RELEASE(new_cap <= PTRDIFF_MAX); - return new_cap; + + 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"); + return -1; + } else { + err->type = GUF_ERR_NONE; + err->msg = NULL; + return new_cap; + } } -static inline bool GUFCAT(GUF_CNT_NAME, _grow_if_full)(GUF_CNT_NAME *dbuf) +static inline bool GUFCAT(GUF_CNT_NAME, _grow_if_full)(GUF_CNT_NAME *dbuf, guf_err *err) { GUF_ASSERT_RELEASE(dbuf->capacity >= 0 && dbuf->size >= 0); + GUF_ASSERT_RELEASE(err); + if (dbuf->size == dbuf->capacity) { - bool success = GUFCAT(GUF_CNT_NAME, _reserve)(dbuf, GUFCAT(GUF_CNT_NAME, _next_capacity)(dbuf->capacity)); - if (!success) { + ptrdiff_t next_cap = GUFCAT(GUF_CNT_NAME, _next_capacity)(dbuf->capacity, err); + if (guf_is_err(err)) { return false; - } + } + GUF_ASSERT(next_cap > 0); + GUFCAT(GUF_CNT_NAME, _try_reserve)(dbuf, next_cap, err); + if (guf_is_err(err)) { + return false; + } } + GUF_ASSERT_RELEASE(dbuf->size < dbuf->capacity); + err->type = GUF_ERR_NONE; + err->msg = NULL; return true; } -GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _push)(GUF_CNT_NAME *dbuf, GUF_CNT_T *elem, guf_obj_cpy_opt cpy_opt) +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_RELEASE(GUFCAT(GUF_CNT_NAME,_valid_and_maybe_empty)(dbuf)); - GUF_ASSERT_RELEASE(!(cpy_opt == GUF_CPY_VALUE && GUF_CNT_T_OPS.free)); // Don't allow copy by value if there's a free operation. + GUF_ASSERT(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + GUF_ASSERT_RELEASE(err); + + 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)"); + } + return NULL; + } - bool success = GUFCAT(GUF_CNT_NAME, _grow_if_full)(dbuf); - GUF_ASSERT(success); - if (!success) { + GUFCAT(GUF_CNT_NAME, _grow_if_full)(dbuf, err); + if (guf_is_err(err)) { return NULL; } 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); - GUF_ASSERT(cpy_success); - if (cpy_success) { - return dst; - } else { + if (!cpy_success) { + err->type = GUF_ERR_ALLOC_FAIL; + err->msg = GUF_ERR_MSG("in function dbuf_try_push: Failed to copy elem"); return NULL; + } else { + err->type = GUF_ERR_NONE; + err->msg = NULL; + GUF_ASSERT(dst); + return dst; } } + +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; +} + +GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_push_val)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem, guf_err *err) +{ + return GUFCAT(GUF_CNT_NAME, _try_push)(dbuf, &elem, GUF_CPY_VALUE, err); +} + GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _push_val)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem) { - GUF_ASSERT_RELEASE(!GUF_CNT_T_OPS.free); // Don't allow copy by value if there's a free operation. return GUFCAT(GUF_CNT_NAME, _push)(dbuf, &elem, GUF_CPY_VALUE); } +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); +} + 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); } -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_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(GUFCAT(GUF_CNT_NAME,_valid_and_maybe_empty)(dbuf)); - GUF_ASSERT_RELEASE(idx >= 0 && idx <= dbuf->size); - GUF_ASSERT_RELEASE(!(cpy_opt == GUF_CPY_VALUE && GUF_CNT_T_OPS.free)); // Don't allow copy by value if there's a free operation. + 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, _push)(dbuf, elem, cpy_opt); + return GUFCAT(GUF_CNT_NAME, _try_push)(dbuf, elem, cpy_opt, err); } + GUF_ASSERT(idx < dbuf->size); - GUF_ASSERT_RELEASE(idx < dbuf->size); - - bool success = GUFCAT(GUF_CNT_NAME, _grow_if_full)(dbuf); - GUF_ASSERT(success); - if (!success) { + GUFCAT(GUF_CNT_NAME, _grow_if_full)(dbuf, err); + if (guf_is_err(err)) { return NULL; } @@ -209,20 +310,50 @@ GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _insert)(GUF_CNT_NAME *dbuf, GUF_CNT_T *elem, pt 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) - GUF_ASSERT(cpy_success); - if (cpy_success) { - return dst; - } else { + 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; } -void GUFCAT(GUF_CNT_NAME, _erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx) +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_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(dbuf)); - GUF_ASSERT_RELEASE(idx >= 0); - GUF_ASSERT_RELEASE(idx < dbuf->size); + 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"); + return false; + } + + if (idx < 0 || idx >= dbuf->size) { + err->type = GUF_ERR_IDX_RANGE; + err->msg = GUF_ERR_MSG("in function dbuf_try_erase"); + return false; + } if (GUF_CNT_T_OPS.free) { GUF_CNT_T_OPS.free(dbuf->data + idx); @@ -233,54 +364,179 @@ void GUFCAT(GUF_CNT_NAME, _erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx) GUF_CNT_T *src = dst + 1; *dst = *src; } + + *err = GUF_SUCCESS; + return true; } -void GUFCAT(GUF_CNT_NAME, pop)(GUF_CNT_NAME *dbuf) +void GUFCAT(GUF_CNT_NAME, _erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(dbuf)); + guf_err err; + GUFCAT(GUF_CNT_NAME, _try_erase)(dbuf, idx, &err); + if (guf_is_err(&err)) { + guf_panic(&err); + } + +} + +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"); + return false; + } + GUF_CNT_T *popped = dbuf->data + --dbuf->size; if (GUF_CNT_T_OPS.free) { GUF_CNT_T_OPS.free(popped); } + + *err = GUF_SUCCESS; + return true; } -GUF_CNT_T GUFCAT(GUF_CNT_NAME, pop_cpy)(GUF_CNT_NAME *dbuf, guf_obj_cpy_opt cpy_opt) +void GUFCAT(GUF_CNT_NAME, _pop)(GUF_CNT_NAME *dbuf) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(dbuf)); - GUF_CNT_T *popped = dbuf->data + --dbuf->size; + guf_err err; + GUFCAT(GUF_CNT_NAME, _try_pop)(dbuf, &err); + if (guf_is_err(&err)) { + guf_panic(&err); + } +} + +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; + return dummy; + } + + GUF_CNT_T *popped = dbuf->data + (dbuf->size - 1); GUF_CNT_T popped_val; - bool success = false; - GUF_OBJ_CPY(GUF_CNT_T, GUF_CNT_T_OPS, &popped_val, popped, cpy_opt, &success); - GUF_ASSERT_RELEASE(success); + + 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; + } else { + dbuf->size -= 1; + } if (cpy_opt == GUF_CPY_DEEP && GUF_CNT_T_OPS.free) { GUF_CNT_T_OPS.free(popped); } + + *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; +} + + +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"); + return NULL; + } + if (idx < 0 || idx >= dbuf->size) { + err->type = GUF_ERR_IDX_RANGE; + err->msg = GUF_ERR_MSG("in function dbuf_try_at"); + return NULL; + } + GUF_ASSERT(dbuf->data); + *err = GUF_SUCCESS; + return dbuf->data + idx; +} + GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(dbuf)); - GUF_ASSERT_RELEASE(idx >= 0 && idx < dbuf->size) - return dbuf->data + 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; +} + + +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"); + return NULL; + } + return dbuf->data + 0; } GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _front)(GUF_CNT_NAME *dbuf) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(dbuf)); - return dbuf->data + 0; + 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; } -GUF_CNT_T *GUFCAT(GUF_CNT_NAME, back)(GUF_CNT_NAME *dbuf) + +GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_back)(GUF_CNT_NAME *dbuf, guf_err *err) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_not_empty)(dbuf)); + 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"); + return NULL; + } + *err = GUF_SUCCESS; return dbuf->data + (dbuf->size - 1); } -bool GUFCAT(GUF_CNT_NAME, _shrink_to_fit)(GUF_CNT_NAME *dbuf) +GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _back)(GUF_CNT_NAME *dbuf) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_maybe_empty)(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; +} + +bool GUFCAT(GUF_CNT_NAME, _try_shrink_to_fit)(GUF_CNT_NAME *dbuf) +{ + GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); const ptrdiff_t new_capacity = dbuf->size; if (new_capacity == dbuf->capacity) { return true; @@ -300,7 +556,7 @@ bool GUFCAT(GUF_CNT_NAME, _shrink_to_fit)(GUF_CNT_NAME *dbuf) void GUFCAT(GUF_CNT_NAME, _free)(GUF_CNT_NAME *dbuf) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_maybe_empty)(dbuf)); + GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->capacity == 0) { GUF_ASSERT_RELEASE(!dbuf->data); GUF_ASSERT_RELEASE(dbuf->size == 0); @@ -319,7 +575,7 @@ void GUFCAT(GUF_CNT_NAME, _free)(GUF_CNT_NAME *dbuf) void GUFCAT(GUF_CNT_NAME, _sort)(GUF_CNT_NAME *dbuf, guf_sort_opt sort_opt) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid_and_maybe_empty)(dbuf)); + GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->size == 0) { return; @@ -358,9 +614,9 @@ static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _iter_next)(const } else if (next_ptr < (intptr_t)it->begin) { // TODO: different end for begin next_it.cur = it->end; } else { - GUF_ASSERT_RELEASE(next_ptr >= (intptr_t)it->begin); - GUF_ASSERT_RELEASE(next_ptr <= (intptr_t)it->end); - GUF_ASSERT_RELEASE(it->cur + step >= it->begin && it->cur + step <= it->end); + 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; @@ -368,7 +624,7 @@ static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _iter_next)(const static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _begin)(const GUF_CNT_NAME* dbuf) { - GUFCAT(GUF_CNT_NAME, _valid_and_maybe_empty)(dbuf); + GUFCAT(GUF_CNT_NAME, _valid)(dbuf); return (GUFCAT(GUF_CNT_NAME, _iter)) { .cur = dbuf->data, .begin = dbuf->data, @@ -379,7 +635,7 @@ static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _begin)(const GUF static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _end)(const GUF_CNT_NAME* dbuf) { - GUFCAT(GUF_CNT_NAME, _valid_and_maybe_empty)(dbuf); + GUFCAT(GUF_CNT_NAME, _valid)(dbuf); return (GUFCAT(GUF_CNT_NAME, _iter)) { .cur = dbuf->data ? dbuf->data + dbuf->size : NULL, .begin = dbuf->data, diff --git a/src/guf_int.c b/src/guf_int.c deleted file mode 100644 index 8e442ef..0000000 --- a/src/guf_int.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "guf_int.h" - -#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), \ - .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) \ No newline at end of file diff --git a/src/guf_int.h b/src/guf_int.h index 9574a38..0056a96 100644 --- a/src/guf_int.h +++ b/src/guf_int.h @@ -1,6 +1,80 @@ #ifndef GUF_INT_H #define GUF_INT_H -#include "guf_common.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(int, int) +GUF_DEFINE_MIN_MAX_CLAMP(short, short) +GUF_DEFINE_MIN_MAX_CLAMP(long, long) +GUF_DEFINE_MIN_MAX_CLAMP(long long, long_long) +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(unsigned, unsigned) +GUF_DEFINE_MIN_MAX_CLAMP(unsigned short, unsigned_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) + +#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 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;} +static inline int16_t guf_abs_i16(int16_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT16_MIN); return -x;} +static inline int32_t guf_abs_i32(int32_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT32_MIN); 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: @@ -122,81 +196,78 @@ 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; +extern const guf_double_ops_type guf_double_ops; -// #define GUF_ABS(X) ((X) >= 0 ? (X) : -(X)) -// #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)) +#ifdef GUF_IMPLEMENTATION -#define GUF_DEFINE_MIN_MAX_CLAMP(int_type, int_type_name)\ - static inline int_type GUFCAT(int_type_name, _min)(int_type a, int_type b) {return a >= b ? a : b;}\ - static inline int_type GUFCAT(int_type_name, _max)(int_type a, int_type b) {return a >= b ? a : b;}\ - static inline int_type GUFCAT(int_type_name, _clamp)(int_type x, int_type min, int_type max) {return GUFCAT(int_type_name, _max)(GUFCAT(int_type_name, _min)(x, max), min);} +#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) -GUF_DEFINE_MIN_MAX_CLAMP(int, guf_int) -GUF_DEFINE_MIN_MAX_CLAMP(short, guf_short) -GUF_DEFINE_MIN_MAX_CLAMP(long, guf_long) -GUF_DEFINE_MIN_MAX_CLAMP(long long, guf_long_long) -GUF_DEFINE_MIN_MAX_CLAMP(int8_t, guf_int8_t) -GUF_DEFINE_MIN_MAX_CLAMP(int16_t, guf_int16_t) -GUF_DEFINE_MIN_MAX_CLAMP(int32_t, guf_int32_t) -GUF_DEFINE_MIN_MAX_CLAMP(int64_t, guf_int64_t) +#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_MIN_MAX_CLAMP(unsigned, guf_unsigned) -GUF_DEFINE_MIN_MAX_CLAMP(unsigned short, guf_unsigned_short) -GUF_DEFINE_MIN_MAX_CLAMP(unsigned long, guf_unsigned_long) -GUF_DEFINE_MIN_MAX_CLAMP(unsigned long long, guf_unsigned_long_long) -GUF_DEFINE_MIN_MAX_CLAMP(uint8_t, guf_uint8_t) -GUF_DEFINE_MIN_MAX_CLAMP(uint16_t, guf_uint16_t) -GUF_DEFINE_MIN_MAX_CLAMP(uint32_t, guf_uint32_t) -GUF_DEFINE_MIN_MAX_CLAMP(uint64_t, guf_uint64_t) +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_MIN_MAX_CLAMP(char, guf_char) -GUF_DEFINE_MIN_MAX_CLAMP(unsigned char, guf_unsigned_char) +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) -#undef GUF_DEFINE_MIN_MAX_CLAMP +GUF_DEFINE_INTEGER_CMP(char, guf_char_cmp) +GUF_DEFINE_INTEGER_CMP(unsigned char, guf_unsigned_char_cmp) -static inline int guf_int(int x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT_MIN ); return -x;} -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;} -static inline int16_t guf_abs_i16(int16_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT16_MIN); return -x;} -static inline int32_t guf_abs_i32(int32_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT32_MIN); 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;} +GUF_DEFINE_INTEGER_CMP(float, guf_float_cmp) +GUF_DEFINE_INTEGER_CMP(double, guf_double_cmp) -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); -} +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) -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; -} +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) -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; -} +GUF_DEFINE_INTEGER_OPS(guf_char) +GUF_DEFINE_INTEGER_OPS(guf_unsigned_char) -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; -} +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 f40e620..2a0bbf3 100644 --- a/src/guf_obj.h +++ b/src/guf_obj.h @@ -49,7 +49,7 @@ // #endif typedef struct GUF_OBJ_OPS_TYPENAME { - // GUF_OBJ_TYPE *(*init_from)(GUF_OBJ_TYPE *dst, GUF_OBJ_TYPE *src, guf_obj_cpy_opt cpy_opt); + 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); @@ -59,24 +59,5 @@ typedef struct GUF_OBJ_OPS_TYPENAME { int (*cmp_void_inv)(const void *a, const void *b); } GUF_OBJ_OPS_TYPENAME; -// static inline GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_OPS_TYPENAME, _cpy) (GUF_OBJ_TYPE *dst, GUF_OBJ_TYPE *src, const GUF_OBJ_OPS_TYPENAME *ops, guf_obj_cpy_opt cpy_opt) -// { -// GUF_ASSERT_RELEASE(dst); -// GUF_ASSERT_RELEASE(src); -// GUF_ASSERT_RELEASE(ops); -// if (cpy_opt == GUF_CPY_VALUE) { -// dst = memcpy(dst, src, sizeof(GUF_OBJ_TYPE)); -// GUF_ASSERT_RELEASE(dst); // Should never fail. -// } else if (cpy_opt == GUF_CPY_DEEP) { -// GUF_ASSERT_RELEASE(ops->copy_init); -// dst = ops->copy_init(dst, src); -// } else if (cpy_opt == GUF_CPY_MOVE) { -// GUF_ASSERT_RELEASE(ops->move_init); -// dst = ops->move_init(dst, src); -// } -// GUF_ASSERT(dst); -// return dst; -// } - #undef GUF_OBJ_TYPE #undef GUF_OBJ_OPS_TYPENAME \ No newline at end of file diff --git a/src/guf_test.c b/src/guf_test.c index 633c952..eab4c10 100644 --- a/src/guf_test.c +++ b/src/guf_test.c @@ -1,6 +1,8 @@ #include #include #include + +#define GUF_IMPLEMENTATION #include "guf_common.h" #include "guf_cstr.h" @@ -42,7 +44,7 @@ int main(void) bool success = true; GUF_LIFETIME_BLOCK(dbuf_float floats = dbuf_float_new(), floats, dbuf_float_free, { - for (int i = 0; i < 16; ++i) { + for (int i = 0; i < 10; ++i) { dbuf_float_push_val(&floats, i % 2 ? i * 2.718f : i * -2.718f); } @@ -50,7 +52,7 @@ int main(void) printf("float: %f\n", *it.cur); } - dbuf_float_sort(&floats, GUF_SORT_DESCENDING); + dbuf_float_sort(&floats, GUF_SORT_ASCENDING); printf("Sorted.\n"); GUF_FOREACH(&floats, dbuf_float, it) { printf("float: %f\n", *it.cur); @@ -72,6 +74,8 @@ int main(void) dbuf_int_push_val(&integers, 620); dbuf_int_push_val(&integers, 720); + printf("%d\n", *dbuf_int_at(&integers, 4)); + int i = 0; GUF_DBUF_FOREACH(integers, int, elem) { printf("elem %d: %d\n", i, *elem);