Major refactor
This commit is contained in:
parent
c9b18a220e
commit
8c324c00f7
@ -2,39 +2,31 @@ cmake_minimum_required(VERSION 3.12)
|
||||
set(PROJECT_NAME libguf)
|
||||
project(${PROJECT_NAME})
|
||||
|
||||
set(SOURCES src/guf_str.c src/guf_dict.c)
|
||||
set(SOURCES src/guf_str.c)
|
||||
|
||||
add_library(${PROJECT_NAME} STATIC ${SOURCES})
|
||||
# target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}}/lib/guf)
|
||||
# target_include_directories(${PROJECT_NAME} PRIVATE src)
|
||||
|
||||
add_executable(libguf_test ${SOURCES} src/guf_test.c)
|
||||
target_include_directories(libguf_test PRIVATE src)
|
||||
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_C_EXTENSIONS OFF)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD 17)
|
||||
|
||||
if (NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
|
||||
endif ()
|
||||
|
||||
add_executable(libguf_test ${SOURCES} src/guf_test.c)
|
||||
target_include_directories(libguf_test PRIVATE src)
|
||||
|
||||
if (TARGET libguf_test)
|
||||
message("-- Configure libguf_test...")
|
||||
|
||||
set(CMAKE_DEBUG_POSTFIX _dbg)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
|
||||
|
||||
if (APPLE OR UNIX OR LINUX)
|
||||
set(WARNING_FLAGS_C -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wno-newline-eof -Wno-unused-function -Wno-unused-parameter)
|
||||
endif ()
|
||||
|
||||
set_target_properties(libguf_test PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
|
||||
|
||||
if (APPLE OR UNIX OR LINUX)
|
||||
set(DBG_FLAGS -fsanitize=undefined,address -g3 -glldb -Og)
|
||||
if (NOT DEFINED MSVC)
|
||||
set(WARNING_FLAGS_C -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wconversion -Wno-sign-conversion -Wdouble-promotion -Wno-unused-function)
|
||||
else ()
|
||||
set(DBG_FLAGS /fsanitize=address)
|
||||
endif()
|
||||
|
||||
target_compile_options(libguf_test PRIVATE ${WARNING_FLAGS_C} $<$<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}> )
|
||||
set(WARNING_FLAGS_CXX /W4)
|
||||
endif ()
|
||||
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT ipo_available)
|
||||
@ -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()
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
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
|
||||
59
src/guf_alloc_libc.h
Normal file
59
src/guf_alloc_libc.h
Normal 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
13
src/guf_alloc_tracker.c
Normal 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];
|
||||
|
||||
// }
|
||||
121
src/guf_assert.h
121
src/guf_assert.h
@ -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",
|
||||
@ -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_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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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);
|
||||
|
||||
662
src/guf_dbuf.h
662
src/guf_dbuf.h
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
|
||||
|
||||
@ -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,
|
||||
};
|
||||
@ -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
21
src/guf_init.h
Normal 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
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
11
src/guf_utils.h
Normal 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
|
||||
Loading…
x
Reference in New Issue
Block a user