diff --git a/CMakeLists.txt b/CMakeLists.txt index d48b747..324aece 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,50 +2,56 @@ cmake_minimum_required(VERSION 3.12) set(PROJECT_NAME libguf) project(${PROJECT_NAME}) -set(SOURCES src/guf_str.c src/guf_dict.c) +set(SOURCES src/guf_str.c) add_library(${PROJECT_NAME} STATIC ${SOURCES}) # target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}}/lib/guf) # target_include_directories(${PROJECT_NAME} PRIVATE src) -add_executable(libguf_test ${SOURCES} src/guf_test.c) -target_include_directories(libguf_test PRIVATE src) - set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) -set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD 17) + +if (NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) +endif () + +add_executable(libguf_test ${SOURCES} src/guf_test.c) +target_include_directories(libguf_test PRIVATE src) if (TARGET libguf_test) message("-- Configure libguf_test...") - set(CMAKE_DEBUG_POSTFIX _dbg) - set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) - - if (APPLE OR UNIX OR LINUX) - set(WARNING_FLAGS_C -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wno-newline-eof -Wno-unused-function -Wno-unused-parameter) - endif () - - set_target_properties(libguf_test PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) - - if (APPLE OR UNIX OR LINUX) - set(DBG_FLAGS -fsanitize=undefined,address -g3 -glldb -Og) + if (NOT DEFINED MSVC) + set(WARNING_FLAGS_C -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wconversion -Wno-sign-conversion -Wdouble-promotion -Wno-unused-function) else () - set(DBG_FLAGS /fsanitize=address) - endif() - - target_compile_options(libguf_test PRIVATE ${WARNING_FLAGS_C} $<$: ${DBG_FLAGS}>) # Note: no higher optimisations at all for debugger to work... - target_link_options(libguf_test PRIVATE ${WARNING_FLAGS_C} $<$: ${DBG_FLAGS}> ) + set(WARNING_FLAGS_CXX /W4) + endif () include(CheckIPOSupported) check_ipo_supported(RESULT ipo_available) if (ipo_available AND (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")) message(STATUS "LTO enabled") - set_target_properties(${PROJECT_NAME} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) + set_target_properties(${PROJECT_NAME} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE) else() message(STATUS "LTO disabled") endif() - message("-- Configured libguf_test") + if (NOT DEFINED CMAKE_DEBUG_POSTFIX) + set(CMAKE_DEBUG_POSTFIX _dbg) + endif () + set_target_properties(libguf_test PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX}) + + if (NOT DEFINED MSVC) + set(DBG_FLAGS -fsanitize=undefined,address -g3 -glldb -Og) + else () + set(DBG_FLAGS /fsanitize=address) + endif() + + target_compile_options(libguf_test PRIVATE ${WARNING_FLAGS_C} $<$: ${DBG_FLAGS}>) + target_link_options(libguf_test PRIVATE ${WARNING_FLAGS_C} $<$: ${DBG_FLAGS}> ) + + message(STATUS "Configured libguf_test") endif() diff --git a/src/guf_alloc.h b/src/guf_alloc.h index bbec982..5ba327c 100644 --- a/src/guf_alloc.h +++ b/src/guf_alloc.h @@ -1,10 +1,9 @@ #ifndef GUF_ALLOC_H #define GUF_ALLOC_H -#include -#include #include "guf_common.h" +#include "guf_assert.h" -// A Custom allocator interface as described in https://nullprogram.com/blog/2023/12/17/ (last-retrieved 2025-01-25) +// 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); @@ -12,80 +11,34 @@ typedef struct guf_allocator { void *ctx; } guf_allocator; -// register_alloc_type(const char *typestr) (starting from 1) -// lookup_alloc_typename(ptrdiff_t type_id) +typedef enum guf_alloc_fn_type { + GUF_ALLOC_FN_TYPE_ALLOC, + GUF_ALLOC_FN_TYPE_REALLOC, + GUF_ALLOC_FN_TYPE_FREE, +} guf_alloc_fn_type; -// 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) +static inline bool guf_size_t_mul_is_overflow(size_t a, size_t b) { - 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); - } + const size_t c = a * b; + return a != 0 && ((c / a) != b); } -static inline void *guf_realloc_libc(void *ptr, ptrdiff_t old_size, ptrdiff_t new_size, void *ctx) +static inline bool guf_size_calc_safe(ptrdiff_t count, ptrdiff_t sizeof_elem, ptrdiff_t *result) { - 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; + if (count < 0 || sizeof_elem <= 0) { + return false; } + if (guf_size_t_mul_is_overflow((size_t)count, (size_t)sizeof_elem)) { + return false; + } + const size_t size = (size_t)count * (size_t)sizeof_elem; + GUF_ASSERT(size != 0); - 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 + const bool is_safe = size <= PTRDIFF_MAX; + if (result) { + *result = is_safe ? (ptrdiff_t)size : -1; } - return new_ptr; + return is_safe; } -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 +#endif diff --git a/src/guf_alloc_libc.h b/src/guf_alloc_libc.h new file mode 100644 index 0000000..8afc555 --- /dev/null +++ b/src/guf_alloc_libc.h @@ -0,0 +1,59 @@ +#ifndef GUF_ALLOC_LIBC_H +#define GUF_ALLOC_LIBC_H +#include +#include "guf_alloc.h" + +typedef struct guf_libc_alloc_ctx { + bool zero_init; + int alloc_type_id, thread_id; +} guf_libc_alloc_ctx; + +static inline void *guf_libc_alloc(ptrdiff_t size, void *ctx) +{ + GUF_ASSERT_RELEASE(size >= 0); + guf_libc_alloc_ctx *alloc_ctx = (guf_libc_alloc_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_libc_realloc(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); + if (old_size == new_size) { + return ptr; + } + guf_libc_alloc_ctx *alloc_ctx = (guf_libc_alloc_ctx*)ctx; + + 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_libc_free(void *ptr, ptrdiff_t size, void *ctx) +{ + (void)ctx; + (void)size; + free(ptr); +} + +static guf_allocator guf_allocator_libc = { + .alloc = guf_libc_alloc, + .realloc = guf_libc_realloc, + .free = guf_libc_free, + .ctx = NULL +}; + +#endif diff --git a/src/guf_alloc_tracker.c b/src/guf_alloc_tracker.c new file mode 100644 index 0000000..2cc6730 --- /dev/null +++ b/src/guf_alloc_tracker.c @@ -0,0 +1,13 @@ +#include "guf_common.h" +#include "guf_alloc.h" + +typedef struct guf_alloc_info { + ptrdiff_t allocated_bytes, freed_bytes; + size_t alloc_count, realloc_count, free_count; +} guf_alloc_info; + +// void guf_alloc_track(int type_id, int thread_id, ptrdiff_t size) +// { +// static guf_alloc_info guf_alloc_info[GUF_ALLOC_THREAD_ID_MAX][GUF_ALLOC_TYPE_ID_MAX]; + +// } diff --git a/src/guf_assert.h b/src/guf_assert.h index ed59f71..8f58113 100644 --- a/src/guf_assert.h +++ b/src/guf_assert.h @@ -1,9 +1,8 @@ #ifndef GUF_ASSERT_H #define GUF_ASSERT_H -#include #include #include -#include "guf_common_utils.h" +#include "guf_common.h" typedef enum guf_err { GUF_ERR_NONE = 0, @@ -21,28 +20,29 @@ typedef enum guf_err { GUF_ERR_TYPES_NUM } guf_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]; -static inline void guf_set_global_panic_handler(guf_panic_handler_fn panic_handler) -{ - guf_global_panic_handler = panic_handler; -} +extern void guf_set_global_panic_handler(guf_panic_handler_fn panic_handler); +extern void guf_panic_handler_default(guf_err err, const char *msg); +extern const char *guf_err_to_str(guf_err err); #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() ")" +extern void guf_panic(guf_err err, const char *msg); + +#define GUF_PANIC(err) guf_panic(err, "(" 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() #elif _MSC_VER #define GUF_DEBUG_BREAK_CODE __debugbreak() #else - #define GUF_DEBUG_BREAK_CODE + #define GUF_DEBUG_BREAK_CODE abort() #endif #ifndef NDEBUG @@ -63,48 +63,6 @@ static inline void guf_set_global_panic_handler(guf_panic_handler_fn panic_handl } \ } while (0); -#define GUF_PANIC(err) do {guf_global_panic_handler(err, "(" GUF_FILE_LINE_STR() ")")} while(0); - -static inline void guf_panic_handler_default(guf_err err, const char *msg) -{ - 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(guf_err err, const char *msg) -{ - 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... -} - static inline void guf_err_set_or_panic(guf_err *err, guf_err err_val, const char *panic_msg) { if (!err) { @@ -120,24 +78,73 @@ static inline void guf_err_set_if_not_null(guf_err *err, guf_err err_val) *err = err_val; } } - -#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_ASSERT_FAIL] = "Assertion failed" - }; #endif -#endif \ No newline at end of file +#ifdef GUF_INIT +static 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_ASSERT_FAIL] = "Assertion failed" +}; + +extern void guf_panic(guf_err err, const char *msg) +{ + 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... +} + +extern void guf_set_global_panic_handler(guf_panic_handler_fn panic_handler) +{ + guf_global_panic_handler = panic_handler; +} + +extern const char *guf_err_to_str(guf_err err) +{ + GUF_ASSERT(GUF_STATIC_BUF_SIZE(guf_err_type_str) == GUF_ERR_TYPES_NUM); + + if (err < 0 || err >= GUF_ERR_TYPES_NUM) { + return "Invalid error code"; + } else { + GUF_ASSERT(err < GUF_STATIC_BUF_SIZE(guf_err_type_str)); + return guf_err_type_str[err]; + } +} + +extern void guf_panic_handler_default(guf_err err, const char *msg) +{ + fputs("libguf panic: ", stderr); + fputs(guf_err_to_str(err), stderr); + fputc(' ', stderr); + if (msg && err != GUF_ERR_NONE) { + fputs(msg, stderr); + } + fputc('\n', stderr); + + #ifndef NDEBUG + GUF_DEBUG_BREAK_CODE; + #endif + abort(); +} + +guf_panic_handler_fn guf_global_panic_handler = guf_panic_handler_default; + +#undef GUF_INIT +#endif diff --git a/src/guf_common.h b/src/guf_common.h index b4d0b3a..7e19b72 100644 --- a/src/guf_common.h +++ b/src/guf_common.h @@ -1,56 +1,55 @@ #ifndef GUF_COMMON_H #define GUF_COMMON_H -#include -#include #include -#include -#include +#include +#include +#include +#include -#include "guf_assert.h" -#include "guf_common_utils.h" +/* + // Copy- and move constructors: + GUF_T_COPY: GUF_T *(*copy)(GUF_T *dst, const GUF_T *src, void *ctx); + GUF_T_MOVE: GUF_T *(*move)(GUF_T *dst, GUF_T *src, void *ctx); // "Steals" the resources of src (named after move constructors in C++) -typedef enum guf_obj_cpy_opt { + // Destructor: + GUF_T_FREE: void (*free)(GUF_T *obj, void *ctx); + + // Comparison- and hash operators: + GUF_T_CMP: int (*cmp)(const GUF_T *a, const GUF_T *b); // a < b -> -1; a == b -> 0; a > b -> 1 + GUF_T_EQ: bool (*eq)(const GUF_T *a, const GUF_T *b); + GUF_T_HASH: guf_hash_size_t (*hash)(const GUF_T *obj); +*/ + +typedef enum guf_cpy_opt { GUF_CPY_VALUE = 0, GUF_CPY_DEEP = 1, GUF_CPY_MOVE = 2, -} guf_obj_cpy_opt; +} guf_cpy_opt; -typedef enum guf_sort_opt { - GUF_SORT_ASCENDING = 0, - GUF_SORT_DESCENDING = 1 -} guf_sort_opt; +#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]))) -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); -} +#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)) -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; -} +// The GUF_CAT/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 GUF_CAT(a, b) GUF_TOK_CAT(a, b) +// See comment above. +#define GUF_TOK_STRINGIFY(x) #x +#define GUF_STRINGIFY(x) GUF_TOK_STRINGIFY(x) -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; -} +#define GUF_CNT_FOREACH(CNT_PTR, CNT_TYPE, IT_NAME) for (GUF_CAT(CNT_TYPE, _iter) IT_NAME = GUF_CAT(CNT_TYPE, _begin)(CNT_PTR); IT_NAME.ptr != GUF_CAT(CNT_TYPE, _end)(CNT_PTR).ptr; it = GUF_CAT(CNT_TYPE, _iter_next)(CNT_PTR, IT_NAME, 1)) +#define GUF_CNT_FOREACH_STEP(CNT_PTR, CNT_TYPE, IT_NAME, STEP) for (GUF_CAT(CNT_TYPE, _iter) IT_NAME = GUF_CAT(CNT_TYPE, _begin)(CNT_PTR); IT_NAME.ptr != GUF_CAT(CNT_TYPE, _end)(CNT_PTR).ptr; it = GUF_CAT(CNT_TYPE, _iter_next)(CNT_PTR, IT_NAME, STEP)) -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; -} +#define GUF_CNT_FOREACH_REVERSE(CNT_PTR, CNT_TYPE, IT_NAME) for (GUF_CAT(CNT_TYPE, _iter) IT_NAME = GUF_CAT(CNT_TYPE, _rbegin)(CNT_PTR); IT_NAME.ptr != GUF_CAT(CNT_TYPE, _rend)(CNT_PTR).ptr; it = GUF_CAT(CNT_TYPE, _iter_next)(CNT_PTR, IT_NAME, 1)) +#define GUF_CNT_FOREACH_REVERSE_STEP(CNT_PTR, CNT_TYPE, IT_NAME, STEP) for (GUF_CAT(CNT_TYPE, _iter) IT_NAME = GUF_CAT(CNT_TYPE, _begin)(CNT_PTR); IT_NAME.ptr != GUF_CAT(CNT_TYPE, _end)(CNT_PTR).ptr; it = GUF_CAT(CNT_TYPE, _iter_next)(CNT_PTR, IT_NAME, STEP)) + +#define GUF_CNT_LIFETIME_BLOCK(GUF_CNT_TYPE, CNT_VARNAME, CODE) do { \ + GUF_CNT_TYPE CNT_VARNAME; \ + CODE; \ + GUF_CAT(GUF_CNT_TYPE, _free)(&CNT_VARNAME, NULL); \ +} while (0); #endif diff --git a/src/guf_common_utils.h b/src/guf_common_utils.h deleted file mode 100644 index bcb2905..0000000 --- a/src/guf_common_utils.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef GUF_COMMON_UTILS_H -#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) - -#define GUF_TOK_STRINGIFY(x) #x -#define GUF_STRINGIFY(x) GUF_TOK_STRINGIFY(x) - -#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; \ - code;\ - free_fn(&obj_name);\ -} while (0); - -static inline bool guf_is_big_endian(void) -{ - unsigned i = 1; - const char *bytes = (const char*)&i; - return bytes[0] != 1; -} - -#endif \ No newline at end of file diff --git a/src/guf_cstr.h b/src/guf_cstr.h index 7dfb872..d689def 100644 --- a/src/guf_cstr.h +++ b/src/guf_cstr.h @@ -1,29 +1,29 @@ #ifndef GUF_CSTR_H #define GUF_CSTR_H -#include #include "guf_common.h" +#include "guf_assert.h" -typedef const char* guf_const_cstr; +typedef const char* guf_cstr_const; -static inline int guf_const_cstr_cmp(const guf_const_cstr *a, const guf_const_cstr *b) +static inline int guf_cstr_const_cmp(const guf_cstr_const *a, const guf_cstr_const *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) +static inline bool guf_cstr_const_eq(const guf_cstr_const *a, const guf_cstr_const *b) { GUF_ASSERT_RELEASE(a && b); GUF_ASSERT_RELEASE(*a && *b); return 0 == strcmp(*a, *b); } +typedef char* guf_cstr_heap; -typedef char* guf_heap_cstr; - -static inline guf_heap_cstr *guf_heap_cstr_copy_construct(guf_heap_cstr *dst, const guf_heap_cstr *src) +static inline guf_cstr_heap *guf_cstr_heap_copy(guf_cstr_heap *dst, const guf_cstr_heap *src, void *ctx) { + (void)ctx; GUF_ASSERT_RELEASE(dst && src); if (*src == NULL) { *dst = NULL; @@ -34,21 +34,23 @@ static inline guf_heap_cstr *guf_heap_cstr_copy_construct(guf_heap_cstr *dst, co return dst; } -static inline guf_heap_cstr *guf_heap_cstr_move_construct(guf_heap_cstr *dst, guf_heap_cstr *src) +static inline guf_cstr_heap *guf_cstr_heap_move(guf_cstr_heap *dst, guf_cstr_heap *src, void *ctx) { + (void)ctx; GUF_ASSERT_RELEASE(dst && src); *dst = *src; *src = NULL; return dst; } -static inline void guf_heap_cstr_free(guf_heap_cstr *a) +static inline void guf_cstr_heap_free(guf_cstr_heap *a, void *ctx) { + (void)ctx; GUF_ASSERT_RELEASE(a); free(*a); } -static inline int guf_heap_cstr_cmp(const guf_heap_cstr *a, const guf_heap_cstr *b) +static inline int guf_cstr_heap_cmp(const guf_cstr_heap *a, const guf_cstr_heap *b) { GUF_ASSERT_RELEASE(a && b); @@ -56,11 +58,11 @@ static inline int guf_heap_cstr_cmp(const guf_heap_cstr *a, const guf_heap_cstr return strcmp(*a, *b); } -static inline bool guf_heap_cstr_eq(const guf_heap_cstr *a, const guf_heap_cstr *b) +static inline bool guf_cstr_heap_eq(const guf_cstr_heap *a, const guf_cstr_heap *b) { GUF_ASSERT_RELEASE(a && b); GUF_ASSERT_RELEASE(*a && *b); return 0 == strcmp(*a, *b); } -#endif \ No newline at end of file +#endif diff --git a/src/guf_dbuf.h b/src/guf_dbuf.h index 67d4a9b..8e2b0b2 100644 --- a/src/guf_dbuf.h +++ b/src/guf_dbuf.h @@ -1,77 +1,139 @@ #ifndef GUF_DBUF_H #define GUF_DBUF_H - #include - #include - #include - #include - #include "guf_assert.h" #include "guf_common.h" + #include "guf_assert.h" #include "guf_alloc.h" - #define GUF_DBUF_FOREACH(DBUF, ELEM_TYPE, ELEM_PTR_NAME) for (ELEM_TYPE *ELEM_PTR_NAME = (DBUF).data, *end = (DBUF).data ? (DBUF).data + (DBUF).size : NULL; ELEM_PTR_NAME != end; ++ELEM_PTR_NAME) + #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: - - GUF_CNT_T: The value type stored in the container. - - 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_T: The value type stored in the container. + - GUF_CNT_NAME: The typename of the resulting container (default: dbuf_GUF_T) + - GUF_T_IS_INTEGRAL_TYPE: whether the type is an integral type (if defined, must not define COPY, MOVE, FREE and EQ) - - GUF_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_T_COPY: cpy function with signature GUF_T *copy(GUF_T *dst, const GUF_T *src, void *ctx) (default: copy by value) + - GUF_T_MOVE: move function with signature GUF_T *move(GUF_T *dst, GUF_T *src, void *ctx) (default: undefined) + - GUF_T_FREE: free function with signature void free(GUF_T *a, void *ctx) (default: undefined) + - GUF_T_EQ: equality function with signature bool eq(const GUF_T *a, const GUF_T *a) (default: undefined, or equality by value if GUF_T_IS_INTEGRAL_TYPE is defined) - GUF_DBUF_INITIAL_CAP: The capacity used for the first allocation if the container got initialised with a capacity of zero (default: 8) - GUF_DBUF_USE_GROWTH_FAC_ONE_POINT_FIVE: If defined, always use 1.5 as the growth-factor (instead of 2) */ +#ifndef GUF_T + #error "Undefined container template GUF_T" +#endif + +#ifndef GUF_CNT_NAME + #define GUF_CNT_NAME GUF_CAT(dbuf_, GUF_T) +#endif + // Used for the first growth if dbuf->capacity is zero. #ifndef GUF_DBUF_INITIAL_CAP #define GUF_DBUF_INITIAL_CAP 8 #endif -#ifndef GUF_CNT_T - #error "Undefined container template GUF_CONTAINER_T" +#if (defined(GUF_T_COPY) || defined(GUF_T_MOVE)) && !defined(GUF_T_FREE) + #error "Must define GUF_T_FREE because GUF_T_COPY or GUF_T_MOVE is defined" #endif -#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)) +#if defined(GUF_T_IS_INTEGRAL_TYPE) && (defined(GUF_T_COPY) || defined(GUF_CNT_MOVE) || defined(GUF_CNT_FREE) || defined(GUF_T_EQ)) #error "Integral types do not need COPY, MOVE, FREE or EQ functions" #endif -// #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 +#ifdef GUF_IMPL_STATIC + #define GUF_FN_KEYWORDS static +#else + #define GUF_FN_KEYWORDS +#endif typedef struct GUF_CNT_NAME { - guf_allocator *allocator; // Must not be NULL. - GUF_CNT_T *data; + GUF_T *data; ptrdiff_t size, capacity; + guf_allocator *allocator; + #ifdef GUF_CNT_WITH_ELEM_CTX + void *elem_ctx; // NULL by default; is passed as the ctx argument to GUF_T_COPY, GUF_T_MOVE and GUF_T_FREE + #endif } GUF_CNT_NAME; -bool GUFCAT(GUF_CNT_NAME, _valid)(const GUF_CNT_NAME* dbuf) +typedef struct GUF_CAT(GUF_CNT_NAME, _iter) { + GUF_T *ptr; + bool reverse; +} GUF_CAT(GUF_CNT_NAME, _iter); + +GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _valid)(const GUF_CNT_NAME* dbuf); + +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity, guf_err *err); +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity); + +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_init)(GUF_CNT_NAME *dbuf, ptrdiff_t capacity, guf_allocator *allocator, guf_err *err); +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _init)(GUF_CNT_NAME *dbuf, ptrdiff_t start_cap, guf_allocator *allocator); +GUF_FN_KEYWORDS GUF_CNT_NAME GUF_CAT(GUF_CNT_NAME, _new)(guf_allocator *allocator); +GUF_FN_KEYWORDS GUF_CNT_NAME GUF_CAT(GUF_CNT_NAME, _try_new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator, guf_err *err); +GUF_FN_KEYWORDS GUF_CNT_NAME GUF_CAT(GUF_CNT_NAME, _new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator); + +#ifdef GUF_CNT_WITH_ELEM_CTX + GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _set_elem_ctx)(GUF_CNT_NAME *dbuf, void *elem_ctx); +#endif +GUF_FN_KEYWORDS void *GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(const GUF_CNT_NAME *dbuf); // Always returns NULL if GUF_CNT_WITH_ELEM_CTX is not defined + +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _free)(GUF_CNT_NAME *dbuf, void *ctx); +GUF_FN_KEYWORDS GUF_CNT_NAME *GUF_CAT(GUF_CNT_NAME, _copy)(GUF_CNT_NAME *dst, const GUF_CNT_NAME *src, void *ctx); +GUF_FN_KEYWORDS GUF_CNT_NAME *GUF_CAT(GUF_CNT_NAME, _move)(GUF_CNT_NAME *dst, GUF_CNT_NAME *src, void *ctx); + +GUF_FN_KEYWORDS ptrdiff_t GUF_CAT(GUF_CNT_NAME, _try_get_next_capacity)(ptrdiff_t old_cap, guf_err *err); +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_grow_if_full)(GUF_CNT_NAME *dbuf, guf_err *err); +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_shrink_to_fit)(GUF_CNT_NAME *dbuf, guf_err *err); + +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_insert)(GUF_CNT_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt, guf_err *err); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _insert)(GUF_CNT_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_insert_val)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx, guf_err *err); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _insert_val)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx); +#ifdef GUF_T_COPY +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_insert_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx, guf_err *err); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _insert_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx); +#endif + +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_push)(GUF_CNT_NAME *dbuf, GUF_T *elem, guf_cpy_opt cpy_opt, guf_err *err); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _push)(GUF_CNT_NAME *dbuf, GUF_T *elem, guf_cpy_opt cpy_opt); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_push_val)(GUF_CNT_NAME *dbuf, GUF_T elem, guf_err *err); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _push_val)(GUF_CNT_NAME *dbuf, GUF_T elem); +#ifdef GUF_T_COPY +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_push_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem, guf_err *err); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _push_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem); +#endif + +GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _try_erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx, guf_err *err); +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx); + +GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _try_pop)(GUF_CNT_NAME *dbuf, guf_err *err); +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _pop)(GUF_CNT_NAME *dbuf); +GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _try_pop_cpy)(GUF_CNT_NAME *dbuf, guf_cpy_opt cpy_opt, guf_err *err); +GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _pop_cpy)(GUF_CNT_NAME *dbuf, guf_cpy_opt cpy_opt); + +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx, guf_err *err); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_front)(GUF_CNT_NAME *dbuf, guf_err *err); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _front)(GUF_CNT_NAME *dbuf); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_back)(GUF_CNT_NAME *dbuf, guf_err *err); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _back)(GUF_CNT_NAME *dbuf); + +GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _begin)(const GUF_CNT_NAME* dbuf); +GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _end)(const GUF_CNT_NAME* dbuf); +GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _rbegin)(const GUF_CNT_NAME* dbuf); +GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _rend)(const GUF_CNT_NAME* dbuf); +GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _iter_next)(const GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) it, ptrdiff_t step); + +#if defined(GUF_T_IS_INTEGRAL_TYPE) || defined(GUF_T_EQ) +GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find)(GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) begin, GUF_CAT(GUF_CNT_NAME, _iter) end, const GUF_T *needle); +GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find_val)(GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) begin, GUF_CAT(GUF_CNT_NAME, _iter) end, GUF_T needle_val); +GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find_if)(GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) *begin, GUF_CAT(GUF_CNT_NAME, _iter) *end, bool (*predicate)(const GUF_T *)); +#endif + +#if defined(GUF_IMPL) || defined(GUF_IMPL_STATIC) + +GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _valid)(const GUF_CNT_NAME* dbuf) { if (!dbuf) { return false; @@ -81,32 +143,37 @@ bool GUFCAT(GUF_CNT_NAME, _valid)(const GUF_CNT_NAME* dbuf) return valid_data_ptr && valid_allocator && dbuf->capacity >= 0 && dbuf->size >= 0 && dbuf->size <= dbuf->capacity; } -void GUFCAT(GUF_CNT_NAME, _try_reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity, guf_err *err) +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _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(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); GUF_ASSERT_RELEASE(min_capacity >= 0); - if (min_capacity <= dbuf->capacity) { + if (min_capacity <= dbuf->capacity || min_capacity == 0) { guf_err_set_if_not_null(err, GUF_ERR_NONE); return; } GUF_ASSERT(min_capacity > 0); - if (!dbuf->data) { - 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); + ptrdiff_t new_alloc_bytes = -1; + if (!guf_size_calc_safe(min_capacity, sizeof(GUF_T), &new_alloc_bytes)) { + guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_reserve: overflow of ptrdiff_t")); + return; + } + GUF_ASSERT(new_alloc_bytes > 0); + + if (!dbuf->data) { // a.) Allocate. + GUF_T *data = dbuf->allocator->alloc(new_alloc_bytes, dbuf->allocator->ctx); if (!data) { - guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_reserve: failed to allocate initial buffer data")); + guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_reserve: failed to allocate initial dbuf->data")); return; } dbuf->data = data; - } else { - GUF_CNT_T *data = dbuf->allocator->realloc(dbuf->data, dbuf->capacity * sizeof(GUF_CNT_T), min_capacity * sizeof(GUF_CNT_T), dbuf->allocator->ctx); + } else { // b.) Re-allocate. + ptrdiff_t old_alloc_bytes = dbuf->capacity * sizeof(GUF_T); + GUF_ASSERT(old_alloc_bytes < new_alloc_bytes); + GUF_T *data = dbuf->allocator->realloc(dbuf->data, old_alloc_bytes, new_alloc_bytes, dbuf->allocator->ctx); if (!data) { - guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_reserv: failed to reallocate buffer data")); + guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function dbuf_try_reserve: failed to reallocate dbuf->data")); return; } dbuf->data = data; @@ -114,25 +181,28 @@ void GUFCAT(GUF_CNT_NAME, _try_reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capaci guf_err_set_if_not_null(err, GUF_ERR_NONE); dbuf->capacity = min_capacity; - return; + GUF_ASSERT(dbuf->data && dbuf->capacity); } -void GUFCAT(GUF_CNT_NAME, _reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity) +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _reserve)(GUF_CNT_NAME *dbuf, ptrdiff_t min_capacity) { - GUFCAT(GUF_CNT_NAME, _try_reserve)(dbuf, min_capacity, NULL); + GUF_CAT(GUF_CNT_NAME, _try_reserve)(dbuf, min_capacity, NULL); } -void GUFCAT(GUF_CNT_NAME, _try_init)(GUF_CNT_NAME *dbuf, ptrdiff_t capacity, guf_allocator *allocator, guf_err *err) +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_init)(GUF_CNT_NAME *dbuf, ptrdiff_t capacity, guf_allocator *allocator, guf_err *err) { GUF_ASSERT_RELEASE(dbuf); GUF_ASSERT_RELEASE(capacity >= 0); - if (dbuf->size != 0 || dbuf->capacity != 0 || dbuf->data) { + if (dbuf->size != 0 || dbuf->capacity != 0 || dbuf->data || GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)) { guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in function dbuf_try_init: dbuf might have been already initialised")); return; } dbuf->size = dbuf->capacity = 0; + #ifdef GUF_CNT_WITH_ELEM_CTX + dbuf->elem_ctx = NULL; + #endif if (!allocator || !(allocator->alloc && allocator->realloc && allocator->free)) { guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in function dbuf_try_init: allocator (or one of it's function pointers) is NULL")); @@ -145,28 +215,28 @@ void GUFCAT(GUF_CNT_NAME, _try_init)(GUF_CNT_NAME *dbuf, ptrdiff_t capacity, guf dbuf->data = NULL; guf_err_set_if_not_null(err, GUF_ERR_NONE); } else { - GUFCAT(GUF_CNT_NAME, _try_reserve)(dbuf, capacity, err); + GUF_CAT(GUF_CNT_NAME, _try_reserve)(dbuf, capacity, err); } } -void GUFCAT(GUF_CNT_NAME, _init)(GUF_CNT_NAME *dbuf, ptrdiff_t start_cap, guf_allocator *allocator) +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _init)(GUF_CNT_NAME *dbuf, ptrdiff_t start_cap, guf_allocator *allocator) { - GUFCAT(GUF_CNT_NAME, _try_init)(dbuf, start_cap, allocator, NULL); + GUF_CAT(GUF_CNT_NAME, _try_init)(dbuf, start_cap, allocator, NULL); } -GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _new)(guf_allocator *allocator) +GUF_FN_KEYWORDS GUF_CNT_NAME GUF_CAT(GUF_CNT_NAME, _new)(guf_allocator *allocator) { GUF_CNT_NAME dbuf = {0}; - GUFCAT(GUF_CNT_NAME, _init)(&dbuf, 0, allocator); + GUF_CAT(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)(ptrdiff_t capacity, guf_allocator *allocator, guf_err *err) +GUF_FN_KEYWORDS GUF_CNT_NAME GUF_CAT(GUF_CNT_NAME, _try_new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator, guf_err *err) { GUF_CNT_NAME dbuf = {0}; - GUFCAT(GUF_CNT_NAME, _try_init)(&dbuf, capacity, allocator, err); + GUF_CAT(GUF_CNT_NAME, _try_init)(&dbuf, capacity, allocator, err); if (err && *err != GUF_ERR_NONE) { return (GUF_CNT_NAME){0}; } @@ -174,12 +244,135 @@ GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _try_new_with_capacity)(ptrdiff_t capacity, gu return dbuf; } -GUF_CNT_NAME GUFCAT(GUF_CNT_NAME, _new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator) +GUF_FN_KEYWORDS GUF_CNT_NAME GUF_CAT(GUF_CNT_NAME, _new_with_capacity)(ptrdiff_t capacity, guf_allocator *allocator) { - return GUFCAT(GUF_CNT_NAME, _try_new_with_capacity)(capacity, allocator, NULL); + return GUF_CAT(GUF_CNT_NAME, _try_new_with_capacity)(capacity, allocator, NULL); } -ptrdiff_t GUFCAT(GUF_CNT_NAME, _try_next_capacity)(ptrdiff_t old_cap, guf_err *err) +#ifdef GUF_CNT_WITH_ELEM_CTX +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _set_elem_ctx)(GUF_CNT_NAME *dbuf, void *elem_ctx) +{ + GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); + #ifdef GUF_CNT_WITH_ELEM_CTX + dbuf->elem_ctx = elem_ctx; + #else + (void)elem_ctx; + GUF_ASSERT_RELEASE(false); + #endif +} +#endif + +GUF_FN_KEYWORDS void *GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(const GUF_CNT_NAME *dbuf) +{ + #ifdef GUF_CNT_WITH_ELEM_CTX + return dbuf->elem_ctx; + #else + (void)dbuf; + return NULL; + #endif +} + +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _free)(GUF_CNT_NAME *dbuf, void *ctx) +{ + (void)ctx; + GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); + if (dbuf->capacity == 0) { + GUF_ASSERT_RELEASE(!dbuf->data); + GUF_ASSERT_RELEASE(dbuf->size == 0); + return; + } + GUF_ASSERT_RELEASE(dbuf->data); + + #ifdef GUF_T_FREE + for (ptrdiff_t idx = 0; idx < dbuf->size; ++idx) { + GUF_T_FREE(dbuf->data + idx, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); + } + #endif + + dbuf->allocator->free(dbuf->data, sizeof(GUF_T) * dbuf->capacity, dbuf->allocator->ctx); + dbuf->data = NULL; + dbuf->capacity = dbuf->size = 0; + dbuf->allocator = NULL; + #ifdef GUF_CNT_WITH_ELEM_CTX + dbuf->elem_ctx = NULL; + #endif +} + +GUF_FN_KEYWORDS GUF_CNT_NAME *GUF_CAT(GUF_CNT_NAME, _copy)(GUF_CNT_NAME *dst, const GUF_CNT_NAME *src, void *ctx) +{ + (void)ctx; + if (!dst || !src || GUF_CAT(GUF_CNT_NAME, _valid)(src)) { + return NULL; + } + + if (src->capacity == 0) { + GUF_ASSERT(!src->data); + GUF_ASSERT(src->size == 0); + *dst = *src; + return dst; + } + + GUF_ASSERT(src->data && src->capacity > 0); + + dst->allocator = src->allocator; + #ifdef GUF_CNT_WITH_ELEM_CTX + dst->elem_ctx = src->elem_ctx; + #endif + + if (src->size == 0) { + dst->capacity = 0; + dst->size = 0; + dst->data = NULL; + return dst; + } else { + ptrdiff_t dst_cap = src->size; + GUF_T *dst_data = src->allocator->alloc(dst_cap * sizeof(GUF_T), src->allocator->ctx); + dst->data = dst_data; + if (!dst->data) { + dst->capacity = 0; + dst->size = 0; + return NULL; + } else { + dst->capacity = dst_cap; + dst->size = src->size; + } + + for (ptrdiff_t i = 0; i < src->size; ++i) { + #ifdef GUF_T_COPY + GUF_T *cpy_success = GUF_T_COPY(dst->data + i, src->data + i, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(src)); + if (!cpy_success) { + dst->size = i; + return NULL; + } + #else + dst->data[i] = src->data[i]; + #endif + } + return dst; + } +} + +GUF_FN_KEYWORDS GUF_CNT_NAME *GUF_CAT(GUF_CNT_NAME, _move)(GUF_CNT_NAME *dst, GUF_CNT_NAME *src, void *ctx) +{ + (void)ctx; + if (!dst || !src) { + return NULL; + } + + *dst = *src; + + src->capacity = 0; + src->size = 0; + src->data = NULL; + src->allocator = NULL; + #ifdef GUF_CNT_WITH_ELEM_CTX + src->elem_ctx = NULL; + #endif + + return dst; +} + +GUF_FN_KEYWORDS ptrdiff_t GUF_CAT(GUF_CNT_NAME, _try_get_next_capacity)(ptrdiff_t old_cap, guf_err *err) { GUF_ASSERT_RELEASE(old_cap >= 0); size_t new_cap = 0; @@ -196,10 +389,9 @@ ptrdiff_t GUFCAT(GUF_CNT_NAME, _try_next_capacity)(ptrdiff_t old_cap, guf_err *e } new_cap = GUF_MIN(new_cap, PTRDIFF_MAX); - GUF_ASSERT(new_cap <= PTRDIFF_MAX); if (new_cap <= (size_t)old_cap) { // Detect overflow. - guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, GUF_ERR_MSG("in function dbuf_try_next_capacity: next capacity would overflow ptrdiff_t")); + guf_err_set_or_panic(err, GUF_ERR_INT_OVERFLOW, GUF_ERR_MSG("in function dbuf_try_get_next_capacity: next capacity would overflow ptrdiff_t")); return -1; } else { guf_err_set_if_not_null(err, GUF_ERR_NONE); @@ -207,18 +399,18 @@ ptrdiff_t GUFCAT(GUF_CNT_NAME, _try_next_capacity)(ptrdiff_t old_cap, guf_err *e } } -void GUFCAT(GUF_CNT_NAME, _try_grow_if_full)(GUF_CNT_NAME *dbuf, guf_err *err) +GUF_FN_KEYWORDS void GUF_CAT(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(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); GUF_ASSERT_RELEASE(dbuf->capacity >= 0 && dbuf->size >= 0); if (dbuf->size == dbuf->capacity) { - ptrdiff_t next_cap = GUFCAT(GUF_CNT_NAME, _try_next_capacity)(dbuf->capacity, err); + ptrdiff_t next_cap = GUF_CAT(GUF_CNT_NAME, _try_get_next_capacity)(dbuf->capacity, err); if (err && *err != GUF_ERR_NONE) { return; } GUF_ASSERT(next_cap > 0); - GUFCAT(GUF_CNT_NAME, _try_reserve)(dbuf, next_cap, err); + GUF_CAT(GUF_CNT_NAME, _try_reserve)(dbuf, next_cap, err); if (err && *err != GUF_ERR_NONE) { return; } @@ -228,16 +420,16 @@ void GUFCAT(GUF_CNT_NAME, _try_grow_if_full)(GUF_CNT_NAME *dbuf, guf_err *err) guf_err_set_if_not_null(err, GUF_ERR_NONE); } -static inline bool GUFCAT(GUF_CNT_NAME, _copy_opt_available)(guf_obj_cpy_opt cpy_opt) +static inline bool GUF_CAT(GUF_CNT_NAME, _copy_opt_available)(guf_cpy_opt cpy_opt) { if (cpy_opt == GUF_CPY_DEEP) { - #ifdef GUF_CNT_T_COPY + #ifdef GUF_T_COPY return true; #else return false; #endif } else if (cpy_opt == GUF_CPY_MOVE) { - #ifdef GUF_CNT_T_MOVE + #ifdef GUF_T_MOVE return true; #else return false; @@ -249,39 +441,41 @@ static inline bool GUFCAT(GUF_CNT_NAME, _copy_opt_available)(guf_obj_cpy_opt cpy } } -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_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_insert)(GUF_CNT_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt, guf_err *err) { - GUF_ASSERT(GUFCAT(GUF_CNT_NAME,_valid)(dbuf)); + GUF_ASSERT(GUF_CAT(GUF_CNT_NAME,_valid)(dbuf)); if (idx < 0 || idx > dbuf->size) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_insert")); return NULL; } - GUFCAT(GUF_CNT_NAME, _try_grow_if_full)(dbuf, err); + GUF_CAT(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]; } - GUF_CNT_T *dst = dbuf->data + idx; + GUF_T *dst = dbuf->data + idx; - 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")); + if (!GUF_CAT(GUF_CNT_NAME, _copy_opt_available)(cpy_opt)) { + guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in function " GUF_STRINGIFY(GUF_CAT(GUF_CNT_NAME, _copy_opt_available)) ": cpy_opt unavailable")); return NULL; } else if (cpy_opt == GUF_CPY_DEEP) { - #ifdef GUF_CNT_T_COPY - dst = GUF_CNT_T_COPY(dst, elem); + #ifdef GUF_T_COPY + dst = GUF_T_COPY(dst, elem, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); + #else + GUF_ASSERT_RELEASE(false); #endif } else if (cpy_opt == GUF_CPY_MOVE) { - #ifdef GUF_CNT_T_MOVE - dst = GUF_CNT_T_MOVE(dst, elem); + #ifdef GUF_T_MOVE + dst = GUF_T_MOVE(dst, elem, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); + #else + GUF_ASSERT_RELEASE(false); #endif } else { GUF_ASSERT_RELEASE(cpy_opt == GUF_CPY_VALUE); @@ -302,69 +496,71 @@ GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_insert)(GUF_CNT_NAME *dbuf, GUF_CNT_T *elem } } -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_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _insert)(GUF_CNT_NAME *dbuf, GUF_T *elem, ptrdiff_t idx, guf_cpy_opt cpy_opt) { - return GUFCAT(GUF_CNT_NAME, _try_insert)(dbuf, elem, idx, cpy_opt, NULL); + return GUF_CAT(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) +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_insert_val)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx, guf_err *err) { - return GUFCAT(GUF_CNT_NAME, _try_insert)(dbuf, &elem, idx, GUF_CPY_VALUE, err); + return GUF_CAT(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) +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _insert_val)(GUF_CNT_NAME *dbuf, GUF_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); + return GUF_CAT(GUF_CNT_NAME, _try_insert_val)(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) +#ifdef GUF_T_COPY +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_insert_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx, guf_err *err) { - GUF_ASSERT(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - return GUFCAT(GUF_CNT_NAME, _try_insert)(dbuf, elem, dbuf->size, cpy_opt, err); + return GUF_CAT(GUF_CNT_NAME, _try_insert)(dbuf, &elem, idx, GUF_CPY_DEEP, err); } -GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _push)(GUF_CNT_NAME *dbuf, GUF_CNT_T *elem, guf_obj_cpy_opt cpy_opt) +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _insert_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem, ptrdiff_t idx) { - return GUFCAT(GUF_CNT_NAME, _try_push)(dbuf, elem, cpy_opt, NULL); + return GUF_CAT(GUF_CNT_NAME, _try_insert_val_cpy)(dbuf, elem, idx, NULL); +} +#endif + +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_push)(GUF_CNT_NAME *dbuf, GUF_T *elem, guf_cpy_opt cpy_opt, guf_err *err) +{ + GUF_ASSERT(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); + return GUF_CAT(GUF_CNT_NAME, _try_insert)(dbuf, elem, dbuf->size, cpy_opt, err); } -GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_push_val)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem, guf_err *err) +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _push)(GUF_CNT_NAME *dbuf, GUF_T *elem, guf_cpy_opt cpy_opt) { - return GUFCAT(GUF_CNT_NAME, _try_push)(dbuf, &elem, GUF_CPY_VALUE, err); + return GUF_CAT(GUF_CNT_NAME, _try_push)(dbuf, elem, cpy_opt, NULL); } -GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _push_val)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem) +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_push_val)(GUF_CNT_NAME *dbuf, GUF_T elem, guf_err *err) { - return GUFCAT(GUF_CNT_NAME, _try_push_val)(dbuf, elem, NULL); + return GUF_CAT(GUF_CNT_NAME, _try_push)(dbuf, &elem, GUF_CPY_VALUE, err); } -#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) +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _push_val)(GUF_CNT_NAME *dbuf, GUF_T elem) { - return GUFCAT(GUF_CNT_NAME, _try_push)(dbuf, &elem, GUF_CPY_DEEP, err); + return GUF_CAT(GUF_CNT_NAME, _try_push_val)(dbuf, elem, NULL); } -GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _push_val_cpy)(GUF_CNT_NAME *dbuf, GUF_CNT_T elem) +#ifdef GUF_T_COPY +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_push_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem, guf_err *err) { - return GUFCAT(GUF_CNT_NAME, _try_push_val_cpy)(dbuf, elem, NULL); + return GUF_CAT(GUF_CNT_NAME, _try_push)(dbuf, &elem, GUF_CPY_DEEP, err); +} + +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _push_val_cpy)(GUF_CNT_NAME *dbuf, GUF_T elem) +{ + return GUF_CAT(GUF_CNT_NAME, _try_push_val_cpy)(dbuf, elem, NULL); } #endif -bool GUFCAT(GUF_CNT_NAME, _try_erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx, guf_err *err) +GUF_FN_KEYWORDS bool GUF_CAT(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(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->size == 0) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_erase: cannot erase from empty buffer")); @@ -375,8 +571,8 @@ bool GUFCAT(GUF_CNT_NAME, _try_erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx, guf_err return false; } - #ifdef GUF_CNT_T_FREE - GUF_CNT_T_FREE(dbuf->data + idx); + #ifdef GUF_T_FREE + GUF_T_FREE(dbuf->data + idx, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); #endif for (ptrdiff_t free_idx = idx; free_idx < dbuf->size - 1; ++free_idx) { // Make space by moving elements to the left if necessary. @@ -388,23 +584,23 @@ bool GUFCAT(GUF_CNT_NAME, _try_erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx, guf_err return true; } -void GUFCAT(GUF_CNT_NAME, _erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx) +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _erase)(GUF_CNT_NAME *dbuf, ptrdiff_t idx) { - GUFCAT(GUF_CNT_NAME, _try_erase)(dbuf, idx, NULL); + GUF_CAT(GUF_CNT_NAME, _try_erase)(dbuf, idx, NULL); } -bool GUFCAT(GUF_CNT_NAME, _try_pop)(GUF_CNT_NAME *dbuf, guf_err *err) +GUF_FN_KEYWORDS bool GUF_CAT(GUF_CNT_NAME, _try_pop)(GUF_CNT_NAME *dbuf, guf_err *err) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->size == 0) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_pop: Cannot pop from empty dbuf")); return false; } - #ifdef GUF_CNT_T_FREE - GUF_CNT_T *popped = dbuf->data + --dbuf->size; - GUF_CNT_T_FREE(popped); + #ifdef GUF_T_FREE + GUF_T *popped = dbuf->data + --dbuf->size; + GUF_T_FREE(popped, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); #else --dbuf->size; #endif @@ -413,39 +609,39 @@ bool GUFCAT(GUF_CNT_NAME, _try_pop)(GUF_CNT_NAME *dbuf, guf_err *err) return true; } -void GUFCAT(GUF_CNT_NAME, _pop)(GUF_CNT_NAME *dbuf) +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _pop)(GUF_CNT_NAME *dbuf) { - GUFCAT(GUF_CNT_NAME, _try_pop)(dbuf, NULL); + GUF_CAT(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_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _try_pop_cpy)(GUF_CNT_NAME *dbuf, guf_cpy_opt cpy_opt, guf_err *err) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->size == 0) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_pop_cpy: Cannot pop from empty dbuf")); - GUF_CNT_T dummy; - memset(&dummy, 0, sizeof(GUF_CNT_T)); + GUF_T dummy; + memset(&dummy, 0, sizeof(GUF_T)); return dummy; } - GUF_CNT_T *popped = dbuf->data + (dbuf->size - 1); - GUF_CNT_T popped_val; - GUF_CNT_T *dst = &popped_val; + GUF_T *popped = dbuf->data + (dbuf->size - 1); + GUF_T popped_val; + GUF_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")); + if (!GUF_CAT(GUF_CNT_NAME, _copy_opt_available)(cpy_opt)) { + guf_err_set_or_panic(err, GUF_ERR_INVALID_ARG, GUF_ERR_MSG("in function " GUF_STRINGIFY(GUF_CAT(GUF_CNT_NAME, _copy_opt_available)) ": cpy_opt unavailable")); memset(&popped_val, 0, sizeof(popped_val)); return popped_val; } else if (cpy_opt == GUF_CPY_DEEP) { - #ifdef GUF_CNT_T_COPY - dst = GUF_CNT_T_COPY(dst, popped); + #ifdef GUF_T_COPY + dst = GUF_T_COPY(dst, popped, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); #else GUF_ASSERT_RELEASE(false); #endif } else if (cpy_opt == GUF_CPY_MOVE) { - #ifdef GUF_CNT_T_MOVE - dst = GUF_CNT_T_MOVE(dst, popped); + #ifdef GUF_T_MOVE + dst = GUF_T_MOVE(dst, popped, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); #else GUF_ASSERT_RELEASE(false); #endif @@ -456,12 +652,12 @@ GUF_CNT_T GUFCAT(GUF_CNT_NAME, _try_pop_cpy)(GUF_CNT_NAME *dbuf, guf_obj_cpy_opt 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)); + memset(&popped_val, 0, sizeof(GUF_T)); return popped_val; // Return a dummy value in case the copy failed (as we have to return something). } else { - #ifdef GUF_CNT_T_FREE + #ifdef GUF_T_FREE if (cpy_opt == GUF_CPY_DEEP) { - GUF_CNT_T_FREE(popped); + GUF_T_FREE(popped, GUF_CAT(GUF_CNT_NAME, _get_elem_ctx)(dbuf)); } #endif dbuf->size--; @@ -470,15 +666,15 @@ GUF_CNT_T GUFCAT(GUF_CNT_NAME, _try_pop_cpy)(GUF_CNT_NAME *dbuf, guf_obj_cpy_opt } } -GUF_CNT_T GUFCAT(GUF_CNT_NAME, _pop_cpy)(GUF_CNT_NAME *dbuf, guf_obj_cpy_opt cpy_opt) +GUF_FN_KEYWORDS GUF_T GUF_CAT(GUF_CNT_NAME, _pop_cpy)(GUF_CNT_NAME *dbuf, guf_cpy_opt cpy_opt) { - return GUFCAT(GUF_CNT_NAME, _try_pop_cpy)(dbuf, cpy_opt, NULL); + return GUF_CAT(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_FN_KEYWORDS GUF_T *GUF_CAT(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_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->size == 0) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_at: dbuf is empty")); @@ -493,15 +689,15 @@ GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx, guf_ return dbuf->data + idx; } -GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx) +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _at)(GUF_CNT_NAME *dbuf, ptrdiff_t idx) { - return GUFCAT(GUF_CNT_NAME, _try_at)(dbuf, idx, NULL); + return GUF_CAT(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_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_front)(GUF_CNT_NAME *dbuf, guf_err *err) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->size == 0) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_front: dbuf is empty")); @@ -510,15 +706,15 @@ GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_front)(GUF_CNT_NAME *dbuf, guf_err *err) return dbuf->data + 0; } -GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _front)(GUF_CNT_NAME *dbuf) +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _front)(GUF_CNT_NAME *dbuf) { - return GUFCAT(GUF_CNT_NAME, _try_front)(dbuf, NULL); + return GUF_CAT(GUF_CNT_NAME, _try_front)(dbuf, NULL); } -GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_back)(GUF_CNT_NAME *dbuf, guf_err *err) +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _try_back)(GUF_CNT_NAME *dbuf, guf_err *err) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); if (dbuf->size == 0) { guf_err_set_or_panic(err, GUF_ERR_IDX_RANGE, GUF_ERR_MSG("in function dbuf_try_back: dbuf is empty")); @@ -528,14 +724,14 @@ GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _try_back)(GUF_CNT_NAME *dbuf, guf_err *err) return dbuf->data + (dbuf->size - 1); } -GUF_CNT_T *GUFCAT(GUF_CNT_NAME, _back)(GUF_CNT_NAME *dbuf) +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_CNT_NAME, _back)(GUF_CNT_NAME *dbuf) { - return GUFCAT(GUF_CNT_NAME, _try_back)(dbuf, NULL); + return GUF_CAT(GUF_CNT_NAME, _try_back)(dbuf, NULL); } -void GUFCAT(GUF_CNT_NAME, _try_shrink_to_fit)(GUF_CNT_NAME *dbuf, guf_err *err) +GUF_FN_KEYWORDS void GUF_CAT(GUF_CNT_NAME, _try_shrink_to_fit)(GUF_CNT_NAME *dbuf, guf_err *err) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); const ptrdiff_t new_capacity = dbuf->size; if (new_capacity == dbuf->capacity || (!dbuf->data && !dbuf->capacity)) { guf_err_set_if_not_null(err, GUF_ERR_NONE); @@ -545,11 +741,11 @@ void GUFCAT(GUF_CNT_NAME, _try_shrink_to_fit)(GUF_CNT_NAME *dbuf, guf_err *err) GUF_ASSERT_RELEASE(dbuf->data); if (new_capacity == 0) { - dbuf->allocator->free(dbuf->data, sizeof(GUF_CNT_T) * dbuf->capacity, dbuf->allocator->ctx); + dbuf->allocator->free(dbuf->data, sizeof(GUF_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); + GUF_T *data = dbuf->allocator->realloc(dbuf->data, new_capacity, sizeof(GUF_T), dbuf->allocator->ctx); if (!data) { guf_err_set_or_panic(err, GUF_ERR_ALLOC_FAIL, GUF_ERR_MSG("in function _try_shrink_to_fit")); return; @@ -560,76 +756,47 @@ void GUFCAT(GUF_CNT_NAME, _try_shrink_to_fit)(GUF_CNT_NAME *dbuf, guf_err *err) } -void GUFCAT(GUF_CNT_NAME, _free)(GUF_CNT_NAME *dbuf) +GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _begin)(const GUF_CNT_NAME* dbuf) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - if (dbuf->capacity == 0) { - GUF_ASSERT_RELEASE(!dbuf->data); - GUF_ASSERT_RELEASE(dbuf->size == 0); - return; - } - GUF_ASSERT_RELEASE(dbuf->data); - - #ifdef GUF_CNT_T_FREE - for (ptrdiff_t idx = 0; idx < dbuf->size; ++idx) { - GUF_CNT_T_FREE(dbuf->data + idx); - } - #endif - - dbuf->allocator->free(dbuf->data, sizeof(GUF_CNT_T) * dbuf->capacity, dbuf->allocator->ctx); - dbuf->data = NULL; - dbuf->capacity = dbuf->size = 0; -} - - -typedef struct GUFCAT(GUF_CNT_NAME, _iter) { - GUF_CNT_T *ptr; - bool reverse; - -} GUFCAT(GUF_CNT_NAME, _iter); - -static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _begin)(const GUF_CNT_NAME* dbuf) -{ - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - return (GUFCAT(GUF_CNT_NAME, _iter)) { + GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); + return (GUF_CAT(GUF_CNT_NAME, _iter)) { .ptr = dbuf->data, .reverse = false }; } -static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _end)(const GUF_CNT_NAME* dbuf) +GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _end)(const GUF_CNT_NAME* dbuf) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - return (GUFCAT(GUF_CNT_NAME, _iter)) { + GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); + return (GUF_CAT(GUF_CNT_NAME, _iter)) { .ptr = dbuf->data ? dbuf->data + dbuf->size : NULL, .reverse = false }; } -static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _rbegin)(const GUF_CNT_NAME* dbuf) +GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _rbegin)(const GUF_CNT_NAME* dbuf) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - return (GUFCAT(GUF_CNT_NAME, _iter)) { + GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); + return (GUF_CAT(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_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _rend)(const GUF_CNT_NAME* dbuf) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); - return (GUFCAT(GUF_CNT_NAME, _iter)) { + GUF_ASSERT_RELEASE(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); + return (GUF_CAT(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_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _iter_next)(const GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) it, ptrdiff_t step) { - GUF_ASSERT(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + GUF_ASSERT(GUF_CAT(GUF_CNT_NAME, _valid)(dbuf)); - GUF_CNT_T *begin_ptr = dbuf->data; - GUF_CNT_T *end_ptr = dbuf->data ? dbuf->data + dbuf->size : NULL; + GUF_T *end_ptr = dbuf->data ? dbuf->data + dbuf->size : NULL; if (!dbuf->size || !dbuf->data || !it.ptr) { it.ptr = end_ptr; @@ -643,35 +810,42 @@ static inline GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _iter_next)(const 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); + ptrdiff_t it_idx = 0; + if (it.ptr == end_ptr && it.reverse) { // Handle rend() + it_idx = step - 1; + } else if (it.ptr == end_ptr && !it.reverse) { // Handle end() + it_idx = dbuf->size + step; } else { - next_ptr = (intptr_t)it.ptr + step * sizeof(GUF_CNT_T); + GUF_ASSERT_RELEASE(it.ptr >= dbuf->data && it.ptr < end_ptr && (ptrdiff_t)it.ptr - (ptrdiff_t)dbuf->data >= 0) + it_idx = (ptrdiff_t)(it.ptr - dbuf->data) + step; // TODO: is this legal? } - if (next_ptr >= (intptr_t)end_ptr) { + const ptrdiff_t len = end_ptr && dbuf->data ? end_ptr - dbuf->data : 0; + GUF_ASSERT_RELEASE(len >= 0); + + if (len == 0) { it.ptr = end_ptr; - } else if (next_ptr < (intptr_t)dbuf->data) { + } else if (it_idx >= len) { + it.ptr = end_ptr; + } else if (it_idx < 0) { 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; + GUF_ASSERT(dbuf->data && it.ptr); + it.ptr = dbuf->data + it_idx; } 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) + +#if defined(GUF_T_IS_INTEGRAL_TYPE) || defined(GUF_T_EQ) +GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find)(GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) begin, GUF_CAT(GUF_CNT_NAME, _iter) end, const GUF_T *needle) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + GUF_ASSERT_RELEASE(GUF_CAT(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)) { + for (GUF_CAT(GUF_CNT_NAME, _iter) it = begin; it.ptr != end.ptr; it = GUF_CAT(GUF_CNT_NAME, _iter_next)(dbuf, it, 1)) { + #ifdef GUF_T_EQ + if (GUF_T_EQ(it.ptr, needle)) { return it; } #else @@ -680,35 +854,43 @@ GUFCAT(GUF_CNT_NAME, _iter) GUFCAT(GUF_CNT_NAME, _find)(GUF_CNT_NAME *dbuf, GUFC } #endif } - return GUFCAT(GUF_CNT_NAME, _end)(dbuf); + return GUF_CAT(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) +GUF_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find_val)(GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) begin, GUF_CAT(GUF_CNT_NAME, _iter) end, GUF_T needle_val) { - return GUFCAT(GUF_CNT_NAME, _find)(dbuf, begin, end, &needle_val); + return GUF_CAT(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_FN_KEYWORDS GUF_CAT(GUF_CNT_NAME, _iter) GUF_CAT(GUF_CNT_NAME, _find_if)(GUF_CNT_NAME *dbuf, GUF_CAT(GUF_CNT_NAME, _iter) *begin, GUF_CAT(GUF_CNT_NAME, _iter) *end, bool (*predicate)(const GUF_T *)) { - GUF_ASSERT_RELEASE(GUFCAT(GUF_CNT_NAME, _valid)(dbuf)); + GUF_ASSERT_RELEASE(GUF_CAT(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)) { + for (GUF_CAT(GUF_CNT_NAME, _iter) it = *begin; it.ptr != end->ptr; GUF_CAT(GUF_CNT_NAME, _iter_next)(dbuf, it, 1)) { if (predicate(it.ptr)) { return it; } } - return GUFCAT(GUF_CNT_NAME, _end)(dbuf); + return GUF_CAT(GUF_CNT_NAME, _end)(dbuf); } #endif +#endif /* end #ifdef GUF_IMPL */ + #undef GUF_DBUF_INITIAL_CAP #undef GUF_DBUF_USE_GROWTH_FAC_ONE_POINT_FIVE #undef GUF_CNT_NAME -#undef GUF_CNT_T -#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 +#undef GUF_CNT_WITH_ELEM_CTX + +#undef GUF_T +#undef GUF_T_COPY +#undef GUF_T_MOVE +#undef GUF_T_FREE +#undef GUF_T_EQ +#undef GUF_T_IS_INTEGRAL_TYPE + +#undef GUF_FN_KEYWORDS +#undef GUF_IMPL +#undef GUF_IMPL_STATIC diff --git a/src/guf_dict.h b/src/guf_dict.h index 4e9f50e..03cef6d 100755 --- a/src/guf_dict.h +++ b/src/guf_dict.h @@ -1,10 +1,7 @@ #ifndef GUF_DICT_H #define GUF_DICT_H - -#include -#include -#include #include "guf_common.h" +#include "guf_hash.h" typedef enum guf_dict_probe_type {GUF_DICT_PROBE_LINEAR = 0, GUF_DICT_PROBE_QUADRATIC} guf_dict_probe_type; @@ -108,4 +105,4 @@ extern const guf_dict_kv_funcs GUF_DICT_FUNCS_float; extern const guf_dict_kv_funcs GUF_DICT_FUNCS_double; extern const guf_dict_kv_funcs GUF_DICT_FUNCS_NULL; -#endif \ No newline at end of file +#endif diff --git a/src/guf_dict_impls.c b/src/guf_dict_impls.c deleted file mode 100644 index f513ec8..0000000 --- a/src/guf_dict_impls.c +++ /dev/null @@ -1,348 +0,0 @@ -#include -#include -#include -#include - -#include "guf_common.h" -#include "guf_dict.h" -#include "guf_str.h" - -// Guf_str -static bool guf_dict_eq_guf_str(const void *guf_str_a, const void *guf_str_b) -{ - return guf_str_equals((const guf_str*)guf_str_a, (const guf_str*)guf_str_b); -} - -static guf_hash_size_t guf_dict_hash_guf_str(const void *str) -{ - const char *c_str = guf_str_get_const_c_str_non_zero_term((const guf_str*)str); - size_t num_bytes = guf_str_len((const guf_str*)str); - return guf_hash(c_str, num_bytes, GUF_HASH_INIT); -} - -static void *guf_dict_cpy_guf_str(void *dst, const void *key_or_val) -{ - GUF_ASSERT_RELEASE(dst && key_or_val); - const guf_str *src_ptr = (guf_str*)key_or_val; - guf_str *dst_ptr = (guf_str*)dst; - guf_str cpy = guf_str_cpy(src_ptr, false); - *dst_ptr = cpy; - return dst_ptr; -} - -static void *guf_dict_move_guf_str(void *dst, void *key_or_val) -{ - GUF_ASSERT_RELEASE(dst && key_or_val); - if (guf_str_is_view(key_or_val) && !guf_str_is_stack_allocated(key_or_val)) { - printf("gufdictmove nulllllll\n"); - return NULL; - } - - guf_str *src_ptr = (guf_str*)key_or_val; - guf_str *dst_ptr = (guf_str*)dst; - guf_str moved = guf_str_move(src_ptr); - *dst_ptr = moved; - return dst_ptr; -} - -static void guf_dict_free_guf_str(void *key_or_val) -{ - GUF_ASSERT_RELEASE(key_or_val); - guf_str_free((guf_str*)key_or_val); -} - - - -// Regular cstr -static bool guf_dict_eq_cstr(const void *str_a, const void *str_b) -{ - return 0 == strcmp(*(const char**)str_a, *(const char**)str_b); -} - -static guf_hash_size_t guf_dict_hash_cstr(const void *str) -{ - size_t num_bytes = strlen(*(const char **)str); - return guf_hash(*(const char**)str, num_bytes, GUF_HASH_INIT); -} - -static void *guf_dict_cpy_cstr(void *dst, const void *key_or_val) -{ - GUF_ASSERT_RELEASE(dst && key_or_val); - char **dst_ptr = (char**)dst; - *dst_ptr = strdup(*(const char**)key_or_val); - GUF_ASSERT(*dst_ptr); - return dst_ptr; -} - -static void *guf_dict_move_cstr(void *dst, void *src) -{ - GUF_ASSERT_RELEASE(dst && src); - char **dst_ptr = (char**)dst; - char **src_ptr = (char**)src; - *dst_ptr = *src_ptr; - *src_ptr = NULL; - return dst_ptr; -} - -static void guf_dict_free_cstr(void *key_or_val) -{ - GUF_ASSERT_RELEASE(key_or_val); - free(*(char**)key_or_val); -} - - -// Signed ints. - -static guf_hash_size_t guf_dict_hash_i8(const void *n) -{ - if (!guf_is_big_endian()) { - return guf_hash(n, sizeof(int8_t), GUF_HASH_INIT); - } else { - const unsigned char *bytes = (const unsigned char*)n; - unsigned char bytes_reversed[sizeof(int8_t)]; - for (size_t i = 0; i < sizeof(bytes); ++i) { - bytes_reversed[i] = bytes[sizeof(bytes) - 1 - i]; - } - return guf_hash(bytes_reversed, sizeof(int8_t), GUF_HASH_INIT); - - } -} -static guf_hash_size_t guf_dict_hash_i16(const void *n) -{ - if (!guf_is_big_endian()) { - return guf_hash(n, sizeof(int16_t), GUF_HASH_INIT); - } else { - const unsigned char *bytes = (const unsigned char*)n; - unsigned char bytes_reversed[sizeof(int16_t)]; - for (size_t i = 0; i < sizeof(bytes); ++i) { - bytes_reversed[i] = bytes[sizeof(bytes) - 1 - i]; - } - return guf_hash(bytes_reversed, sizeof(int16_t), GUF_HASH_INIT); - - } -} -static guf_hash_size_t guf_dict_hash_i32(const void *n) -{ - if (!guf_is_big_endian()) { - return guf_hash(n, sizeof(int32_t), GUF_HASH_INIT); - } else { - const unsigned char *bytes = (const unsigned char*)n; - unsigned char bytes_reversed[sizeof(int32_t)]; - for (size_t i = 0; i < sizeof(bytes); ++i) { - bytes_reversed[i] = bytes[sizeof(bytes) - 1 - i]; - } - return guf_hash(bytes_reversed, sizeof(int32_t), GUF_HASH_INIT); - - } -} -static guf_hash_size_t guf_dict_hash_i64(const void *n) -{ - if (!guf_is_big_endian()) { - return guf_hash(n, sizeof(int64_t), GUF_HASH_INIT); - } else { - const unsigned char *bytes = (const unsigned char*)n; - unsigned char bytes_reversed[sizeof(int64_t)]; - for (size_t i = 0; i < sizeof(bytes); ++i) { - bytes_reversed[i] = bytes[sizeof(bytes) - 1 - i]; - } - return guf_hash(bytes_reversed, sizeof(int64_t), GUF_HASH_INIT); - - } -} - -// Unsigned ints. - -static guf_hash_size_t guf_dict_hash_u8(const void *n) -{ - if (!guf_is_big_endian()) { - return guf_hash(n, sizeof(uint8_t), GUF_HASH_INIT); - } else { - const unsigned char *bytes = (const unsigned char*)n; - unsigned char bytes_reversed[sizeof(uint8_t)]; - for (size_t i = 0; i < sizeof(bytes); ++i) { - bytes_reversed[i] = bytes[sizeof(bytes) - 1 - i]; - } - return guf_hash(bytes_reversed, sizeof(uint8_t), GUF_HASH_INIT); - } -} -static guf_hash_size_t guf_dict_hash_u16(const void *n) -{ - if (!guf_is_big_endian()) { - return guf_hash(n, sizeof(uint16_t), GUF_HASH_INIT); - } else { - const unsigned char *bytes = (const unsigned char*)n; - unsigned char bytes_reversed[sizeof(uint16_t)]; - for (size_t i = 0; i < sizeof(bytes); ++i) { - bytes_reversed[i] = bytes[sizeof(bytes) - 1 - i]; - } - return guf_hash(bytes_reversed, sizeof(uint16_t), GUF_HASH_INIT); - - } -} -static guf_hash_size_t guf_dict_hash_u32(const void *n) -{ - if (!guf_is_big_endian()) { - return guf_hash(n, sizeof(uint32_t), GUF_HASH_INIT); - } else { - const unsigned char *bytes = (const unsigned char*)n; - unsigned char bytes_reversed[sizeof(uint32_t)]; - for (size_t i = 0; i < sizeof(bytes); ++i) { - bytes_reversed[i] = bytes[sizeof(bytes) - 1 - i]; - } - return guf_hash(bytes_reversed, sizeof(uint32_t), GUF_HASH_INIT); - - } -} -static guf_hash_size_t guf_dict_hash_u64(const void *n) -{ - if (!guf_is_big_endian()) { - return guf_hash(n, sizeof(uint64_t), GUF_HASH_INIT); - } else { - const unsigned char *bytes = (const unsigned char*)n; - unsigned char bytes_reversed[sizeof(uint64_t)]; - for (size_t i = 0; i < sizeof(bytes); ++i) { - bytes_reversed[i] = bytes[sizeof(bytes) - 1 - i]; - } - return guf_hash(bytes_reversed, sizeof(uint64_t), GUF_HASH_INIT); - } -} - - -// Floats. - -static guf_hash_size_t guf_dict_hash_float(const void *n) -{ - if (!guf_is_big_endian()) { - return guf_hash(n, sizeof(float), GUF_HASH_INIT); - } else { - const unsigned char *bytes = (const unsigned char*)n; - unsigned char bytes_reversed[sizeof(float)]; - for (size_t i = 0; i < sizeof(bytes); ++i) { - bytes_reversed[i] = bytes[sizeof(bytes) - 1 - i]; - } - return guf_hash(bytes_reversed, sizeof(float), GUF_HASH_INIT); - } -} -static guf_hash_size_t guf_dict_hash_double(const void *n) -{ - if (!guf_is_big_endian()) { - return guf_hash(n, sizeof(double), GUF_HASH_INIT); - } else { - const unsigned char *bytes = (const unsigned char*)n; - unsigned char bytes_reversed[sizeof(double)]; - for (size_t i = 0; i < sizeof(bytes); ++i) { - bytes_reversed[i] = bytes[sizeof(bytes) - 1 - i]; - } - return guf_hash(bytes_reversed, sizeof(double), GUF_HASH_INIT); - } -} - -const guf_dict_kv_funcs GUF_DICT_FUNCS_CSTR = { - .type_size = sizeof(char*), - .eq = guf_dict_eq_cstr, - .hash = guf_dict_hash_cstr, - .cpy = guf_dict_cpy_cstr, - .move = guf_dict_move_cstr, - .free = guf_dict_free_cstr, -}; - -const guf_dict_kv_funcs GUF_DICT_FUNCS_GUF_STR = { - .type_size = sizeof(guf_str), - .eq = guf_dict_eq_guf_str, - .hash = guf_dict_hash_guf_str, - .cpy = guf_dict_cpy_guf_str, - .move = guf_dict_move_guf_str, - .free = guf_dict_free_guf_str, -}; - -const guf_dict_kv_funcs GUF_DICT_FUNCS_i8 = { - .type_size = sizeof(int8_t), - .eq = NULL, - .hash = guf_dict_hash_i8, - .cpy = NULL, - .move = NULL, - .free = NULL, -}; -const guf_dict_kv_funcs GUF_DICT_FUNCS_i16 = { - .type_size = sizeof(int16_t), - .eq = NULL, - .hash = guf_dict_hash_i16, - .cpy = NULL, - .move = NULL, - .free = NULL, -}; -const guf_dict_kv_funcs GUF_DICT_FUNCS_i32 = { - .type_size = sizeof(int32_t), - .eq = NULL, - .hash = guf_dict_hash_i32, - .cpy = NULL, - .move = NULL, - .free = NULL, -}; -const guf_dict_kv_funcs GUF_DICT_FUNCS_i64 = { - .type_size = sizeof(int64_t), - .eq = NULL, - .hash = guf_dict_hash_i64, - .cpy = NULL, - .move = NULL, - .free = NULL, -}; - -const guf_dict_kv_funcs GUF_DICT_FUNCS_u8 = { - .type_size = sizeof(uint8_t), - .eq = NULL, - .hash = guf_dict_hash_u8, - .cpy = NULL, - .move = NULL, - .free = NULL, -}; -const guf_dict_kv_funcs GUF_DICT_FUNCS_u16 = { - .type_size = sizeof(uint16_t), - .eq = NULL, - .hash = guf_dict_hash_u16, - .cpy = NULL, - .move = NULL, - .free = NULL, -}; -const guf_dict_kv_funcs GUF_DICT_FUNCS_u32 = { - .type_size = sizeof(uint32_t), - .eq = NULL, - .hash = guf_dict_hash_u32, - .cpy = NULL, - .move = NULL, - .free = NULL, -}; -const guf_dict_kv_funcs GUF_DICT_FUNCS_u64 = { - .type_size = sizeof(uint64_t), - .eq = NULL, - .hash = guf_dict_hash_u64, - .cpy = NULL, - .move = NULL, - .free = NULL, -}; - -const guf_dict_kv_funcs GUF_DICT_FUNCS_float = { - .type_size = sizeof(float), - .eq = NULL, - .hash = guf_dict_hash_float, - .cpy = NULL, - .move = NULL, - .free = NULL, -}; -const guf_dict_kv_funcs GUF_DICT_FUNCS_double = { - .type_size = sizeof(double), - .eq = NULL, - .hash = guf_dict_hash_double, - .cpy = NULL, - .move = NULL, - .free = NULL, -}; - -const guf_dict_kv_funcs GUF_DICT_FUNCS_NULL = { - .type_size = 0, - .eq = NULL, - .hash = NULL, - .cpy = NULL, - .move = NULL, - .free = NULL, -}; diff --git a/src/guf_hash.h b/src/guf_hash.h index 3929e81..487f81f 100644 --- a/src/guf_hash.h +++ b/src/guf_hash.h @@ -1,6 +1,7 @@ #ifndef GUF_HASH_H #define GUF_HASH_H #include "guf_common.h" +#include "guf_assert.h" // #define GUF_USE_32_BIT_HASH /* Define GUF_USE_32_BIT_HASH to make guflib use 32 bit hashes */ @@ -39,7 +40,7 @@ static inline uint64_t guf_hash64(const void *data, ptrdiff_t num_bytes, uint64_ return hash; } -#ifdef GUF_USE_32_BIT_HASH +#ifdef GUF_HASH_USE_32_BIT typedef uint32_t guf_hash_size_t; #define GUF_HASH_INIT GUF_HASH32_INIT @@ -56,4 +57,4 @@ static inline uint64_t guf_hash64(const void *data, ptrdiff_t num_bytes, uint64_ #define GUF_HASH_MAX UINT64_MAX #endif -#endif \ No newline at end of file +#endif diff --git a/src/guf_init.h b/src/guf_init.h new file mode 100644 index 0000000..f795a2a --- /dev/null +++ b/src/guf_init.h @@ -0,0 +1,21 @@ +#ifndef GUF_INIT_H +#define GUF_INIT_H + +// Set up the global panic handler. +#define GUF_INIT +#include "guf_assert.h" + +// static inline bool guf_init(void) +// { +// static bool guf_is_init = false; + +// if (guf_is_init) { +// printf("libguf already initialised\n"); +// return true; +// } + +// guf_is_init = true; +// return guf_is_init; +// } + +#endif diff --git a/src/guf_int.h b/src/guf_int.h index 7961a01..85d3efd 100644 --- a/src/guf_int.h +++ b/src/guf_int.h @@ -1,11 +1,12 @@ #ifndef GUF_INT_H #define GUF_INT_H #include "guf_common.h" +#include "guf_assert.h" #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);} + static inline int_type GUF_CAT(guf_min_, int_type_name)(int_type a, int_type b) {return a >= b ? a : b;}\ + static inline int_type GUF_CAT(guf_max_, int_type_name)(int_type a, int_type b) {return a >= b ? a : b;}\ + static inline int_type GUF_CAT(guf_clamp_, int_type_name)(int_type x, int_type min, int_type max) {return GUF_CAT(guf_max_, int_type_name)(GUF_CAT(guf_min_, int_type_name)(x, max), min);} GUF_DEFINE_MIN_MAX_CLAMP(char, char) GUF_DEFINE_MIN_MAX_CLAMP(int, int) @@ -40,4 +41,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;} -#endif \ No newline at end of file +#endif diff --git a/src/guf_obj.h b/src/guf_obj.h deleted file mode 100644 index c8e76c4..0000000 --- a/src/guf_obj.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef GUF_OBJ_H -#define GUF_OBJ_H - #include - #include "guf_common.h" -#endif - -/* - Required template parameters: - - GUF_OBJ_TYPE: The value type of the object for which to define the operations struct. - - Optional temlate parameters: - - 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_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 *(*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 index ffc8800..495d089 100644 --- a/src/guf_sort.h +++ b/src/guf_sort.h @@ -1,34 +1,42 @@ #ifndef GUF_SORT_H #define GUF_SORT_H -#include #include "guf_common.h" + +typedef enum guf_sort_opt { + GUF_SORT_ASCENDING = 0, + GUF_SORT_DESCENDING = 1 +} guf_sort_opt; + #endif -#ifndef GUF_OBJ_TYPE - #error "GUF_OBJ_TYPE not set" +#ifdef GUF_T + +#ifndef GUF_T_NAME + #define GUF_T_NAME GUF_T #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)) +#ifdef GUF_IMPL_STATIC + #define GUF_FN_KEYWORDS static #else + #define GUF_FN_KEYWORDS +#endif + +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_T_NAME, _insertion_sort)(GUF_T *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_T *a, const GUF_T *b)); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_T_NAME, _merge_sort)(GUF_T *restrict arr, GUF_T *restrict arr_tmp, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_T *a, const GUF_T *b)); +GUF_FN_KEYWORDS GUF_T *GUF_CAT(GUF_T_NAME, _qsort)(GUF_T *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_T *a, const GUF_T *b)); + +#if defined(GUF_IMPL) || defined(GUF_IMPL_STATIC) #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) + - time: worst O(n^2); average O(n^2); best O(n) (if arr is already sorted) - 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_FN_KEYWORDS GUF_T *GUF_CAT(GUF_T_NAME, _insertion_sort)(GUF_T *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_T *a, const GUF_T *b)) { GUF_ASSERT_RELEASE(arr); GUF_ASSERT_RELEASE(n >= 0); @@ -36,7 +44,7 @@ GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _insertion_sort)(GUF_OBJ 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]); + GUF_SWAP(GUF_T, arr[j], arr[j - 1]); j = j - 1; } } @@ -47,17 +55,17 @@ GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _insertion_sort)(GUF_OBJ 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) + - space: always O(n) (for arr_tmp, allocated and freed by the caller) */ -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_T *GUF_CAT(GUF_T_NAME, _merge_sort)(GUF_T *restrict arr, GUF_T *restrict arr_tmp, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_T *a, const GUF_T *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; + GUF_T *in = arr; + GUF_T *out = arr_tmp; + const 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) @@ -72,51 +80,53 @@ GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _merge_sort)(GUF_OBJ_TYP } } } - GUF_SWAP(GUF_OBJ_TYPE*, in, out); // Avoid copying memory by switching pointers. + GUF_SWAP(GUF_T*, in, out); // Avoid copying memory by switching pointers. } if (in != arr) { - memcpy(arr, in, sizeof(GUF_OBJ_TYPE) * arr_len); + memcpy(arr, in, sizeof(GUF_T) * arr_len); } return arr; } /* - Quicksort implementation with "Median-of-3 partition" (pp. 188-189) as described in CLRS (plus potential bugs introduced by me). + Quicksort with "Median-of-3" partitioning (non-randomised) and minimal O(log n) stack usage as described in CLRS. cf. Cormen; Leiserson; Riverst; Stein (2009). "II.7 Quicksort". In "Introduction to Algorithms" (3rd ed, pp. 170-190). MIT Press + cf. https://en.wikipedia.org/wiki/Quicksort#Choice_of_pivot + cf. https://en.wikipedia.org/wiki/Quicksort#Optimizations - 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.) + - time: worst O(n^2); average O(n * log n); best O(n * log n) + - space: worst O(log n) (stack space used for the recursive function calls) */ -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)) +static GUF_T *GUF_CAT(GUF_T_NAME, _qsort_range)(GUF_T *arr, ptrdiff_t first_idx, ptrdiff_t last_idx, guf_sort_opt sort_opt, int(*cmp)(const GUF_T *a, const GUF_T *b)) { GUF_ASSERT(arr); GUF_ASSERT(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING); while (first_idx >= 0 && first_idx < last_idx) { + // 1.) Partition using "Median-of-3 partitioning", cf. https://en.wikipedia.org/wiki/Quicksort#Choice_of_pivot (last-retrieved 2025-01-30) 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]); + GUF_SWAP(GUF_T, 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]); + GUF_SWAP(GUF_T, 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]); + GUF_SWAP(GUF_T, 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_SWAP(GUF_T, 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). + GUF_SWAP(GUF_T, 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. @@ -128,17 +138,17 @@ static inline GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _qsort_range)(GUF_OBJ_TYPE (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); + GUF_CAT(GUF_T_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); + GUF_CAT(GUF_T_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_FN_KEYWORDS GUF_T *GUF_CAT(GUF_T_NAME, _qsort)(GUF_T *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_T *a, const GUF_T *b)) { GUF_ASSERT_RELEASE(arr); GUF_ASSERT_RELEASE(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING); @@ -146,15 +156,20 @@ GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _qsort)(GUF_OBJ_TYPE *ar if (n <= 1) { return arr; } else if (n <= 4) { - return GUFCAT(GUF_OBJ_TYPE_NAME, _insertion_sort)(arr, n, sort_opt, cmp); + return GUF_CAT(GUF_T_NAME, _insertion_sort)(arr, n, sort_opt, cmp); } else { - return GUFCAT(GUF_OBJ_TYPE_NAME, _qsort_range)(arr, 0, n - 1, sort_opt, cmp); + return GUF_CAT(GUF_T_NAME, _qsort_range)(arr, 0, n - 1, sort_opt, cmp); } } #undef guf_before #undef guf_before_or_equal -#endif +#endif /* end #ifdef GUF_IMPL */ -#undef GUF_OBJ_TYPE -#undef GUF_OBJ_TYPE_NAME \ No newline at end of file +#undef GUF_T +#undef GUF_T_NAME +#endif /* end #ifdef GUF_T */ + +#undef GUF_IMPL +#undef GUF_IMPL_STATIC +#undef GUF_FN_KEYWORDS diff --git a/src/guf_str.c b/src/guf_str.c index 375fb4b..fcbe932 100644 --- a/src/guf_str.c +++ b/src/guf_str.c @@ -40,7 +40,8 @@ static inline void set_len(guf_str *str, size_t len) GUF_ASSERT(str); if (is_short(str)) { GUF_ASSERT(len <= GUF_STR_SSO_BUFSIZE); - str->data.stack.len = len; + GUF_ASSERT(len <= UCHAR_MAX); + str->data.stack.len = (unsigned char)len; } else { GUF_ASSERT(len <= str->data.heap.capacity); str->data.heap.len = len; @@ -362,7 +363,8 @@ guf_str *guf_str_substr(guf_str* str, size_t pos, size_t count) if (is_short(str)) { // a) Short string (stack). GUF_ASSERT(pos + substr_len <= GUF_STR_SSO_BUFCAP); - str->data.stack.len = substr_len; + GUF_ASSERT(substr_len <= UCHAR_MAX); + str->data.stack.len = (unsigned char)substr_len; memcpy(str->data.stack.c_str, c_str + pos, substr_len); str->data.stack.c_str[substr_len] = '\0'; set_len(str, substr_len); @@ -441,7 +443,8 @@ guf_str *guf_str_shrink_to_fit(guf_str *str) GUF_ASSERT(src); str->data.heap.c_str = NULL; set_flag(str, GUF_STR_STATE_SHORT); - str->data.stack.len = len; + GUF_ASSERT(len < UCHAR_MAX); + str->data.stack.len = (unsigned char)len; memcpy(str->data.stack.c_str, src, len); str->data.stack.c_str[len] = '\0'; free(src); diff --git a/src/guf_str.h b/src/guf_str.h index 74da0a6..95ca926 100644 --- a/src/guf_str.h +++ b/src/guf_str.h @@ -1,34 +1,27 @@ #ifndef GUF_STR_H #define GUF_STR_H - -#include -#include - #include "guf_common.h" #include "guf_alloc.h" -#define GUF_CNT_T char -#define GUF_CNT_NAME guf_dbuf_char -#include "guf_dbuf.h" +// #define GUF_T char +// #define GUF_CNT_NAME guf_dbuf_char +// #define GUF_T_IS_INTEGRAL_TYPE +// #include "guf_dbuf.h" #define GUF_STR_ABORT_ON_ALLOC_FAILURE 1 -// TODO: don't allocate self but take allocator? - typedef enum guf_str_state { GUF_STR_STATE_INIT = 0, GUF_STR_STATE_SHORT = 1, - GUF_STR_STATE_READONLY = 2, + GUF_STR_STATE_VIEW = 2, GUF_STR_STATE_ALLOC_ERR = 4 } guf_str_state; typedef struct guf_str { - guf_str_state state; union { 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)) @@ -37,7 +30,8 @@ typedef struct guf_str { unsigned char len; } stack; } data; - + guf_allocator *allocator; + guf_str_state state; } guf_str; typedef struct guf_str_view { @@ -45,8 +39,9 @@ typedef struct guf_str_view { size_t len; } guf_str_view; -#define GUF_CSTR_TO_VIEW(C_STR_PTR) ((guf_str_view){.str = (C_STR_PTR), .len = strlen((C_STR_PTR))}) +#define GUF_CSTR_TO_VIEW(CSTR) ((guf_str_view){.str = (CSTR), .len = strlen((CSTR))}) #define GUF_STR_TO_VIEW(GUF_STR_PTR) ((guf_str_view){.str = guf_str_const_cstr((GUF_STR_PTR)), .len = guf_str_len((GUF_STR_PTR))}) +#define GUF_CSTR_TO_READONLY_STR(CSTR) ((guf_str){.state = GUF_STR_STATE_VIEW, .allocator = NULL, .data.heap.c_str = CSTR, .data.heap.len = strlen(CSTR), .data.heap.capacity = 0}) extern const guf_str GUF_STR_UNINITIALISED; extern const guf_str GUF_STR_UNINITIALISED_FAILED_ALLOC; @@ -106,4 +101,4 @@ int guf_str_view_cmp(const void *str_view_a, const void *str_view_b); // For qso bool guf_str_char_is_ascii(char c); bool guf_str_is_ascii(const guf_str *str); -#endif \ No newline at end of file +#endif diff --git a/src/guf_test.c b/src/guf_test.c index c2e2465..1c1faae 100644 --- a/src/guf_test.c +++ b/src/guf_test.c @@ -2,57 +2,65 @@ #include #include -#define GUF_IMPLEMENTATION -#include "guf_common.h" -#undef GUF_IMPLEMENTATION +#include "guf_init.h" /* Must be included once (sets up the global panic handler) */ +#include "guf_alloc_libc.h" #include "guf_cstr.h" +#define GUF_IMPL_STATIC +#define GUF_T float +#include "guf_sort.h" + #define GUF_CNT_NAME dbuf_int -#define GUF_CNT_T int -#define GUF_CNT_T_IS_INTEGRAL_TYPE +#define GUF_T int +#define GUF_T_IS_INTEGRAL_TYPE +#define GUF_IMPL_STATIC #include "guf_dbuf.h" #define GUF_CNT_NAME dbuf_float -#define GUF_CNT_T float -#define GUF_CNT_T_IS_INTEGRAL_TYPE +#define GUF_T float +#define GUF_T_IS_INTEGRAL_TYPE +#define GUF_IMPL_STATIC #include "guf_dbuf.h" -#define GUF_OBJ_TYPE float -#include "guf_sort.h" - -#define GUF_CNT_T guf_heap_cstr +#define GUF_T guf_cstr_heap #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 +#define GUF_T_COPY guf_cstr_heap_copy +#define GUF_T_MOVE guf_cstr_heap_move +#define GUF_T_FREE guf_cstr_heap_free +#define GUF_T_EQ guf_cstr_heap_eq +#define GUF_IMPL_STATIC +// #define GUF_CNT_WITH_ELEM_CTX #include "guf_dbuf.h" -#define GUF_CNT_T guf_const_cstr +#define GUF_T guf_cstr_const #define GUF_CNT_NAME dbuf_const_cstr -#define GUF_CNT_T_EQ guf_const_cstr_eq +#define GUF_T_EQ guf_cstr_const_eq +#define GUF_IMPL_STATIC #include "guf_dbuf.h" int main(void) { - bool success = true; + printf("libguf test: compiled with C %ld\n", __STDC_VERSION__); + guf_allocator test_allocator = guf_allocator_libc; + guf_libc_alloc_ctx test_allocator_ctx = {.alloc_type_id = 0, .thread_id = 0, .zero_init = true}; + test_allocator.ctx = &test_allocator_ctx; - 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_CNT_LIFETIME_BLOCK(dbuf_float, floats, { + floats = dbuf_float_new(&guf_allocator_libc); + + for (int i = 0; i <= 16; ++i) { + dbuf_float_push_val(&floats, i % 2 ? i * -2.f : i * 2.f); } - // float *tmp = calloc(floats.size, sizeof(float)); - // float *res = float_merge_sort(floats.data, tmp, floats.size, GUF_SORT_DESCENDING, NULL); - // free(tmp); + // float *tmp = test_allocator.alloc(floats.size * sizeof(float), &test_allocator_ctx); + // float *res = float_merge_sort(floats.data, tmp, floats.size, GUF_SORT_ASCENDING, NULL); + // test_allocator.free(tmp, floats.size * sizeof(float), &test_allocator_ctx); // GUF_ASSERT_RELEASE(res == floats.data); - // float_qsort(floats.data, floats.size, GUF_SORT_ASCENDING, NULL); - - float_insertion_sort(floats.data, floats.size, GUF_SORT_ASCENDING, NULL); - + float_qsort(floats.data, floats.size, GUF_SORT_ASCENDING, NULL); + GUF_CNT_FOREACH(&floats, dbuf_float, it) { - printf("float: %f\n", *it.ptr); + printf("float: %f\n", (double)*it.ptr); } }) @@ -67,14 +75,14 @@ int main(void) 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); + guf_cstr_heap_free(fnd_it.ptr, NULL); *fnd_it.ptr = strdup("Found!"); } GUF_CNT_FOREACH(&strings, dbuf_heap_cstr, it) { printf("%s\n", *it.ptr); } - dbuf_heap_cstr_free(&strings); + dbuf_heap_cstr_free(&strings, NULL); dbuf_const_cstr const_strings = dbuf_const_cstr_new(&guf_allocator_libc); dbuf_const_cstr_push_val(&const_strings, "Const 1"); @@ -90,11 +98,10 @@ int main(void) GUF_CNT_FOREACH(&const_strings, dbuf_const_cstr, it) { printf("%s\n", *it.ptr); } - dbuf_const_cstr_free(&const_strings); + dbuf_const_cstr_free(&const_strings, NULL); 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); @@ -103,7 +110,7 @@ int main(void) 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()); + printf("%s %s\n", guf_err_to_str(err), GUF_ERR_MSG_EMPTY()); } int i = 0; @@ -135,6 +142,6 @@ int main(void) printf("every other reverse: %d\n", *it.ptr); } - dbuf_int_free(&integers); - return success ? EXIT_SUCCESS : EXIT_FAILURE; -} \ No newline at end of file + dbuf_int_free(&integers, NULL); + return EXIT_SUCCESS; +} diff --git a/src/guf_utils.h b/src/guf_utils.h new file mode 100644 index 0000000..71edee2 --- /dev/null +++ b/src/guf_utils.h @@ -0,0 +1,11 @@ +#ifndef GUF_UTILS_H +#define GUF_UTILS_H + +static inline bool guf_is_big_endian(void) +{ + unsigned i = 1; + const char *bytes = (const char*)&i; + return bytes[0] != 1; +} + +#endif