Major refactor

This commit is contained in:
jun 2025-01-30 12:25:04 +01:00
parent c9b18a220e
commit 8c324c00f7
20 changed files with 839 additions and 987 deletions

View File

@ -2,39 +2,31 @@ cmake_minimum_required(VERSION 3.12)
set(PROJECT_NAME libguf) set(PROJECT_NAME libguf)
project(${PROJECT_NAME}) 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}) add_library(${PROJECT_NAME} STATIC ${SOURCES})
# target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}}/lib/guf) # target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}}/lib/guf)
# target_include_directories(${PROJECT_NAME} PRIVATE src) # 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_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF) 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) if (TARGET libguf_test)
message("-- Configure libguf_test...") message("-- Configure libguf_test...")
set(CMAKE_DEBUG_POSTFIX _dbg) if (NOT DEFINED MSVC)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) 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)
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)
else () else ()
set(DBG_FLAGS /fsanitize=address) set(WARNING_FLAGS_CXX /W4)
endif() endif ()
target_compile_options(libguf_test PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>: ${DBG_FLAGS}>) # Note: no higher optimisations at all for debugger to work...
target_link_options(libguf_test PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>: ${DBG_FLAGS}> )
include(CheckIPOSupported) include(CheckIPOSupported)
check_ipo_supported(RESULT ipo_available) check_ipo_supported(RESULT ipo_available)
@ -45,7 +37,21 @@ if (TARGET libguf_test)
message(STATUS "LTO disabled") message(STATUS "LTO disabled")
endif() 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} $<$<CONFIG:Debug>: ${DBG_FLAGS}>)
target_link_options(libguf_test PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>: ${DBG_FLAGS}> )
message(STATUS "Configured libguf_test")
endif() endif()

View File

