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,40 +2,32 @@ 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)
set(WARNING_FLAGS_CXX /W4)
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)
check_ipo_supported(RESULT ipo_available)
if (ipo_available AND (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
@ -45,7 +37,21 @@ if (TARGET libguf_test)
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} $<$<CONFIG:Debug>: ${DBG_FLAGS}>)
target_link_options(libguf_test PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>: ${DBG_FLAGS}> )
message(STATUS "Configured libguf_test")
endif()

View File

@ -1,10 +1,9 @@
#ifndef GUF_ALLOC_H
#define GUF_ALLOC_H
#include <stdlib.h>
#include <stddef.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 {
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;
}
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
if (guf_size_t_mul_is_overflow((size_t)count, (size_t)sizeof_elem)) {
return false;
}
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;
}
static inline void guf_free_libc(void *ptr, ptrdiff_t size, void *ctx)
{
(void)ctx;
(void)size;
free(ptr);
return is_safe;
}
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

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
#define GUF_ASSERT_H
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#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,11 +78,10 @@ static inline void guf_err_set_if_not_null(guf_err *err, guf_err err_val)
*err = err_val;
}
}
#endif
#ifdef GUF_IMPLEMENTATION
guf_panic_handler_fn guf_global_panic_handler = guf_panic_handler_default;
const char *guf_err_type_str[] = {
#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",
@ -138,6 +95,56 @@ static inline void guf_err_set_if_not_null(guf_err *err, guf_err err_val)
[GUF_ERR_LOGIC] = "Logic error",
[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

View File

@ -1,56 +1,55 @@
#ifndef GUF_COMMON_H
#define GUF_COMMON_H
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <stdbool.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_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

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
#define GUF_CSTR_H
#include <string.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);
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,7 +58,7 @@ 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);

File diff suppressed because it is too large Load Diff

View File

@ -1,10 +1,7 @@
#ifndef GUF_DICT_H
#define GUF_DICT_H
#include <stddef.h>
#include <stdint.h>
#include <assert.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;

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

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
#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)

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
#define GUF_SORT_H
#include <stddef.h>
#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
#undef GUF_T
#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);
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);

View File

@ -1,34 +1,27 @@
#ifndef GUF_STR_H
#define GUF_STR_H
#include <stddef.h>
#include <stdbool.h>
#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;

View File

@ -2,57 +2,65 @@
#include <string.h>
#include <stdio.h>
#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;
dbuf_int_free(&integers, NULL);
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