@ -1,10 +1,9 @@
#ifndef GUF_ALLOC_H #ifndef GUF_ALLOC_H
#define GUF_ALLOC_H #define GUF_ALLOC_H
#include <stdlib.h>
#include <stddef.h>
#include "guf_common.h" #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 { typedef struct guf_allocator {
void *(*alloc)(ptrdiff_t size, void *ctx); void *(*alloc)(ptrdiff_t size, void *ctx);
void *(*realloc)(void *ptr, ptrdiff_t old_size, ptrdiff_t new_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; void *ctx;
} guf_allocator; } guf_allocator;
// register_alloc_type(const char *typestr) (starting from 1) typedef enum guf_alloc_fn_type {
// lookup_alloc_typename(ptrdiff_t type_id) 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 static inline bool guf_size_t_mul_is_overflow(size_t a, size_t b)
// type_id = 0 "other"
// keeping track of (heap?) allocs: alloc_map[type_id] = {.size_alloced, .size_freed}
#define GUF_ALLOC_ENABLE_TRACKING
typedef struct guf_alloc_libc_ctx {
bool zero_init;
size_t alloc_id;
// dbuf_alloc_info
} guf_alloc_libc_ctx;
static inline void *guf_alloc_libc(ptrdiff_t size, void *ctx)
{ {
GUF_ASSERT_RELEASE(size >= 0); const size_t c = a * b;
guf_alloc_libc_ctx *alloc_ctx = (guf_alloc_libc_ctx*)ctx; return a != 0 && ((c / a) != b);
if (size == 0) {
return NULL;
} else if (alloc_ctx && alloc_ctx->zero_init) {
return calloc(size, 1);
} else {
return malloc(size);
}
} }
static inline void *guf_realloc_libc(void *ptr, ptrdiff_t old_size, ptrdiff_t new_size, void *ctx) static inline bool guf_size_calc_safe(ptrdiff_t count, ptrdiff_t sizeof_elem, ptrdiff_t *result)
{ {
GUF_ASSERT_RELEASE(ptr); if (count < 0 || sizeof_elem <= 0) {
GUF_ASSERT_RELEASE(old_size >= 0 && new_size >= 0); return false;
guf_alloc_libc_ctx *alloc_ctx = (guf_alloc_libc_ctx*)ctx;
if (old_size == new_size) {
return ptr;
} }
if (guf_size_t_mul_is_overflow((size_t)count, (size_t)sizeof_elem)) {
void *new_ptr = realloc(ptr, new_size); return false;
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; const size_t size = (size_t)count * (size_t)sizeof_elem;
GUF_ASSERT(size != 0);
const bool is_safe = size <= PTRDIFF_MAX;
if (result) {
*result = is_safe ? (ptrdiff_t)size : -1;
}
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 #endif

59
src/guf_alloc_libc.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef GUF_ALLOC_LIBC_H
#define GUF_ALLOC_LIBC_H
#include <memory.h>
#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

13
src/guf_alloc_tracker.c Normal file
View File

@ -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];
// }

View File

@ -1,9 +1,8 @@
#ifndef GUF_ASSERT_H #ifndef GUF_ASSERT_H
#define GUF_ASSERT_H #define GUF_ASSERT_H
#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#include "guf_common_utils.h" #include "guf_common.h"
typedef enum guf_err { typedef enum guf_err {
GUF_ERR_NONE = 0, GUF_ERR_NONE = 0,
@ -21,28 +20,29 @@ typedef enum guf_err {
GUF_ERR_TYPES_NUM GUF_ERR_TYPES_NUM
} guf_err; } guf_err;
typedef void(*guf_panic_handler_fn)(guf_err err, const char *msg); typedef void(*guf_panic_handler_fn)(guf_err err, const char *msg);
extern guf_panic_handler_fn guf_global_panic_handler; 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) 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);
guf_global_panic_handler = panic_handler; extern const char *guf_err_to_str(guf_err err);
}
#define GUF_FILE_LINE_STR() "file '" __FILE__ "' line " GUF_STRINGIFY(__LINE__) #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(msg) msg " (" GUF_FILE_LINE_STR() ")"
#define GUF_ERR_MSG_EMPTY() "(" 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) // Break on debug, cf. https://nullprogram.com/blog/2022/06/26/ (last retrieved 2025-01-07)
#if __GNUC__ || __clang__ #if __GNUC__ || __clang__
#define GUF_DEBUG_BREAK_CODE __builtin_trap() #define GUF_DEBUG_BREAK_CODE __builtin_trap()
#elif _MSC_VER #elif _MSC_VER
#define GUF_DEBUG_BREAK_CODE __debugbreak() #define GUF_DEBUG_BREAK_CODE __debugbreak()
#else #else
#define GUF_DEBUG_BREAK_CODE #define GUF_DEBUG_BREAK_CODE abort()
#endif #endif
#ifndef NDEBUG #ifndef NDEBUG
@ -63,48 +63,6 @@ static inline void guf_set_global_panic_handler(guf_panic_handler_fn panic_handl
} \ } \
} while (0); } 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) static inline void guf_err_set_or_panic(guf_err *err, guf_err err_val, const char *panic_msg)
{ {
if (!err) { if (!err) {
@ -120,11 +78,10 @@ static inline void guf_err_set_if_not_null(guf_err *err, guf_err err_val)
*err = err_val; *err = err_val;
} }
} }
#endif
#ifdef GUF_IMPLEMENTATION #ifdef GUF_INIT
guf_panic_handler_fn guf_global_panic_handler = guf_panic_handler_default; static const char *guf_err_type_str[] = {
const char *guf_err_type_str[] = {
[GUF_ERR_NONE] = "Not an error", [GUF_ERR_NONE] = "Not an error",
[GUF_ERR_UNSPECIFIED] = "Error", [GUF_ERR_UNSPECIFIED] = "Error",
[GUF_ERR_ALLOC_FAIL] = "Allocation error", [GUF_ERR_ALLOC_FAIL] = "Allocation error",
@ -137,7 +94,57 @@ static inline void guf_err_set_if_not_null(guf_err *err, guf_err err_val)
[GUF_ERR_RUNTIME] = "Runtime error", [GUF_ERR_RUNTIME] = "Runtime error",
[GUF_ERR_LOGIC] = "Logic error", [GUF_ERR_LOGIC] = "Logic error",
[GUF_ERR_ASSERT_FAIL] = "Assertion failed" [GUF_ERR_ASSERT_FAIL] = "Assertion failed"
}; };
#endif
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 #endif

View File

@ -1,56 +1,55 @@
#ifndef GUF_COMMON_H #ifndef GUF_COMMON_H
#define GUF_COMMON_H #define GUF_COMMON_H
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdbool.h>
#include <assert.h> #include <limits.h>
#include <inttypes.h>
#include <stddef.h>
#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_VALUE = 0,
GUF_CPY_DEEP = 1, GUF_CPY_DEEP = 1,
GUF_CPY_MOVE = 2, GUF_CPY_MOVE = 2,
} guf_obj_cpy_opt; } guf_cpy_opt;
typedef enum 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);
GUF_SORT_ASCENDING = 0, #define GUF_STATIC_BUF_SIZE(BUF) (sizeof((BUF)) / (sizeof((BUF)[0])))
GUF_SORT_DESCENDING = 1
} guf_sort_opt;
static inline bool guf_is_mul_overflow_size_t(size_t a, size_t b) #define GUF_MIN(X, Y) ((X) <= (Y) ? (X) : (Y))
{ #define GUF_MAX(X, Y) ((X) >= (Y) ? (X) : (Y))
size_t c = a * b; #define GUF_CLAMP(X, MIN, MAX) GUF_MAX(GUF_MIN((X), (MAX)), (MIN))
return a != 0 && ((c / a) != b);
}
static inline size_t guf_safe_mul_size_t(size_t a, size_t 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
GUF_ASSERT_RELEASE(!guf_is_mul_overflow_size_t(a, b)); #define GUF_CAT(a, b) GUF_TOK_CAT(a, b)
return 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) #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))
if (count < 0 || sizeof_elem <= 0) {
return false;
}
if (guf_is_mul_overflow_size_t(count, sizeof_elem)) {
return false;
}
size_t size = (size_t)count * (size_t)sizeof_elem;
return size <= PTRDIFF_MAX;
}
static inline ptrdiff_t guf_safe_size_calc(ptrdiff_t count, ptrdiff_t sizeof_elem) #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))
GUF_ASSERT_RELEASE(count >= 0);
GUF_ASSERT_RELEASE(sizeof_elem > 0); #define GUF_CNT_LIFETIME_BLOCK(GUF_CNT_TYPE, CNT_VARNAME, CODE) do { \
size_t size = guf_safe_mul_size_t(count, sizeof_elem); GUF_CNT_TYPE CNT_VARNAME; \
GUF_ASSERT_RELEASE(size <= PTRDIFF_MAX); CODE; \
return size; GUF_CAT(GUF_CNT_TYPE, _free)(&CNT_VARNAME, NULL); \
} } while (0);
#endif #endif

View File

@ -1,35 +0,0 @@
#ifndef GUF_COMMON_UTILS_H
#define GUF_COMMON_UTILS_H
#include <limits.h>
#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

View File

@ -1,29 +1,29 @@
#ifndef GUF_CSTR_H #ifndef GUF_CSTR_H
#define GUF_CSTR_H #define GUF_CSTR_H
#include <string.h>
#include "guf_common.h" #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);
GUF_ASSERT_RELEASE(*a && *b); GUF_ASSERT_RELEASE(*a && *b);
return strcmp(*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);
GUF_ASSERT_RELEASE(*a && *b); GUF_ASSERT_RELEASE(*a && *b);
return 0 == strcmp(*a, *b); return 0 == strcmp(*a, *b);
} }
typedef char* guf_cstr_heap;
typedef char* guf_heap_cstr; static inline guf_cstr_heap *guf_cstr_heap_copy(guf_cstr_heap *dst, const guf_cstr_heap *src, void *ctx)
static inline guf_heap_cstr *guf_heap_cstr_copy_construct(guf_heap_cstr *dst, const guf_heap_cstr *src)
{ {
(void)ctx;
GUF_ASSERT_RELEASE(dst && src); GUF_ASSERT_RELEASE(dst && src);
if (*src == NULL) { if (*src == NULL) {
*dst = NULL; *dst = NULL;
@ -34,21 +34,23 @@ static inline guf_heap_cstr *guf_heap_cstr_copy_construct(guf_heap_cstr *dst, co
return dst; 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); GUF_ASSERT_RELEASE(dst && src);
*dst = *src; *dst = *src;
*src = NULL; *src = NULL;
return dst; 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); GUF_ASSERT_RELEASE(a);
free(*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); GUF_ASSERT_RELEASE(a && b);
@ -56,7 +58,7 @@ static inline int guf_heap_cstr_cmp(const guf_heap_cstr *a, const guf_heap_cstr
return strcmp(*a, *b); 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);
GUF_ASSERT_RELEASE(*a && *b); GUF_ASSERT_RELEASE(*a && *b);

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,7 @@
#ifndef GUF_DICT_H #ifndef GUF_DICT_H
#define GUF_DICT_H #define GUF_DICT_H
#include <stddef.h>
#include <stdint.h>
#include <assert.h>
#include "guf_common.h" #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; typedef enum guf_dict_probe_type {GUF_DICT_PROBE_LINEAR = 0, GUF_DICT_PROBE_QUADRATIC} guf_dict_probe_type;

View File

@ -1,348 +0,0 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#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,
};

View File

@ -1,6 +1,7 @@
#ifndef GUF_HASH_H #ifndef GUF_HASH_H
#define GUF_HASH_H #define GUF_HASH_H
#include "guf_common.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 */ // #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; return hash;
} }
#ifdef GUF_USE_32_BIT_HASH #ifdef GUF_HASH_USE_32_BIT
typedef uint32_t guf_hash_size_t; typedef uint32_t guf_hash_size_t;
#define GUF_HASH_INIT GUF_HASH32_INIT #define GUF_HASH_INIT GUF_HASH32_INIT

21
src/guf_init.h Normal file
View File

@ -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

View File

@ -1,11 +1,12 @@
#ifndef GUF_INT_H #ifndef GUF_INT_H
#define GUF_INT_H #define GUF_INT_H
#include "guf_common.h" #include "guf_common.h"
#include "guf_assert.h"
#define GUF_DEFINE_MIN_MAX_CLAMP(int_type, int_type_name)\ #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 GUF_CAT(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 GUF_CAT(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_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(char, char)
GUF_DEFINE_MIN_MAX_CLAMP(int, int) GUF_DEFINE_MIN_MAX_CLAMP(int, int)

View File

@ -1,37 +0,0 @@
#ifndef GUF_OBJ_H
#define GUF_OBJ_H
#include <string.h>
#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

View File

@ -1,34 +1,42 @@
#ifndef GUF_SORT_H #ifndef GUF_SORT_H
#define GUF_SORT_H #define GUF_SORT_H
#include <stddef.h>
#include "guf_common.h" #include "guf_common.h"
typedef enum guf_sort_opt {
GUF_SORT_ASCENDING = 0,
GUF_SORT_DESCENDING = 1
} guf_sort_opt;
#endif #endif
#ifndef GUF_OBJ_TYPE #ifdef GUF_T
#error "GUF_OBJ_TYPE not set"
#ifndef GUF_T_NAME
#define GUF_T_NAME GUF_T
#endif #endif
#ifndef GUF_OBJ_TYPE_NAME #ifdef GUF_IMPL_STATIC
#define GUF_OBJ_TYPE_NAME GUF_OBJ_TYPE #define GUF_FN_KEYWORDS static
#endif
#define GUF_FN_KEYWORDS static inline
#ifdef GUF_DECLARATIONS_ONLY
GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _insertion_sort)(GUF_OBJ_TYPE *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b));
GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _merge_sort)(GUF_OBJ_TYPE *arr, GUF_OBJ_TYPE *arr_tmp, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b));
GUF_FN_KEYWORDS GUF_OBJ_TYPE *GUFCAT(GUF_OBJ_TYPE_NAME, _qsort)(GUF_OBJ_TYPE *arr, ptrdiff_t n, guf_sort_opt sort_opt, int(*cmp)(const GUF_OBJ_TYPE *a, const GUF_OBJ_TYPE *b))
#else #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(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)) #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. Insertion sort.
- stable: yes - 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) - 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(arr);
GUF_ASSERT_RELEASE(n >= 0); 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. for (ptrdiff_t i = 1; i < n; ++i) { // Range [0, i) is sorted.
ptrdiff_t j = i; ptrdiff_t j = i;
while (j > 0 && guf_before(&arr[j], &arr[j - 1])) { 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; 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) Iterative bottom-up merge-sort: cf. https://en.wikipedia.org/wiki/Merge_sort#Bottom-up_implementation (last-retrieved: 2025-01-26)
- stable: yes - stable: yes
- time: O(n * log n) (worst, average, and best) - 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(arr);
GUF_ASSERT_RELEASE(n >= 0); GUF_ASSERT_RELEASE(n >= 0);
GUF_ASSERT_RELEASE(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING); GUF_ASSERT_RELEASE(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING);
GUF_OBJ_TYPE *in = arr; GUF_T *in = arr;
GUF_OBJ_TYPE *out = arr_tmp; GUF_T *out = arr_tmp;
size_t arr_len = n; 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 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: for (size_t i = 0; i < arr_len; i = ((i + 2 * len) > i) ? (i + 2 * len) : SIZE_T_MAX) { // For each pair of subarrays of length len:
const size_t left_begin = i; // left subarray: [left_begin, right_begin) const size_t 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) { if (in != arr) {
memcpy(arr, in, sizeof(GUF_OBJ_TYPE) * arr_len); memcpy(arr, in, sizeof(GUF_T) * arr_len);
} }
return arr; 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. 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 - stable: no
- time: worst O(n^2), average O(n * log n), best O(n * log n) - 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.) - 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(arr);
GUF_ASSERT(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING); GUF_ASSERT(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING);
while (first_idx >= 0 && first_idx < last_idx) { 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) 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); GUF_ASSERT(mid_idx >= 0 && mid_idx <= last_idx);
// 1.) Partition
ptrdiff_t pivot_idx = last_idx; ptrdiff_t pivot_idx = last_idx;
if (guf_before(&arr[mid_idx], &arr[first_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])) { 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])) { 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 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 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])) { if (guf_before_or_equal(&arr[j], &arr[pivot_idx])) {
++i; ++i;
GUF_ASSERT(i >= 0 && i < last_idx); 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_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; pivot_idx = i + 1;
// 2.) Sort the two partitions [first_idx, pivot_idx) and (pivot_idx, last_idx] recursively. // 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)) (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. 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; first_idx = pivot_idx + 1;
} else { // b.) Right subarray is smaller than the left subarray -> recur into the smaller right subarray first. } 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; last_idx = pivot_idx - 1;
} }
} }
return arr; 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(arr);
GUF_ASSERT_RELEASE(sort_opt == GUF_SORT_ASCENDING || sort_opt == GUF_SORT_DESCENDING); 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) { if (n <= 1) {
return arr; return arr;
} else if (n <= 4) { } 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 { } 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
#undef guf_before_or_equal #undef guf_before_or_equal
#endif #endif /* end #ifdef GUF_IMPL */
#undef GUF_OBJ_TYPE #undef GUF_T
#undef GUF_OBJ_TYPE_NAME #undef GUF_T_NAME
#endif /* end #ifdef GUF_T */
#undef GUF_IMPL
#undef GUF_IMPL_STATIC
#undef GUF_FN_KEYWORDS

View File

@ -40,7 +40,8 @@ static inline void set_len(guf_str *str, size_t len)
GUF_ASSERT(str); GUF_ASSERT(str);
if (is_short(str)) { if (is_short(str)) {
GUF_ASSERT(len <= GUF_STR_SSO_BUFSIZE); 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 { } else {
GUF_ASSERT(len <= str->data.heap.capacity); GUF_ASSERT(len <= str->data.heap.capacity);
str->data.heap.len = len; 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). if (is_short(str)) { // a) Short string (stack).
GUF_ASSERT(pos + substr_len <= GUF_STR_SSO_BUFCAP); 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); memcpy(str->data.stack.c_str, c_str + pos, substr_len);
str->data.stack.c_str[substr_len] = '\0'; str->data.stack.c_str[substr_len] = '\0';
set_len(str, substr_len); set_len(str, substr_len);
@ -441,7 +443,8 @@ guf_str *guf_str_shrink_to_fit(guf_str *str)
GUF_ASSERT(src); GUF_ASSERT(src);
str->data.heap.c_str = NULL; str->data.heap.c_str = NULL;
set_flag(str, GUF_STR_STATE_SHORT); 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); memcpy(str->data.stack.c_str, src, len);
str->data.stack.c_str[len] = '\0'; str->data.stack.c_str[len] = '\0';
free(src); free(src);

View File

@ -1,34 +1,27 @@
#ifndef GUF_STR_H #ifndef GUF_STR_H
#define GUF_STR_H #define GUF_STR_H
#include <stddef.h>
#include <stdbool.h>
#include "guf_common.h" #include "guf_common.h"
#include "guf_alloc.h" #include "guf_alloc.h"
#define GUF_CNT_T char // #define GUF_T char
#define GUF_CNT_NAME guf_dbuf_char // #define GUF_CNT_NAME guf_dbuf_char
#include "guf_dbuf.h" // #define GUF_T_IS_INTEGRAL_TYPE
// #include "guf_dbuf.h"
#define GUF_STR_ABORT_ON_ALLOC_FAILURE 1 #define GUF_STR_ABORT_ON_ALLOC_FAILURE 1
// TODO: don't allocate self but take allocator?
typedef enum guf_str_state { typedef enum guf_str_state {
GUF_STR_STATE_INIT = 0, GUF_STR_STATE_INIT = 0,
GUF_STR_STATE_SHORT = 1, GUF_STR_STATE_SHORT = 1,
GUF_STR_STATE_READONLY = 2, GUF_STR_STATE_VIEW = 2,
GUF_STR_STATE_ALLOC_ERR = 4 GUF_STR_STATE_ALLOC_ERR = 4
} guf_str_state; } guf_str_state;
typedef struct guf_str { typedef struct guf_str {
guf_str_state state;
union { union {
struct heap { struct heap {
char *c_str; char *c_str;
size_t len, capacity; // len and capacity do not include the null-terminator. size_t len, capacity; // len and capacity do not include the null-terminator.
guf_dbuf_char chars; // TODO
} heap; } heap;
struct stack { // Short-string optimisation. struct stack { // Short-string optimisation.
#define GUF_STR_SSO_BUFSIZE (sizeof(struct heap) - sizeof(unsigned char)) #define GUF_STR_SSO_BUFSIZE (sizeof(struct heap) - sizeof(unsigned char))
@ -37,7 +30,8 @@ typedef struct guf_str {
unsigned char len; unsigned char len;
} stack; } stack;
} data; } data;
guf_allocator *allocator;
guf_str_state state;
} guf_str; } guf_str;
typedef struct guf_str_view { typedef struct guf_str_view {
@ -45,8 +39,9 @@ typedef struct guf_str_view {
size_t len; size_t len;
} guf_str_view; } 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_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;
extern const guf_str GUF_STR_UNINITIALISED_FAILED_ALLOC; extern const guf_str GUF_STR_UNINITIALISED_FAILED_ALLOC;

View File

@ -2,57 +2,65 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#define GUF_IMPLEMENTATION #include "guf_init.h" /* Must be included once (sets up the global panic handler) */
#include "guf_common.h" #include "guf_alloc_libc.h"
#undef GUF_IMPLEMENTATION
#include "guf_cstr.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_NAME dbuf_int
#define GUF_CNT_T int #define GUF_T int
#define GUF_CNT_T_IS_INTEGRAL_TYPE #define GUF_T_IS_INTEGRAL_TYPE
#define GUF_IMPL_STATIC
#include "guf_dbuf.h" #include "guf_dbuf.h"
#define GUF_CNT_NAME dbuf_float #define GUF_CNT_NAME dbuf_float
#define GUF_CNT_T float #define GUF_T float
#define GUF_CNT_T_IS_INTEGRAL_TYPE #define GUF_T_IS_INTEGRAL_TYPE
#define GUF_IMPL_STATIC
#include "guf_dbuf.h" #include "guf_dbuf.h"
#define GUF_OBJ_TYPE float #define GUF_T guf_cstr_heap
#include "guf_sort.h"
#define GUF_CNT_T guf_heap_cstr
#define GUF_CNT_NAME dbuf_heap_cstr #define GUF_CNT_NAME dbuf_heap_cstr
#define GUF_CNT_T_COPY guf_heap_cstr_copy_construct #define GUF_T_COPY guf_cstr_heap_copy
#define GUF_CNT_T_MOVE guf_heap_cstr_move_construct #define GUF_T_MOVE guf_cstr_heap_move
#define GUF_CNT_T_FREE guf_heap_cstr_free #define GUF_T_FREE guf_cstr_heap_free
#define GUF_CNT_T_EQ guf_heap_cstr_eq #define GUF_T_EQ guf_cstr_heap_eq
#define GUF_IMPL_STATIC
// #define GUF_CNT_WITH_ELEM_CTX
#include "guf_dbuf.h" #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_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" #include "guf_dbuf.h"
int main(void) 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, { GUF_CNT_LIFETIME_BLOCK(dbuf_float, floats, {
for (int i = 0; i < 16; ++i) { floats = dbuf_float_new(&guf_allocator_libc);
dbuf_float_push_val(&floats, i % 2 ? i * 2.718f : i * -2.718f);
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 *tmp = test_allocator.alloc(floats.size * sizeof(float), &test_allocator_ctx);
// float *res = float_merge_sort(floats.data, tmp, floats.size, GUF_SORT_DESCENDING, NULL); // float *res = float_merge_sort(floats.data, tmp, floats.size, GUF_SORT_ASCENDING, NULL);
// free(tmp); // test_allocator.free(tmp, floats.size * sizeof(float), &test_allocator_ctx);
// GUF_ASSERT_RELEASE(res == floats.data); // GUF_ASSERT_RELEASE(res == floats.data);
// float_qsort(floats.data, floats.size, GUF_SORT_ASCENDING, NULL); float_qsort(floats.data, floats.size, GUF_SORT_ASCENDING, NULL);
float_insertion_sort(floats.data, floats.size, GUF_SORT_ASCENDING, NULL);
GUF_CNT_FOREACH(&floats, dbuf_float, it) { 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); 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) { 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!"); *fnd_it.ptr = strdup("Found!");
} }
GUF_CNT_FOREACH(&strings, dbuf_heap_cstr, it) { GUF_CNT_FOREACH(&strings, dbuf_heap_cstr, it) {
printf("%s\n", *it.ptr); 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 const_strings = dbuf_const_cstr_new(&guf_allocator_libc);
dbuf_const_cstr_push_val(&const_strings, "Const 1"); dbuf_const_cstr_push_val(&const_strings, "Const 1");
@ -90,11 +98,10 @@ int main(void)
GUF_CNT_FOREACH(&const_strings, dbuf_const_cstr, it) { GUF_CNT_FOREACH(&const_strings, dbuf_const_cstr, it) {
printf("%s\n", *it.ptr); 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 integers = dbuf_int_new(&guf_allocator_libc);
dbuf_int_push_val(&integers, 420); dbuf_int_push_val(&integers, 420);
printf("initial cap %td\n", integers.capacity);
dbuf_int_push_val(&integers, 520); dbuf_int_push_val(&integers, 520);
dbuf_int_push_val(&integers, 620); dbuf_int_push_val(&integers, 620);
dbuf_int_push_val(&integers, 720); dbuf_int_push_val(&integers, 720);
@ -103,7 +110,7 @@ int main(void)
guf_err err; guf_err err;
dbuf_int_try_at(&integers, 16, &err); dbuf_int_try_at(&integers, 16, &err);
if (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; int i = 0;
@ -135,6 +142,6 @@ int main(void)
printf("every other reverse: %d\n", *it.ptr); printf("every other reverse: %d\n", *it.ptr);
} }
dbuf_int_free(&integers); dbuf_int_free(&integers, NULL);
return success ? EXIT_SUCCESS : EXIT_FAILURE; return EXIT_SUCCESS;
} }

11
src/guf_utils.h Normal file
View File

@ -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