Compare commits

..

10 Commits

Author SHA1 Message Date
zeichensystem
2f50db1858 Fix more GCC warnings 2025-12-21 19:51:42 +01:00
zeichensystem
a964466c8b Fix GCC errors 2025-12-21 18:17:22 +01:00
zeichensystem
45f07986c3 Add string functions 2025-12-21 17:30:13 +01:00
jun
ae83ee66e1 Make guf_utf8_char 4 bytes instead of 5
(Null-termination was superfluous here.)
2025-05-25 16:04:26 +02:00
jun
c54fc75221 Use least vs fast int types 2025-05-19 09:50:05 +02:00
jun
2355feaa70 Use portable guf_math_ckdint functions in guf_rand 2025-05-19 00:48:38 +02:00
jun
096b83638b Fix guf_id_pool integer portability 2025-05-19 00:42:27 +02:00
jun
4217ef6e0b Fix guf_str uninit 2025-05-19 00:12:27 +02:00
jun
57f0e47efc Refactor to use portable minimum-width integers.
The signed and unsigned fixed-width integers (int32_t, uint32_t etc.) are optional
in C99 (and above). Use the non-optional minimum-width integers (int_fast32_t, uint_fast32_t and int_least32_t, uint_least32_t etc.) instead.

To simulate unsigned wrap-around, use the GUF_UWRAP macros in guf_common.h

cf. https://en.cppreference.com/w/c/types/integer (last-retrieved: 2025-05-18)
2025-05-18 22:03:03 +02:00
jun
7ec2af0c33 Add unsigned integer wrapping functions 2025-05-16 13:44:27 +02:00
57 changed files with 4153 additions and 2112 deletions

0
.gitignore vendored Normal file → Executable file
View File

View File

@ -18,9 +18,9 @@ if (NOT DEFINED CMAKE_DEBUG_POSTFIX)
endif () endif ()
if (NOT DEFINED MSVC) if (NOT DEFINED MSVC)
set(WARNING_FLAGS_C -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wstrict-overflow=5 -Wconversion -Wno-sign-conversion -Wsign-promo -Wcast-align -Wcast-qual -Wdouble-promotion -Wformat=2 -Winit-self -Wdisabled-optimization -Wno-unused-function) set(WARNING_FLAGS_C -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wstrict-overflow=5 -Wconversion -Wno-sign-conversion -Wcast-align -Wcast-qual -Wdouble-promotion -Wformat=2 -Winit-self -Wdisabled-optimization -Wno-unused-function)
set(WARNING_FLAGS_CXX -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wstrict-overflow=5 -Wconversion -Wno-sign-conversion -Wsign-promo -Wcast-align -Wcast-qual -Wdouble-promotion -Wformat=2 -Winit-self -Wdisabled-optimization -Woverloaded-virtual -Wredundant-decls -Wctor-dtor-privacy -Wno-unused-function) set(WARNING_FLAGS_CXX -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wstrict-overflow=5 -Wconversion -Wno-sign-conversion -Wsign-promo -Wcast-align -Wcast-qual -Wdouble-promotion -Wformat=2 -Winit-self -Wdisabled-optimization -Woverloaded-virtual -Wredundant-decls -Wctor-dtor-privacy -Wno-unused-function)
set(DBG_FLAGS -fsanitize=undefined,address -g3 -glldb -Og) set(DBG_FLAGS -fsanitize=undefined,address -g3 -Og)
else () else ()
set(WARNING_FLAGS_C /W4) set(WARNING_FLAGS_C /W4)
set(WARNING_FLAGS_CXX /W4) set(WARNING_FLAGS_CXX /W4)
@ -47,10 +47,10 @@ endif()
message(STATUS "Configure libguf_test...") message(STATUS "Configure libguf_test...")
target_compile_definitions(libguf_test PUBLIC TEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/src/test/data/") target_compile_definitions(libguf_test PUBLIC TEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/src/test/data/")
target_compile_options(libguf_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${WARNING_FLAGS_CXX}> $<$<COMPILE_LANGUAGE:C>:${WARNING_FLAGS_C}> $<$<CONFIG:Debug>: ${DBG_FLAGS}>) target_compile_options(libguf_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${WARNING_FLAGS_CXX}> $<$<COMPILE_LANGUAGE:C>:${WARNING_FLAGS_C}> $<$<CONFIG:Debug>: ${DBG_FLAGS}>)
target_link_options(libguf_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${WARNING_FLAGS_CXX}> $<$<COMPILE_LANGUAGE:C>:${WARNING_FLAGS_C}> $<$<CONFIG:Debug>: ${DBG_FLAGS}>) target_link_options(libguf_test PRIVATE $<$<COMPILE_LANGUAGE:CXX>:${WARNING_FLAGS_CXX}> $<$<COMPILE_LANGUAGE:C>:${WARNING_FLAGS_C}> $<$<CONFIG:Debug>: ${DBG_FLAGS}> -lm)
message(STATUS "Configured libguf_test") message(STATUS "Configured libguf_test")
message(STATUS "Configure libguf_example...") message(STATUS "Configure libguf_example...")
target_compile_options(libguf_example PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>: ${DBG_FLAGS}>) target_compile_options(libguf_example PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>: ${DBG_FLAGS}>)
target_link_options(libguf_example PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>: ${DBG_FLAGS}>) target_link_options(libguf_example PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>: ${DBG_FLAGS}> -lm)
message(STATUS "Configured libguf_example") message(STATUS "Configured libguf_example")

BIN
doc/._.DS_Store Executable file

Binary file not shown.

BIN
doc/._guf_dict-diagram.png Executable file

Binary file not shown.

0
doc/guf_dict-diagram.png Normal file → Executable file
View File

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 133 KiB

0
src/guf_alloc.h Normal file → Executable file
View File

3
src/guf_alloc_libc.h Normal file → Executable file
View File

@ -23,7 +23,6 @@ typedef struct guf_libc_alloc_ctx {
#if !defined(GUF_ALLOC_LIBC_IMPL_STATIC) && !defined(GUF_ALLOC_LIBC_IMPL) #if !defined(GUF_ALLOC_LIBC_IMPL_STATIC) && !defined(GUF_ALLOC_LIBC_IMPL)
GUF_ALLOC_LIBC_KWRDS void *guf_libc_alloc(ptrdiff_t size, void *ctx); GUF_ALLOC_LIBC_KWRDS void *guf_libc_alloc(ptrdiff_t size, void *ctx);
GUF_ALLOC_LIBC_KWRDS void *guf_libc_realloc(void *ptr, ptrdiff_t old_size, ptrdiff_t new_size, void *ctx); GUF_ALLOC_LIBC_KWRDS void *guf_libc_realloc(void *ptr, ptrdiff_t old_size, ptrdiff_t new_size, void *ctx);
GUF_ALLOC_LIBC_KWRDS void *guf_libc_realloc(void *ptr, ptrdiff_t old_size, ptrdiff_t new_size, void *ctx);
GUF_ALLOC_LIBC_KWRDS void guf_libc_free(void *ptr, ptrdiff_t size, void *ctx); GUF_ALLOC_LIBC_KWRDS void guf_libc_free(void *ptr, ptrdiff_t size, void *ctx);
GUF_ALLOC_LIBC_KWRDS guf_allocator *guf_libc_allocator_init(guf_allocator *a, guf_libc_alloc_ctx *ctx); GUF_ALLOC_LIBC_KWRDS guf_allocator *guf_libc_allocator_init(guf_allocator *a, guf_libc_alloc_ctx *ctx);
@ -71,7 +70,7 @@ GUF_ALLOC_LIBC_KWRDS void *guf_libc_realloc(void *ptr, ptrdiff_t old_size, ptrdi
const ptrdiff_t len = new_size - old_size; const ptrdiff_t len = new_size - old_size;
GUF_ASSERT(len > 0); GUF_ASSERT(len > 0);
GUF_ASSERT(old_size + len == new_size); GUF_ASSERT(old_size + len == new_size);
memset((char*)ptr + old_size, 0, len); // TODO: sketchy memset((char*)new_ptr + old_size, 0, len); // TODO: sketchy
} }
if (alloc_ctx && alloc_ctx->tracker.enabled) { if (alloc_ctx && alloc_ctx->tracker.enabled) {

44
src/guf_alloc_tracker.h Normal file → Executable file
View File

@ -16,13 +16,13 @@ typedef struct guf_alloc_tracker {
const char *name; const char *name;
size_t alloc_count, realloc_count, free_count; size_t alloc_count, realloc_count, free_count;
ptrdiff_t allocated_bytes; ptrdiff_t allocated_bytes;
uint32_t id; unsigned id;
bool enabled; bool enabled;
} guf_alloc_tracker; } guf_alloc_tracker;
#if !defined(GUF_ALLOC_TRACKER_IMPL_STATIC) && !defined(GUF_ALLOC_TRACKER_IMPL) #if !defined(GUF_ALLOC_TRACKER_IMPL_STATIC) && !defined(GUF_ALLOC_TRACKER_IMPL)
GUF_ALLOC_TRACKER_KWRDS guf_alloc_tracker *guf_alloc_tracker_init(guf_alloc_tracker *t, uint32_t id, const char* name, FILE *log, FILE *err_log); GUF_ALLOC_TRACKER_KWRDS guf_alloc_tracker *guf_alloc_tracker_init(guf_alloc_tracker *t, unsigned id, const char* name, FILE *log, FILE *err_log);
GUF_ALLOC_TRACKER_KWRDS guf_alloc_tracker guf_alloc_tracker_new(uint32_t id, const char* name, FILE *log, FILE *err_log); GUF_ALLOC_TRACKER_KWRDS guf_alloc_tracker guf_alloc_tracker_new(unsigned id, const char* name, FILE *log, FILE *err_log);
GUF_ALLOC_TRACKER_KWRDS void guf_alloc_tracker_print(const guf_alloc_tracker *t, FILE *f); GUF_ALLOC_TRACKER_KWRDS void guf_alloc_tracker_print(const guf_alloc_tracker *t, FILE *f);
GUF_ALLOC_TRACKER_KWRDS bool guf_alloc_tracker_found_leak(const guf_alloc_tracker *t); GUF_ALLOC_TRACKER_KWRDS bool guf_alloc_tracker_found_leak(const guf_alloc_tracker *t);
@ -38,7 +38,7 @@ typedef struct guf_alloc_tracker {
#define GUF_MATH_CKDINT_IMPL_STATIC #define GUF_MATH_CKDINT_IMPL_STATIC
#include "guf_math_ckdint.h" #include "guf_math_ckdint.h"
GUF_ALLOC_TRACKER_KWRDS guf_alloc_tracker *guf_alloc_tracker_init(guf_alloc_tracker *t, uint32_t id, const char* name, FILE *log, FILE *err_log) GUF_ALLOC_TRACKER_KWRDS guf_alloc_tracker *guf_alloc_tracker_init(guf_alloc_tracker *t, unsigned id, const char* name, FILE *log, FILE *err_log)
{ {
GUF_ASSERT_RELEASE(t); GUF_ASSERT_RELEASE(t);
t->log = log; t->log = log;
@ -51,7 +51,7 @@ GUF_ALLOC_TRACKER_KWRDS guf_alloc_tracker *guf_alloc_tracker_init(guf_alloc_trac
return t; return t;
} }
GUF_ALLOC_TRACKER_KWRDS guf_alloc_tracker guf_alloc_tracker_new(uint32_t id, const char* name, FILE *log, FILE *err_log) GUF_ALLOC_TRACKER_KWRDS guf_alloc_tracker guf_alloc_tracker_new(unsigned id, const char* name, FILE *log, FILE *err_log)
{ {
guf_alloc_tracker t; guf_alloc_tracker t;
guf_alloc_tracker_init(&t, id, name, log, err_log); guf_alloc_tracker_init(&t, id, name, log, err_log);
@ -78,7 +78,7 @@ GUF_ALLOC_TRACKER_KWRDS void guf_alloc_tracker_print(const guf_alloc_tracker *t,
} }
fprintf(f, fprintf(f,
"guf_alloc_tracker (name '%s' id = %" PRIu32 "):\n" "guf_alloc_tracker (name '%s' id = %u):\n"
"allocated_bytes: %td\n" "allocated_bytes: %td\n"
"alloc_count: %zu\n" "alloc_count: %zu\n"
"realloc_count: %zu\n" "realloc_count: %zu\n"
@ -96,25 +96,25 @@ GUF_ALLOC_TRACKER_KWRDS bool guf_track_alloc(guf_alloc_tracker *t, ptrdiff_t siz
bool success = true; bool success = true;
if (guf_saturating_add_size_t(t->alloc_count, 1, &t->alloc_count) != GUF_MATH_CKD_SUCCESS && t->err_log) { if (guf_saturating_add_size_t(t->alloc_count, 1, &t->alloc_count) != GUF_MATH_CKD_SUCCESS && t->err_log) {
fprintf(t->err_log, "WARNING in guf_track_alloc (name '%s' id %" PRIu32 "): alloc_count reached SIZE_MAX\n", t->name ? t->name : "unnamed", t->id); fprintf(t->err_log, "WARNING in guf_track_alloc (name '%s' id %u): alloc_count reached SIZE_MAX\n", t->name ? t->name : "unnamed", t->id);
} }
if (guf_saturating_add_ptrdiff_t(t->allocated_bytes, size, &t->allocated_bytes) != GUF_MATH_CKD_SUCCESS) { if (guf_saturating_add_ptrdiff_t(t->allocated_bytes, size, &t->allocated_bytes) != GUF_MATH_CKD_SUCCESS) {
if (t->err_log) { if (t->err_log) {
fprintf(t->err_log, "ERROR in guf_track_alloc (name '%s' id %" PRIu32 "): allocated_byte overflow\n", t->name ? t->name : "unnamed", t->id); fprintf(t->err_log, "ERROR in guf_track_alloc (name '%s' id %u): allocated_byte overflow\n", t->name ? t->name : "unnamed", t->id);
} }
success = false; success = false;
} }
if (t->allocated_bytes < 0) { if (t->allocated_bytes < 0) {
if (t->err_log) { if (t->err_log) {
fprintf(t->err_log, "ERROR in guf_track_alloc (name '%s' id %" PRIu32 "): allocated_bytes < 0\n", t->name ? t->name : "unnamed", t->id); fprintf(t->err_log, "ERROR in guf_track_alloc (name '%s' id %u): allocated_bytes < 0\n", t->name ? t->name : "unnamed", t->id);
} }
success = false; success = false;
} }
if (t->log) { if (t->log) {
fprintf(t->log, "guf_alloc_tracker (name '%s' id %" PRIu32 "): alloc (%td bytes) %s\n", t->name ? t->name : "unnamed", t->id, size, success ? "SUCCESS" : "FAILURE"); fprintf(t->log, "guf_alloc_tracker (name '%s' id %u): alloc (%td bytes) %s\n", t->name ? t->name : "unnamed", t->id, size, success ? "SUCCESS" : "FAILURE");
} }
return success; return success;
} }
@ -127,40 +127,40 @@ GUF_ALLOC_TRACKER_KWRDS bool guf_track_realloc(guf_alloc_tracker *t, ptrdiff_t o
bool success = true; bool success = true;
if (guf_saturating_add_size_t(t->realloc_count, 1, &t->realloc_count) && t->err_log) { if (guf_saturating_add_size_t(t->realloc_count, 1, &t->realloc_count) && t->err_log) {
fprintf(t->err_log, "WARNING in guf_track_realloc (name '%s' id %" PRIu32 "): realloc_count reached SIZE_MAX\n", t->name ? t->name : "unnamed", t->id); fprintf(t->err_log, "WARNING in guf_track_realloc (name '%s' id %u): realloc_count reached SIZE_MAX\n", t->name ? t->name : "unnamed", t->id);
} }
if (old_size < 0 || new_size < 0) { if (old_size < 0 || new_size < 0) {
success = false; success = false;
if (t->err_log) { if (t->err_log) {
fprintf(t->err_log, "ERROR in guf_track_realloc (name '%s' id %" PRIu32 "): old_size < 0 or new_size < 0\n", t->name ? t->name : "unnamed", t->id); fprintf(t->err_log, "ERROR in guf_track_realloc (name '%s' id %u): old_size < 0 or new_size < 0\n", t->name ? t->name : "unnamed", t->id);
} }
} }
if (guf_saturating_sub_ptrdiff_t(t->allocated_bytes, old_size, &t->allocated_bytes)) { if (guf_saturating_sub_ptrdiff_t(t->allocated_bytes, old_size, &t->allocated_bytes)) {
success = false; success = false;
if (t->err_log) { if (t->err_log) {
fprintf(t->err_log, "ERROR in guf_track_realloc (name '%s' id %" PRIu32 "): allocated_bytes - old_size under/overflows\n", t->name ? t->name : "unnamed", t->id); fprintf(t->err_log, "ERROR in guf_track_realloc (name '%s' id %u): allocated_bytes - old_size under/overflows\n", t->name ? t->name : "unnamed", t->id);
} }
} }
if (t->allocated_bytes < 0) { if (t->allocated_bytes < 0) {
success = false; success = false;
fprintf(t->err_log, "ERROR in guf_track_realloc (name '%s' id %" PRIu32 "): allocated_bytes < 0 after subtracting old_size\n", t->name ? t->name : "unnamed", t->id); fprintf(t->err_log, "ERROR in guf_track_realloc (name '%s' id %u): allocated_bytes < 0 after subtracting old_size\n", t->name ? t->name : "unnamed", t->id);
} }
if (guf_saturating_add_ptrdiff_t(t->allocated_bytes, new_size, &t->allocated_bytes)) { if (guf_saturating_add_ptrdiff_t(t->allocated_bytes, new_size, &t->allocated_bytes)) {
success = false; success = false;
if (t->err_log) { if (t->err_log) {
fprintf(t->err_log, "ERROR in guf_track_realloc (name '%s' id %" PRIu32 "): allocated_bytes overflow \n", t->name ? t->name : "unnamed", t->id); fprintf(t->err_log, "ERROR in guf_track_realloc (name '%s' id %u): allocated_bytes overflow \n", t->name ? t->name : "unnamed", t->id);
} }
} }
if (t->allocated_bytes < 0) { if (t->allocated_bytes < 0) {
success = false; success = false;
fprintf(t->err_log, "ERROR in guf_track_realloc (name '%s' id %" PRIu32 "): allocated_bytes < 0 after adding new_size\n", t->name ? t->name : "unnamed", t->id); fprintf(t->err_log, "ERROR in guf_track_realloc (name '%s' id %u): allocated_bytes < 0 after adding new_size\n", t->name ? t->name : "unnamed", t->id);
} }
if (t->log) { if (t->log) {
fprintf(t->log, "guf_alloc_tracker (name '%s' id %" PRIu32 "): realloc (from %td to %td bytes) %s\n", t->name ? t->name : "unnamed", t->id, old_size, new_size, (success ? "SUCCESS" : "FAILURE")); fprintf(t->log, "guf_alloc_tracker (name '%s' id %u): realloc (from %td to %td bytes) %s\n", t->name ? t->name : "unnamed", t->id, old_size, new_size, (success ? "SUCCESS" : "FAILURE"));
} }
return success; return success;
@ -173,31 +173,31 @@ GUF_ALLOC_TRACKER_KWRDS bool guf_track_free(guf_alloc_tracker *t, ptrdiff_t size
bool success = true; bool success = true;
if (guf_saturating_add_size_t(t->free_count, 1, &t->free_count) && t->err_log) { if (guf_saturating_add_size_t(t->free_count, 1, &t->free_count) && t->err_log) {
fprintf(t->err_log, "WARNING in guf_track_free (name '%s' id %" PRIu32 "): free_count reached SIZE_MAX\n", t->name ? t->name : "unnamed", t->id); fprintf(t->err_log, "WARNING in guf_track_free (name '%s' id %u): free_count reached SIZE_MAX\n", t->name ? t->name : "unnamed", t->id);
} }
if (size < 0) { if (size < 0) {
success = false; success = false;
if (t->err_log) { if (t->err_log) {
fprintf(t->err_log, "ERROR in guf_track_free (name '%s' id %" PRIu32 "): size < 0\n", t->name ? t->name : "unnamed", t->id); fprintf(t->err_log, "ERROR in guf_track_free (name '%s' id %u): size < 0\n", t->name ? t->name : "unnamed", t->id);
} }
} }
if (t->allocated_bytes < size) { if (t->allocated_bytes < size) {
success = false; success = false;
if (t->err_log) { if (t->err_log) {
fprintf(t->err_log, "ERROR in guf_track_free (name '%s' id %" PRIu32 "): freed more bytes than allocated\n", t->name ? t->name : "unnamed", t->id); fprintf(t->err_log, "ERROR in guf_track_free (name '%s' id %u): freed more bytes than allocated\n", t->name ? t->name : "unnamed", t->id);
} }
} }
if (guf_saturating_sub_ptrdiff_t(t->allocated_bytes, size, &t->allocated_bytes)) { if (guf_saturating_sub_ptrdiff_t(t->allocated_bytes, size, &t->allocated_bytes)) {
success = false; success = false;
if (t->err_log) { if (t->err_log) {
fprintf(t->err_log, "ERROR in guf_track_free (name '%s' id %" PRIu32 "): allocated_bytes - size under/overflows\n", t->name ? t->name : "unnamed", t->id); fprintf(t->err_log, "ERROR in guf_track_free (name '%s' id %u): allocated_bytes - size under/overflows\n", t->name ? t->name : "unnamed", t->id);
} }
} }
if (t->log) { if (t->log) {
fprintf(t->log, "guf_alloc_tracker (name '%s' id %" PRIu32 "): free (%td bytes) %s\n", t->name ? t->name : "unnamed", t->id, size, (success ? "SUCCESS" : "FAILURE")); fprintf(t->log, "guf_alloc_tracker (name '%s' id %u): free (%td bytes) %s\n", t->name ? t->name : "unnamed", t->id, size, (success ? "SUCCESS" : "FAILURE"));
} }
return success; return success;

1
src/guf_assert.h Normal file → Executable file
View File

@ -26,6 +26,7 @@ typedef enum guf_err {
GUF_ERR_NOT_FOUND, GUF_ERR_NOT_FOUND,
GUF_ERR_ALREADY_EXISTS, GUF_ERR_ALREADY_EXISTS,
GUF_ERR_ASSERT_FAIL, GUF_ERR_ASSERT_FAIL,
GUF_ERR_IO,
GUF_ERR_TYPES_NUM GUF_ERR_TYPES_NUM
} guf_err; } guf_err;

30
src/guf_common.h Normal file → Executable file
View File

@ -14,13 +14,35 @@
#define GUF_PLATFORM_LITTLE_ENDIAN #define GUF_PLATFORM_LITTLE_ENDIAN
#endif #endif
#if SIZE_MAX == UINT64_MAX #define GUF_UINT8_MAX 0xffu
#define GUF_UINT16_MAX 0xffffu
#define GUF_UINT32_MAX 0xfffffffful
#define GUF_UINT64_MAX 0xffffffffffffffffull
#define GUF_UWRAP_8(UINT) ( (UINT) & GUF_UINT8_MAX )
#define GUF_UWRAP_16(UINT) ( (UINT) & GUF_UINT16_MAX )
#define GUF_UWRAP_32(UINT) ( (UINT) & GUF_UINT32_MAX )
#define GUF_UWRAP_64(UINT) ( (UINT) & GUF_UINT64_MAX )
#define GUF_INT8_MAX 127
#define GUF_INT8_MIN -128
#define GUF_INT16_MAX 32767
#define GUF_INT16_MIN (-GUF_INT16_MAX - 1)
#define GUF_INT32_MAX 2147483647L
#define GUF_INT32_MIN (-GUF_INT32_MAX - 1)
#define GUF_INT64_MAX 9223372036854775807LL
#define GUF_INT64_MIN (-GUF_INT64_MAX - 1)
#if SIZE_MAX == GUF_UINT64_MAX
#define GUF_PLATFORM_BITS 64 #define GUF_PLATFORM_BITS 64
#elif SIZE_MAX == UINT32_MAX #elif SIZE_MAX == GUF_UINT32_MAX
#define GUF_PLATFORM_BITS 32 #define GUF_PLATFORM_BITS 32
#elif SIZE_MAX == UINT16_MAX #elif SIZE_MAX == GUF_UINT16_MAX
#define GUF_PLATFORM_BITS 16 #define GUF_PLATFORM_BITS 16
#elif SIZE_MAX == UINT8_MAX #elif SIZE_MAX == GUF_UINT8_MAX
#define GUF_PLATFORM_BITS 8 #define GUF_PLATFORM_BITS 8
#else #else
#define GUF_PLATFORM_BITS 64 #define GUF_PLATFORM_BITS 64

13
src/guf_cstr.h Normal file → Executable file
View File

@ -9,6 +9,15 @@
#include "guf_assert.h" #include "guf_assert.h"
#include "guf_hash.h" #include "guf_hash.h"
static inline char *guf_cstr_dup(const char *cstr)
{
char *cpy = (char*)calloc(strlen(cstr) + 1, 1); // Explicit conversion for cpp...
if (cpy) {
strcpy(cpy, cstr);
}
return cpy;
}
typedef const char* guf_cstr_const; typedef const char* guf_cstr_const;
static inline int guf_cstr_const_cmp(const guf_cstr_const *a, const guf_cstr_const *b) static inline int guf_cstr_const_cmp(const guf_cstr_const *a, const guf_cstr_const *b)
@ -42,11 +51,11 @@ static inline guf_cstr_heap *guf_cstr_heap_copy(guf_cstr_heap *dst, const guf_cs
*dst = NULL; *dst = NULL;
return dst; return dst;
} }
*dst = strdup(*src); *dst = guf_cstr_dup(*src);
GUF_ASSERT_RELEASE(*dst); GUF_ASSERT_RELEASE(*dst);
return dst; return dst;
} }
//
static inline guf_cstr_heap *guf_cstr_heap_move(guf_cstr_heap *dst, guf_cstr_heap *src, void *ctx) static inline guf_cstr_heap *guf_cstr_heap_move(guf_cstr_heap *dst, guf_cstr_heap *src, void *ctx)
{ {
(void)ctx; (void)ctx;

0
src/guf_dbuf.h Normal file → Executable file
View File

View File

@ -34,18 +34,18 @@
#endif #endif
#if defined(GUF_DICT_32_BIT_HASH) #if defined(GUF_DICT_32_BIT_HASH)
#define GUF_DICT_HASH_T uint32_t #define GUF_DICT_HASH_T uint_least32_t
#define GUF_DICT_HASH_T_MAX UINT32_MAX #define GUF_DICT_HASH_T_MAX GUF_UINT32_MAX
#elif defined(GUF_DICT_64_BIT_HASH) #elif defined(GUF_DICT_64_BIT_HASH)
#define GUF_DICT_HASH_T uint64_t #define GUF_DICT_HASH_T uint_least64_t
#define GUF_DICT_HASH_T_MAX UINT64_MAX #define GUF_DICT_HASH_T_MAX GUF_UINT64_MAX
#else #else
#define GUF_DICT_HASH_T guf_hash_size_t #define GUF_DICT_HASH_T guf_hash_size_t
#define GUF_DICT_HASH_T_MAX GUF_HASH_MAX #define GUF_DICT_HASH_T_MAX GUF_HASH_MAX
#endif #endif
#if defined (GUF_DICT_64_BIT_IDX) #if defined (GUF_DICT_64_BIT_IDX)
#define GUF_DICT_KV_META_T uint64_t #define GUF_DICT_KV_META_T uint_least64_t
/* /*
Store a 16-bit hash-fragment in the upper 16-bits of kv_meta Store a 16-bit hash-fragment in the upper 16-bits of kv_meta
-> (2^48 - 1 is IDX_NULL, 2^48 - 2 is IDX_TOMBSTONE, 2^48 - 3 is the largest actual idx, -> (2^48 - 1 is IDX_NULL, 2^48 - 2 is IDX_TOMBSTONE, 2^48 - 3 is the largest actual idx,
@ -54,15 +54,15 @@
#define GUF_DICT_KV_META_HASHFRAG_MASK UINT64_C(0xffff000000000000) #define GUF_DICT_KV_META_HASHFRAG_MASK UINT64_C(0xffff000000000000)
#define GUF_DICT_KV_META_IDX_MASK (~UINT64_C(0xffff000000000000)) #define GUF_DICT_KV_META_IDX_MASK (~UINT64_C(0xffff000000000000))
#if GUF_DICT_HASH_T_MAX == UINT64_MAX #if GUF_DICT_HASH_T_MAX == GUF_UINT64_MAX
#define GUF_DICT_HASH_T_GET_HASHFRAG(HASH) ( (HASH) & GUF_DICT_KV_META_HASHFRAG_MASK ) #define GUF_DICT_HASH_T_GET_HASHFRAG(HASH) ( (HASH) & GUF_DICT_KV_META_HASHFRAG_MASK )
#elif GUF_DICT_HASH_T_MAX == UINT32_MAX #elif GUF_DICT_HASH_T_MAX == GUF_UINT32_MAX
#define GUF_DICT_HASH_T_GET_HASHFRAG(HASH) ( (((uint64_t)(HASH)) << 32) & GUF_DICT_KV_META_HASHFRAG_MASK ) #define GUF_DICT_HASH_T_GET_HASHFRAG(HASH) ( (((uint_least64_t)(HASH)) << 32u) & GUF_DICT_KV_META_HASHFRAG_MASK )
#else #else
#error "guf_dict: invalid hash size (should not happen)" #error "guf_dict: invalid hash size (should not happen)"
#endif #endif
#else #else
#define GUF_DICT_KV_META_T uint32_t #define GUF_DICT_KV_META_T uint_least32_t
/* /*
Store a 7-bit hash-fragment in the upper 7-bits of kv_meta Store a 7-bit hash-fragment in the upper 7-bits of kv_meta
-> (2^25 - 1 is IDX_NULL, 2^25 - 2 is IDX_TOMBSTONE, 2^25 - 3 is the largest actual idx, -> (2^25 - 1 is IDX_NULL, 2^25 - 2 is IDX_TOMBSTONE, 2^25 - 3 is the largest actual idx,
@ -71,9 +71,9 @@
#define GUF_DICT_KV_META_HASHFRAG_MASK UINT32_C(0xfe000000) #define GUF_DICT_KV_META_HASHFRAG_MASK UINT32_C(0xfe000000)
#define GUF_DICT_KV_META_IDX_MASK (~UINT32_C(0xfe000000)) #define GUF_DICT_KV_META_IDX_MASK (~UINT32_C(0xfe000000))
#if GUF_DICT_HASH_T_MAX == UINT64_MAX #if GUF_DICT_HASH_T_MAX == GUF_UINT64_MAX
#define GUF_DICT_HASH_T_GET_HASHFRAG(HASH) ( ((uint32_t)((HASH) >> 32)) & GUF_DICT_KV_META_HASHFRAG_MASK ) #define GUF_DICT_HASH_T_GET_HASHFRAG(HASH) ( ((uint_least32_t)((HASH) >> 32u)) & GUF_DICT_KV_META_HASHFRAG_MASK )
#elif GUF_DICT_HASH_T_MAX == UINT32_MAX #elif GUF_DICT_HASH_T_MAX == GUF_UINT32_MAX
#define GUF_DICT_HASH_T_GET_HASHFRAG(HASH) ( (HASH) & GUF_DICT_KV_META_HASHFRAG_MASK ) #define GUF_DICT_HASH_T_GET_HASHFRAG(HASH) ( (HASH) & GUF_DICT_KV_META_HASHFRAG_MASK )
#else #else
#error "guf_dict: invalid hash size (should not happen)" #error "guf_dict: invalid hash size (should not happen)"
@ -275,7 +275,7 @@ GUF_DICT_KWRDS GUF_DICT_NAME *GUF_CAT(GUF_DICT_NAME, _try_init_with_capacity)(GU
if (kv_elem_capacity > 0) { if (kv_elem_capacity > 0) {
const size_t MAX_IDX_CAP = GUF_ALLOC_MAX_CAPACITY(GUF_DICT_KV_META_T); const size_t MAX_IDX_CAP = GUF_ALLOC_MAX_CAPACITY(GUF_DICT_KV_META_T);
const size_t desired_idx_cap = (size_t)guf_min_f64(kv_elem_capacity * 1.0 / GUF_DICT_MAX_LOAD_FACTOR, MAX_IDX_CAP); const size_t desired_idx_cap = (size_t)guf_min_f64((double)kv_elem_capacity * 1.0 / GUF_DICT_MAX_LOAD_FACTOR, (double)MAX_IDX_CAP);
// Capacities must be powers of two. // Capacities must be powers of two.
size_t kv_idx_cap = 1; size_t kv_idx_cap = 1;
while ((kv_idx_cap <= MAX_IDX_CAP / 2) && (kv_idx_cap <= desired_idx_cap)) { while ((kv_idx_cap <= MAX_IDX_CAP / 2) && (kv_idx_cap <= desired_idx_cap)) {
@ -447,10 +447,11 @@ static inline size_t GUF_CAT(GUF_DICT_NAME, _probe_offset_)(size_t probe_len)
return probe_len; // 1, 2, 3, 4, 5, ... return probe_len; // 1, 2, 3, 4, 5, ...
#else #else
/* /*
Quadratic probing:
Guaranteed to visit each index once for capacities which are powers of two. Guaranteed to visit each index once for capacities which are powers of two.
cf. https://fgiesen.wordpress.com/2015/02/22/triangular-numbers-mod-2n/ (last-retrieved 2024-07-29) cf. https://fgiesen.wordpress.com/2015/02/22/triangular-numbers-mod-2n/ (last-retrieved 2024-07-29)
*/ */
return probe_len * (probe_len + 1) / 2; // 1, 3, 6, 10, 15, 21 ... (starting from probe_len == 1) return probe_len * (probe_len + 1u) / 2u; // 1, 3, 6, 10, 15, 21 ... (starting from probe_len == 1)
#endif #endif
} }
@ -597,7 +598,7 @@ static void GUF_CAT(GUF_DICT_NAME, _try_grow_if_necessary_)(GUF_DICT_NAME *ht, b
ht->kv_indices = new_kv_indices; ht->kv_indices = new_kv_indices;
ht->kv_indices_cap = ht->kv_indices_cap * KV_META_GROWTH_FAC;; ht->kv_indices_cap = ht->kv_indices_cap * KV_META_GROWTH_FAC;;
GUF_ASSERT(guf_is_pow2_size_t(ht->kv_indices_cap)); GUF_ASSERT(guf_is_pow2_size_t(ht->kv_indices_cap));
GUF_ASSERT(new_size_bytes / sizeof(GUF_DICT_KV_META_T) == ht->kv_indices_cap); GUF_ASSERT((ptrdiff_t)(new_size_bytes / sizeof(GUF_DICT_KV_META_T)) == ht->kv_indices_cap);
// ht->max_probelen = 0; // ht->max_probelen = 0;
GUF_CAT(GUF_DICT_NAME, _reinsert_elems_)(ht); GUF_CAT(GUF_DICT_NAME, _reinsert_elems_)(ht);
GUF_ASSERT(ht->num_tombstones == 0); GUF_ASSERT(ht->num_tombstones == 0);
@ -658,13 +659,13 @@ GUF_DICT_KWRDS void GUF_CAT(GUF_DICT_NAME, _try_insert)(GUF_DICT_NAME *ht, GUF_D
GUF_DICT_KEY_T *key_cpy_res = NULL; GUF_DICT_KEY_T *key_cpy_res = NULL;
if (key_opt == GUF_CPY_DEEP) { if (key_opt == GUF_CPY_DEEP) {
#ifdef GUF_DICT_KEY_T_COPY #ifdef GUF_DICT_KEY_T_COPY
key_cpy_res = GUF_DICT_KEY_T_COPY(&key_cpy, key); key_cpy_res = GUF_DICT_KEY_T_COPY(&key_cpy, key, NULL);
#else #else
GUF_ASSERT_RELEASE(false); GUF_ASSERT_RELEASE(false);
#endif #endif
} else if (key_opt == GUF_CPY_MOVE) { } else if (key_opt == GUF_CPY_MOVE) {
#ifdef GUF_DICT_KEY_T_MOVE #ifdef GUF_DICT_KEY_T_MOVE
key_cpy_res = GUF_DICT_KEY_T_MOVE(&key_cpy, key); key_cpy_res = GUF_DICT_KEY_T_MOVE(&key_cpy, key, NULL);
#else #else
GUF_ASSERT_RELEASE(false); GUF_ASSERT_RELEASE(false);
#endif #endif

40
src/guf_hash.h Normal file → Executable file
View File

@ -19,29 +19,22 @@
*/ */
#define GUF_HASH32_INIT UINT32_C(2166136261) #define GUF_HASH32_INIT UINT32_C(2166136261)
#ifdef UINT64_MAX
#define GUF_HASH64_INIT UINT64_C(14695981039346656037) #define GUF_HASH64_INIT UINT64_C(14695981039346656037)
#endif
GUF_HASH_KWRDS uint32_t guf_hash32(const void *data, ptrdiff_t num_bytes, uint32_t hash); // FNV-1a (32 bit) GUF_HASH_KWRDS uint_least32_t guf_hash32(const void *data, ptrdiff_t num_bytes, uint_least32_t hash); // FNV-1a (32 bit)
#ifdef UINT64_MAX GUF_HASH_KWRDS uint_least64_t guf_hash64(const void *data, ptrdiff_t num_bytes, uint_least64_t hash); // FNV-1a (64 bit)
GUF_HASH_KWRDS uint64_t guf_hash64(const void *data, ptrdiff_t num_bytes, uint64_t hash); // FNV-1a (64 bit)
#endif
#ifdef GUF_HASH_32_BIT #ifdef GUF_HASH_32_BIT
typedef uint32_t guf_hash_size_t; typedef uint_least32_t guf_hash_size_t;
#define GUF_HASH_INIT GUF_HASH32_INIT #define GUF_HASH_INIT GUF_HASH32_INIT
#define GUF_HASH_MAX UINT32_MAX #define GUF_HASH_MAX GUF_UINT32_MAX
static inline guf_hash_size_t guf_hash(const void *data, ptrdiff_t num_bytes, guf_hash_size_t hash) { static inline guf_hash_size_t guf_hash(const void *data, ptrdiff_t num_bytes, guf_hash_size_t hash) {
return guf_hash32(data, num_bytes, hash); return guf_hash32(data, num_bytes, hash);
} }
#else #else
#ifndef UINT64_MAX typedef uint_least64_t guf_hash_size_t;
#error "guf_hash.h: Platform does not support uint64_t (define GUF_HASH_32_BIT to fix)"
#endif
typedef uint64_t guf_hash_size_t;
#define GUF_HASH_INIT GUF_HASH64_INIT #define GUF_HASH_INIT GUF_HASH64_INIT
#define GUF_HASH_MAX UINT64_MAX #define GUF_HASH_MAX GUF_UINT64_MAX
static inline guf_hash_size_t guf_hash(const void *data, ptrdiff_t num_bytes, guf_hash_size_t hash) { static inline guf_hash_size_t guf_hash(const void *data, ptrdiff_t num_bytes, guf_hash_size_t hash) {
return guf_hash64(data, num_bytes, hash); return guf_hash64(data, num_bytes, hash);
} }
@ -53,36 +46,37 @@ GUF_HASH_KWRDS uint32_t guf_hash32(const void *data, ptrdiff_t num_bytes, uint32
#include "guf_assert.h" #include "guf_assert.h"
GUF_HASH_KWRDS uint32_t guf_hash32(const void *data, ptrdiff_t num_bytes, uint32_t hash) GUF_HASH_KWRDS uint_least32_t guf_hash32(const void *data, ptrdiff_t num_bytes, uint_least32_t hash)
{ {
hash = GUF_UWRAP_32(hash);
GUF_ASSERT_RELEASE(data); GUF_ASSERT_RELEASE(data);
GUF_ASSERT_RELEASE(num_bytes >= 0); GUF_ASSERT_RELEASE(num_bytes >= 0);
const unsigned char *data_bytes = (const unsigned char*)data; // This does not break strict-aliasing rules I think... const unsigned char *data_bytes = (const unsigned char*)data; // This does not break strict-aliasing rules I think...
const uint32_t FNV_32_PRIME = UINT32_C(16777619); const uint_least32_t FNV_32_PRIME = UINT32_C(16777619);
for (ptrdiff_t i = 0; i < num_bytes; ++i) { for (ptrdiff_t i = 0; i < num_bytes; ++i) {
hash ^= 1u * data_bytes[i]; hash = GUF_UWRAP_32(1u * hash ^ data_bytes[i]);
hash *= 1u * FNV_32_PRIME; hash = GUF_UWRAP_32(1u * hash * FNV_32_PRIME);
} }
return hash; return hash;
} }
#ifdef UINT64_MAX GUF_HASH_KWRDS uint_least64_t guf_hash64(const void *data, ptrdiff_t num_bytes, uint_least64_t hash)
GUF_HASH_KWRDS uint64_t guf_hash64(const void *data, ptrdiff_t num_bytes, uint64_t hash)
{ {
hash = GUF_UWRAP_64(hash);
GUF_ASSERT_RELEASE(data); GUF_ASSERT_RELEASE(data);
GUF_ASSERT_RELEASE(num_bytes >= 0); GUF_ASSERT_RELEASE(num_bytes >= 0);
const unsigned char *data_bytes = (const unsigned char*)data; // This does not break strict-aliasing rules I think... const unsigned char *data_bytes = (const unsigned char*)data; // This does not break strict-aliasing rules I think...
const uint64_t FNV_64_PRIME = UINT64_C(1099511628211); const uint_least64_t FNV_64_PRIME = UINT64_C(1099511628211);
for (ptrdiff_t i = 0; i < num_bytes; ++i) { for (ptrdiff_t i = 0; i < num_bytes; ++i) {
hash ^= 1u * data_bytes[i]; hash = GUF_UWRAP_64(1u * hash ^ data_bytes[i]);
hash *= 1u * FNV_64_PRIME; hash = GUF_UWRAP_64(1u * hash * FNV_64_PRIME);
} }
return hash; return hash;
} }
#endif
#undef GUF_HASH_IMPL #undef GUF_HASH_IMPL
#undef GUF_HASH_IMPL_STATIC #undef GUF_HASH_IMPL_STATIC
#endif /* endif GUF_IMPL/GUF_IMPL_STATIC */ #endif /* endif GUF_IMPL/GUF_IMPL_STATIC */
#undef GUF_HASH_KWRDS #undef GUF_HASH_KWRDS

57
src/guf_id_pool.h Normal file → Executable file
View File

@ -13,13 +13,13 @@
#include "guf_common.h" #include "guf_common.h"
#include "guf_assert.h" #include "guf_assert.h"
#define GUF_ID_i32_NULL (-1) #define GUF_ID_I32_NULL (-1)
#define GUF_ID_i16_NULL (-1) #define GUF_ID_I16_NULL (-1)
#define GUF_ID_i8_NULL (-1) #define GUF_ID_I8_NULL (-1)
#define GUF_ID_u32_NULL (UINT32_MAX) #define GUF_ID_U32_NULL (GUF_UINT32_MAX)
#define GUF_ID_u16_NULL (UINT16_MAX) #define GUF_ID_U16_NULL (GUF_UINT16_MAX)
#define GUF_ID_u8_NULL (UINT8_MAX) #define GUF_ID_U8_NULL (GUF_UINT8_MAX)
#endif #endif
/* /*
@ -34,49 +34,49 @@
#if defined(GUF_ID_POOL_i32) #if defined(GUF_ID_POOL_i32)
#undef GUF_ID_POOL_i32 #undef GUF_ID_POOL_i32
#define GUF_ID_POOL_T int32_t #define GUF_ID_POOL_T int_least32_t
#define GUF_ID_POOL_T_MAX INT32_MAX #define GUF_ID_POOL_T_MAX GUF_INT32_MAX
#define GUF_ID_POOL_NULL GUF_ID_i32_NULL #define GUF_ID_POOL_NULL GUF_ID_I32_NULL
#ifndef GUF_ID_POOL_NAME #ifndef GUF_ID_POOL_NAME
#define GUF_ID_POOL_NAME guf_idpool_i32 #define GUF_ID_POOL_NAME guf_idpool_i32
#endif #endif
#elif defined(GUF_ID_POOL_i16) #elif defined(GUF_ID_POOL_i16)
#undef GUF_ID_POOL_i16 #undef GUF_ID_POOL_i16
#define GUF_ID_POOL_T int16_t #define GUF_ID_POOL_T int_least16_t
#define GUF_ID_POOL_T_MAX INT16_MAX #define GUF_ID_POOL_T_MAX GUF_INT16_MAX
#define GUF_ID_POOL_NULL GUF_ID_i16_NULL #define GUF_ID_POOL_NULL GUF_ID_I16_NULL
#ifndef GUF_ID_POOL_NAME #ifndef GUF_ID_POOL_NAME
#define GUF_ID_POOL_NAME guf_idpool_i16 #define GUF_ID_POOL_NAME guf_idpool_i16
#endif #endif
#elif defined(GUF_ID_POOL_i8) #elif defined(GUF_ID_POOL_i8)
#undef GUF_ID_POOL_i8 #undef GUF_ID_POOL_i8
#define GUF_ID_POOL_T int8_t #define GUF_ID_POOL_T int_least8_t
#define GUF_ID_POOL_T_MAX INT8_MAX #define GUF_ID_POOL_T_MAX GUF_INT8_MAX
#define GUF_ID_POOL_NULL GUF_ID_i8_NULL #define GUF_ID_POOL_NULL GUF_ID_I8_NULL
#ifndef GUF_ID_POOL_NAME #ifndef GUF_ID_POOL_NAME
#define GUF_ID_POOL_NAME guf_idpool_i8 #define GUF_ID_POOL_NAME guf_idpool_i8
#endif #endif
#elif defined(GUF_ID_POOL_u32) #elif defined(GUF_ID_POOL_u32)
#undef GUF_ID_POOL_u32 #undef GUF_ID_POOL_u32
#define GUF_ID_POOL_T uint32_t #define GUF_ID_POOL_T uint_least32_t
#define GUF_ID_POOL_T_MAX (UINT32_MAX - 1) #define GUF_ID_POOL_T_MAX (GUF_UINT32_MAX - 1)
#define GUF_ID_POOL_NULL GUF_ID_u32_NULL #define GUF_ID_POOL_NULL GUF_ID_U32_NULL
#ifndef GUF_ID_POOL_NAME #ifndef GUF_ID_POOL_NAME
#define GUF_ID_POOL_NAME guf_idpool_u32 #define GUF_ID_POOL_NAME guf_idpool_u32
#endif #endif
#elif defined(GUF_ID_POOL_u16) #elif defined(GUF_ID_POOL_u16)
#undef GUF_ID_POOL_u16 #undef GUF_ID_POOL_u16
#define GUF_ID_POOL_T uint16_t #define GUF_ID_POOL_T uint_least16_t
#define GUF_ID_POOL_T_MAX (UINT16_MAX - 1) #define GUF_ID_POOL_T_MAX (GUF_UINT16_MAX - 1)
#define GUF_ID_POOL_NULL GUF_ID_u16_NULL #define GUF_ID_POOL_NULL GUF_ID_U16_NULL
#ifndef GUF_ID_POOL_NAME #ifndef GUF_ID_POOL_NAME
#define GUF_ID_POOL_NAME guf_idpool_u16 #define GUF_ID_POOL_NAME guf_idpool_u16
#endif #endif
#elif defined(GUF_ID_POOL_u8) #elif defined(GUF_ID_POOL_u8)
#undef GUF_ID_POOL_u8 #undef GUF_ID_POOL_u8
#define GUF_ID_POOL_T uint8_t #define GUF_ID_POOL_T uint_least8_t
#define GUF_ID_POOL_T_MAX (UINT8_MAX - 1) #define GUF_ID_POOL_T_MAX (GUF_UINT8_MAX - 1)
#define GUF_ID_POOL_NULL GUF_ID_u8_NULL #define GUF_ID_POOL_NULL GUF_ID_U8_NULL
#ifndef GUF_ID_POOL_NAME #ifndef GUF_ID_POOL_NAME
#define GUF_ID_POOL_NAME guf_idpool_u8 #define GUF_ID_POOL_NAME guf_idpool_u8
#endif #endif
@ -116,6 +116,9 @@
void GUF_CAT(GUF_ID_POOL_NAME, _try_free_id)(GUF_ID_POOL_NAME *pool, GUF_ID_POOL_T id, guf_err *err); void GUF_CAT(GUF_ID_POOL_NAME, _try_free_id)(GUF_ID_POOL_NAME *pool, GUF_ID_POOL_T id, guf_err *err);
void GUF_CAT(GUF_ID_POOL_NAME, _free_id)(GUF_ID_POOL_NAME *pool, GUF_ID_POOL_T id); void GUF_CAT(GUF_ID_POOL_NAME, _free_id)(GUF_ID_POOL_NAME *pool, GUF_ID_POOL_T id);
GUF_ID_POOL_NAME ptrdiff_t GUF_CAT(GUF_ID_POOL_NAME, _num_allocated)(const GUF_ID_POOL_NAME *pool); // TODO: test
#endif #endif
#if defined(GUF_ID_POOL_IMPL) || defined(GUF_ID_POOL_IMPL_STATIC) #if defined(GUF_ID_POOL_IMPL) || defined(GUF_ID_POOL_IMPL_STATIC)
@ -137,6 +140,12 @@ static bool GUF_CAT(GUF_ID_POOL_NAME, _is_valid)(const GUF_ID_POOL_NAME *pool)
return valid_next_id && valid_bufsize && GUF_CAT(GUF_ID_POOL_DBUF, _valid)(&pool->free_id_dbuf) && pool->free_id_dbuf.size >= 0; return valid_next_id && valid_bufsize && GUF_CAT(GUF_ID_POOL_DBUF, _valid)(&pool->free_id_dbuf) && pool->free_id_dbuf.size >= 0;
} }
GUF_ID_POOL_NAME ptrdiff_t GUF_CAT(GUF_ID_POOL_NAME, _num_allocated)(const GUF_ID_POOL_NAME *pool)
{
GUF_ASSERT((ptrdiff_t)pool->next_id >= free_id_dbuf.size)
return pool->next_id - free_id_dbuf.size;
}
GUF_ID_POOL_NAME *GUF_CAT(GUF_ID_POOL_NAME, _try_init)(GUF_ID_POOL_NAME *pool, ptrdiff_t initial_cap, guf_allocator *allocator, guf_err *err) GUF_ID_POOL_NAME *GUF_CAT(GUF_ID_POOL_NAME, _try_init)(GUF_ID_POOL_NAME *pool, ptrdiff_t initial_cap, guf_allocator *allocator, guf_err *err)
{ {
if (!pool) { if (!pool) {

0
src/guf_init.h Normal file → Executable file
View File

0
src/guf_linalg.h Normal file → Executable file
View File

245
src/guf_math.h Normal file → Executable file
View File

@ -15,13 +15,61 @@
#define GUF_MAX_F32_LT_ONE (1.f - FLT_EPSILON/FLT_RADIX) #define GUF_MAX_F32_LT_ONE (1.f - FLT_EPSILON/FLT_RADIX)
#define GUF_MAX_F64_LT_ONE (1.0 - DBL_EPSILON/FLT_RADIX) #define GUF_MAX_F64_LT_ONE (1.0 - DBL_EPSILON/FLT_RADIX)
// Typesafe unsigned integer wrapping functions (generated with libguf/tools/intwrap-gen.py)
static inline uint_least8_t guf_wrap8_least_u8(uint_least8_t a) { return a & GUF_UINT8_MAX; }
static inline uint_least16_t guf_wrap16_least_u16(uint_least16_t a) { return a & GUF_UINT16_MAX; }
static inline uint_least32_t guf_wrap32_least_u32(uint_least32_t a) { return a & GUF_UINT32_MAX; }
static inline uint_least64_t guf_wrap64_least_u64(uint_least64_t a) { return a & GUF_UINT64_MAX; }
static inline unsigned char guf_wrap8_uchar(unsigned char a) { return a & GUF_UINT8_MAX; } // unsigned char: >= 8 bits
static inline unsigned short guf_wrap16_ushort(unsigned short a) { return a & GUF_UINT16_MAX; } // unsigned short: >= 16 bits
static inline unsigned long guf_wrap32_ulong(unsigned long a) { return a & GUF_UINT32_MAX; } // unsigned long: >= 32 bits
static inline unsigned long long guf_wrap64_ulong_long(unsigned long long a) { return a & GUF_UINT64_MAX; } // unsigned long long: >= 64 bits
// Rotate left. // Rotate left.
static inline uint64_t guf_rotl_u64(uint64_t x, int k) {return (1u*x << k) | (1u*x >> (64 - k));} #ifdef UINT32_MAX
static inline uint32_t guf_rotl_u32(uint32_t x, int k) {return (1u*x << k) | (1u*x >> (32 - k));} static inline uint32_t guf_rotl_u32(uint32_t x, int k)
{
GUF_ASSERT(k > 0);
return (1u*x << k) | (1u*x >> (32 - k));
}
#endif
#ifdef UINT64_MAX
static inline uint64_t guf_rotl_u64(uint64_t x, int k)
{
GUF_ASSERT(k > 0);
return (1u*x << k) | (1u*x >> (64 - k));
}
#endif
static inline uint_least32_t guf_rotl32_least_u32(uint_least32_t x, int k)
{
GUF_ASSERT(k > 0);
x = guf_wrap32_least_u32(x);
return guf_wrap32_least_u32( (1u*x << k) | (1u*x >> (32 - k)) );
}
static inline uint_least64_t guf_rotl64_least_u64(uint_least64_t x, int k)
{
GUF_ASSERT(k > 0);
x = guf_wrap64_least_u64(x);
return guf_wrap64_least_u64( (1u*x << k) | (1u*x >> (64 - k)) );
}
static inline unsigned long guf_rotl32_ulong(unsigned long x, int k)
{
GUF_ASSERT(k > 0);
x = guf_wrap32_ulong(x);
return guf_wrap32_ulong( (x << k) | (x >> (32 - k)) );
}
static inline unsigned long long guf_rotl64_ulong_long(unsigned long long x, int k)
{
GUF_ASSERT(k > 0);
x = guf_wrap64_ulong_long(x);
return guf_wrap64_ulong_long( (x << k) | (x >> (64 - k)) );
}
// Signed min, max, clamp functions (generated with libguf/tools/min_max_clamp-gen.py) // Signed min, max, clamp functions (generated with libguf/tools/min_max_clamp-gen.py)
static inline char guf_min_char(char a, char b) { return a < b ? a : b; } static inline char guf_min_char(char a, char b) { return a < b ? a : b; }
static inline char guf_max_char(char a, char b) { return a > b ? a : b; } static inline char guf_max_char(char a, char b) { return a > b ? a : b; }
static inline char guf_clamp_char(char x, char min, char max) { if (x < min) {return min;} if (x > max) {return max;} return x; } static inline char guf_clamp_char(char x, char min, char max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
@ -38,26 +86,26 @@ static inline long long guf_min_long_long(long long a, long long b) { return a <
static inline long long guf_max_long_long(long long a, long long b) { return a > b ? a : b; } static inline long long guf_max_long_long(long long a, long long b) { return a > b ? a : b; }
static inline long long guf_clamp_long_long(long long x, long long min, long long max) { if (x < min) {return min;} if (x > max) {return max;} return x; } static inline long long guf_clamp_long_long(long long x, long long min, long long max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline int8_t guf_min_i8(int8_t a, int8_t b) { return a < b ? a : b; }
static inline int8_t guf_max_i8(int8_t a, int8_t b) { return a > b ? a : b; }
static inline int8_t guf_clamp_i8(int8_t x, int8_t min, int8_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline int16_t guf_min_i16(int16_t a, int16_t b) { return a < b ? a : b; }
static inline int16_t guf_max_i16(int16_t a, int16_t b) { return a > b ? a : b; }
static inline int16_t guf_clamp_i16(int16_t x, int16_t min, int16_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline int32_t guf_min_i32(int32_t a, int32_t b) { return a < b ? a : b; }
static inline int32_t guf_max_i32(int32_t a, int32_t b) { return a > b ? a : b; }
static inline int32_t guf_clamp_i32(int32_t x, int32_t min, int32_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline int64_t guf_min_i64(int64_t a, int64_t b) { return a < b ? a : b; }
static inline int64_t guf_max_i64(int64_t a, int64_t b) { return a > b ? a : b; }
static inline int64_t guf_clamp_i64(int64_t x, int64_t min, int64_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline ptrdiff_t guf_min_ptrdiff_t(ptrdiff_t a, ptrdiff_t b) { return a < b ? a : b; } static inline ptrdiff_t guf_min_ptrdiff_t(ptrdiff_t a, ptrdiff_t b) { return a < b ? a : b; }
static inline ptrdiff_t guf_max_ptrdiff_t(ptrdiff_t a, ptrdiff_t b) { return a > b ? a : b; } static inline ptrdiff_t guf_max_ptrdiff_t(ptrdiff_t a, ptrdiff_t b) { return a > b ? a : b; }
static inline ptrdiff_t guf_clamp_ptrdiff_t(ptrdiff_t x, ptrdiff_t min, ptrdiff_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; } static inline ptrdiff_t guf_clamp_ptrdiff_t(ptrdiff_t x, ptrdiff_t min, ptrdiff_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline int_least8_t guf_min_least_i8(int_least8_t a, int_least8_t b) { return a < b ? a : b; }
static inline int_least8_t guf_max_least_i8(int_least8_t a, int_least8_t b) { return a > b ? a : b; }
static inline int_least8_t guf_clamp_least_i8(int_least8_t x, int_least8_t min, int_least8_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline int_least16_t guf_min_least_i16(int_least16_t a, int_least16_t b) { return a < b ? a : b; }
static inline int_least16_t guf_max_least_i16(int_least16_t a, int_least16_t b) { return a > b ? a : b; }
static inline int_least16_t guf_clamp_least_i16(int_least16_t x, int_least16_t min, int_least16_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline int_least32_t guf_min_least_i32(int_least32_t a, int_least32_t b) { return a < b ? a : b; }
static inline int_least32_t guf_max_least_i32(int_least32_t a, int_least32_t b) { return a > b ? a : b; }
static inline int_least32_t guf_clamp_least_i32(int_least32_t x, int_least32_t min, int_least32_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline int_least64_t guf_min_least_i64(int_least64_t a, int_least64_t b) { return a < b ? a : b; }
static inline int_least64_t guf_max_least_i64(int_least64_t a, int_least64_t b) { return a > b ? a : b; }
static inline int_least64_t guf_clamp_least_i64(int_least64_t x, int_least64_t min, int_least64_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline float guf_min_f32(float a, float b) { return a < b ? a : b; } static inline float guf_min_f32(float a, float b) { return a < b ? a : b; }
static inline float guf_max_f32(float a, float b) { return a > b ? a : b; } static inline float guf_max_f32(float a, float b) { return a > b ? a : b; }
static inline float guf_clamp_f32(float x, float min, float max) { if (x < min) {return min;} if (x > max) {return max;} return x; } static inline float guf_clamp_f32(float x, float min, float max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
@ -66,8 +114,31 @@ static inline double guf_min_f64(double a, double b) { return a < b ? a : b; }
static inline double guf_max_f64(double a, double b) { return a > b ? a : b; } static inline double guf_max_f64(double a, double b) { return a > b ? a : b; }
static inline double guf_clamp_f64(double x, double min, double max) { if (x < min) {return min;} if (x > max) {return max;} return x; } static inline double guf_clamp_f64(double x, double min, double max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
// Unsigned min, max, clamp functions (generated with libguf/tools/min_max_clamp-gen.py) #ifdef INT8_MAX
static inline int8_t guf_min_i8(int8_t a, int8_t b) { return a < b ? a : b; }
static inline int8_t guf_max_i8(int8_t a, int8_t b) { return a > b ? a : b; }
static inline int8_t guf_clamp_i8(int8_t x, int8_t min, int8_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
#endif
#ifdef INT16_MAX
static inline int16_t guf_min_i16(int16_t a, int16_t b) { return a < b ? a : b; }
static inline int16_t guf_max_i16(int16_t a, int16_t b) { return a > b ? a : b; }
static inline int16_t guf_clamp_i16(int16_t x, int16_t min, int16_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
#endif
#ifdef INT32_MAX
static inline int32_t guf_min_i32(int32_t a, int32_t b) { return a < b ? a : b; }
static inline int32_t guf_max_i32(int32_t a, int32_t b) { return a > b ? a : b; }
static inline int32_t guf_clamp_i32(int32_t x, int32_t min, int32_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
#endif
#ifdef INT64_MAX
static inline int64_t guf_min_i64(int64_t a, int64_t b) { return a < b ? a : b; }
static inline int64_t guf_max_i64(int64_t a, int64_t b) { return a > b ? a : b; }
static inline int64_t guf_clamp_i64(int64_t x, int64_t min, int64_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
#endif
// Unsigned min, max, clamp functions (generated with libguf/tools/min_max_clamp-gen.py)
static inline unsigned char guf_min_uchar(unsigned char a, unsigned char b) { return a < b ? a : b; } static inline unsigned char guf_min_uchar(unsigned char a, unsigned char b) { return a < b ? a : b; }
static inline unsigned char guf_max_uchar(unsigned char a, unsigned char b) { return a > b ? a : b; } static inline unsigned char guf_max_uchar(unsigned char a, unsigned char b) { return a > b ? a : b; }
static inline unsigned char guf_clamp_uchar(unsigned char x, unsigned char min, unsigned char max) { if (x < min) {return min;} if (x > max) {return max;} return x; } static inline unsigned char guf_clamp_uchar(unsigned char x, unsigned char min, unsigned char max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
@ -84,52 +155,104 @@ static inline unsigned long long guf_min_ulong_long(unsigned long long a, unsign
static inline unsigned long long guf_max_ulong_long(unsigned long long a, unsigned long long b) { return a > b ? a : b; } static inline unsigned long long guf_max_ulong_long(unsigned long long a, unsigned long long b) { return a > b ? a : b; }
static inline unsigned long long guf_clamp_ulong_long(unsigned long long x, unsigned long long min, unsigned long long max) { if (x < min) {return min;} if (x > max) {return max;} return x; } static inline unsigned long long guf_clamp_ulong_long(unsigned long long x, unsigned long long min, unsigned long long max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline uint8_t guf_min_u8(uint8_t a, uint8_t b) { return a < b ? a : b; }
static inline uint8_t guf_max_u8(uint8_t a, uint8_t b) { return a > b ? a : b; }
static inline uint8_t guf_clamp_u8(uint8_t x, uint8_t min, uint8_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline uint16_t guf_min_u16(uint16_t a, uint16_t b) { return a < b ? a : b; }
static inline uint16_t guf_max_u16(uint16_t a, uint16_t b) { return a > b ? a : b; }
static inline uint16_t guf_clamp_u16(uint16_t x, uint16_t min, uint16_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline uint32_t guf_min_u32(uint32_t a, uint32_t b) { return a < b ? a : b; }
static inline uint32_t guf_max_u32(uint32_t a, uint32_t b) { return a > b ? a : b; }
static inline uint32_t guf_clamp_u32(uint32_t x, uint32_t min, uint32_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline uint64_t guf_min_u64(uint64_t a, uint64_t b) { return a < b ? a : b; }
static inline uint64_t guf_max_u64(uint64_t a, uint64_t b) { return a > b ? a : b; }
static inline uint64_t guf_clamp_u64(uint64_t x, uint64_t min, uint64_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline size_t guf_min_size_t(size_t a, size_t b) { return a < b ? a : b; } static inline size_t guf_min_size_t(size_t a, size_t b) { return a < b ? a : b; }
static inline size_t guf_max_size_t(size_t a, size_t b) { return a > b ? a : b; } static inline size_t guf_max_size_t(size_t a, size_t b) { return a > b ? a : b; }
static inline size_t guf_clamp_size_t(size_t x, size_t min, size_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; } static inline size_t guf_clamp_size_t(size_t x, size_t min, size_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline uint_least8_t guf_min_least_u8(uint_least8_t a, uint_least8_t b) { return a < b ? a : b; }
static inline uint_least8_t guf_max_least_u8(uint_least8_t a, uint_least8_t b) { return a > b ? a : b; }
static inline uint_least8_t guf_clamp_least_u8(uint_least8_t x, uint_least8_t min, uint_least8_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline uint_least16_t guf_min_least_u16(uint_least16_t a, uint_least16_t b) { return a < b ? a : b; }
static inline uint_least16_t guf_max_least_u16(uint_least16_t a, uint_least16_t b) { return a > b ? a : b; }
static inline uint_least16_t guf_clamp_least_u16(uint_least16_t x, uint_least16_t min, uint_least16_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline uint_least32_t guf_min_least_u32(uint_least32_t a, uint_least32_t b) { return a < b ? a : b; }
static inline uint_least32_t guf_max_least_u32(uint_least32_t a, uint_least32_t b) { return a > b ? a : b; }
static inline uint_least32_t guf_clamp_least_u32(uint_least32_t x, uint_least32_t min, uint_least32_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
static inline uint_least64_t guf_min_least_u64(uint_least64_t a, uint_least64_t b) { return a < b ? a : b; }
static inline uint_least64_t guf_max_least_u64(uint_least64_t a, uint_least64_t b) { return a > b ? a : b; }
static inline uint_least64_t guf_clamp_least_u64(uint_least64_t x, uint_least64_t min, uint_least64_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
#ifdef UINT8_MAX
static inline uint8_t guf_min_u8(uint8_t a, uint8_t b) { return a < b ? a : b; }
static inline uint8_t guf_max_u8(uint8_t a, uint8_t b) { return a > b ? a : b; }
static inline uint8_t guf_clamp_u8(uint8_t x, uint8_t min, uint8_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
#endif
#ifdef UINT16_MAX
static inline uint16_t guf_min_u16(uint16_t a, uint16_t b) { return a < b ? a : b; }
static inline uint16_t guf_max_u16(uint16_t a, uint16_t b) { return a > b ? a : b; }
static inline uint16_t guf_clamp_u16(uint16_t x, uint16_t min, uint16_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
#endif
#ifdef UINT32_MAX
static inline uint32_t guf_min_u32(uint32_t a, uint32_t b) { return a < b ? a : b; }
static inline uint32_t guf_max_u32(uint32_t a, uint32_t b) { return a > b ? a : b; }
static inline uint32_t guf_clamp_u32(uint32_t x, uint32_t min, uint32_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
#endif
#ifdef UINT64_MAX
static inline uint64_t guf_min_u64(uint64_t a, uint64_t b) { return a < b ? a : b; }
static inline uint64_t guf_max_u64(uint64_t a, uint64_t b) { return a > b ? a : b; }
static inline uint64_t guf_clamp_u64(uint64_t x, uint64_t min, uint64_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
#endif
// abs functions with signed result (can fail/panic for abs(INT_TYPE_MIN) for platforms with two's complement signed ints; C2X for example guarantees two's complement) // abs functions with signed result (can fail/panic for abs(INT_TYPE_MIN) for platforms with two's complement signed ints; C2X for example guarantees two's complement)
static inline int guf_abs_int(int x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT_MAX == INT_MIN || x > INT_MIN); return -x;} // I would not drink that... // static inline int guf_abs_int(int x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT_MAX == INT_MIN || x > INT_MIN); return -x;} // I would not drink that...
static inline int8_t guf_abs_i8 (int8_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT8_MAX == INT8_MIN || x > INT8_MIN); return -x;} // static inline int8_t guf_abs_i8 (int8_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT8_MAX == INT8_MIN || x > INT8_MIN); return -x;}
static inline int16_t guf_abs_i16(int16_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT16_MAX == INT16_MIN || x > INT16_MIN); return -x;} // static inline int16_t guf_abs_i16(int16_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT16_MAX == INT16_MIN || x > INT16_MIN); return -x;}
static inline int32_t guf_abs_i32(int32_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT32_MAX == INT32_MIN || x > INT32_MIN); return -x;} // static inline int32_t guf_abs_i32(int32_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT32_MAX == INT32_MIN || x > INT32_MIN); return -x;}
static inline int64_t guf_abs_i64(int64_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT64_MAX == INT64_MIN || x > INT64_MIN); return -x;} // static inline int64_t guf_abs_i64(int64_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT64_MAX == INT64_MIN || x > INT64_MIN); return -x;}
static inline ptrdiff_t guf_abs_ptrdiff(ptrdiff_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-PTRDIFF_MAX == PTRDIFF_MIN || x > PTRDIFF_MIN); return -x;} // static inline ptrdiff_t guf_abs_ptrdiff(ptrdiff_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-PTRDIFF_MAX == PTRDIFF_MIN || x > PTRDIFF_MIN); return -x;}
// abs functions with unsigned result functions (cannot fail) // abs functions with unsigned result functions (cannot fail)
static inline unsigned guf_uabs_int(int x) {if (x >= 0) {return x;} else if (x == INT_MIN && -INT_MAX != INT_MIN) {return (unsigned)INT_MAX + 1u;} else {return -x;}} static inline unsigned guf_uabs_int(int x) {if (x >= 0) {return x;} else if (x == INT_MIN && -INT_MAX != INT_MIN) {return (unsigned)INT_MAX + 1u;} else {return -x;}}
static inline uint8_t guf_uabs_i8(int8_t x) {if (x >= 0) {return x;} else if (x == INT8_MIN && -INT8_MAX != INT8_MIN) {return (uint8_t)INT8_MAX + 1u;} else {return -x;}} static inline unsigned long guf_uabs_long(long x) {if (x >= 0) {return x;} else if (x == LONG_MIN && -LONG_MAX != LONG_MIN) {return (unsigned long)LONG_MAX + 1u;} else {return -x;}}
static inline uint16_t guf_uabs_i16(int16_t x) {if (x >= 0) {return x;} else if (x == INT16_MIN && -INT16_MAX != INT16_MIN) {return (uint16_t)INT16_MAX + 1u;} else {return -x;}} static inline unsigned long long guf_uabs_long_long(long long x) {if (x >= 0) {return x;} else if (x == LLONG_MIN && -LLONG_MAX != LONG_MIN) {return (unsigned long long)LLONG_MAX + 1u;} else {return -x;}}
static inline uint32_t guf_uabs_i32(int32_t x) {if (x >= 0) {return x;} else if (x == INT32_MIN && -INT32_MAX != INT32_MIN) {return (uint32_t)INT32_MAX + 1u;} else {return -x;}}
static inline uint64_t guf_uabs_i64(int64_t x) {if (x >= 0) {return x;} else if (x == INT64_MIN && -INT64_MAX != INT64_MIN) {return (uint64_t)INT64_MAX + 1u;} else {return -x;}}
static inline size_t guf_uabs_ptrdiff_t(ptrdiff_t x) {if (x >= 0) {return x;} else if (x == PTRDIFF_MIN && -PTRDIFF_MAX != PTRDIFF_MIN) {return (size_t)PTRDIFF_MAX + 1u;} else {return -x;}} static inline size_t guf_uabs_ptrdiff_t(ptrdiff_t x) {if (x >= 0) {return x;} else if (x == PTRDIFF_MIN && -PTRDIFF_MAX != PTRDIFF_MIN) {return (size_t)PTRDIFF_MAX + 1u;} else {return -x;}}
#if defined(UINT8_MAX) && defined(INT8_MAX)
static inline uint8_t guf_uabs_i8(int8_t x) {if (x >= 0) {return x;} else if (x == INT8_MIN) {return (uint8_t)INT8_MAX + (uint8_t)1u;} else {return (uint8_t)-x;}}
#endif
#if defined(UINT16_MAX) && defined(INT16_MAX)
static inline uint16_t guf_uabs_i16(int16_t x) {if (x >= 0) {return x;} else if (x == INT16_MIN) {return (uint16_t)INT16_MAX + (uint16_t)1u;} else {return (uint16_t)-x;}}
#endif
#if defined(UINT32_MAX) && defined(INT32_MAX)
static inline uint32_t guf_uabs_i32(int32_t x) {if (x >= 0) {return x;} else if (x == INT32_MIN) {return (uint32_t)INT32_MAX + (uint32_t)1u;} else {return (uint32_t)-x;}}
#endif
#if defined(UINT64_MAX) && defined(INT64_MAX)
static inline uint64_t guf_uabs_i64(int64_t x) {if (x >= 0) {return x;} else if (x == INT64_MIN) {return (uint64_t)INT64_MAX + (uint64_t)1u;} else {return (uint64_t)-x;}}
#endif
// absdiff functions with unsigned result (cannot fail) // absdiff functions with unsigned result (cannot fail)
static inline unsigned char guf_absdiff_char(char a, char b) {return a > b ? (unsigned char)a - (unsigned char)b : (unsigned char)b - (unsigned char)a;} static inline unsigned char guf_absdiff_char(char a, char b) {return a > b ? (unsigned char)a - (unsigned char)b : (unsigned char)b - (unsigned char)a;}
static inline unsigned short guf_absdiff_short(short a, short b) {return a > b ? (unsigned short)a - (unsigned short)b : (unsigned short)b - (unsigned short)a;}
static inline unsigned guf_absdiff_int(int a, int b) {return a > b ? (unsigned)a - (unsigned)b : (unsigned)b - (unsigned)a;} static inline unsigned guf_absdiff_int(int a, int b) {return a > b ? (unsigned)a - (unsigned)b : (unsigned)b - (unsigned)a;}
static inline uint8_t guf_absdiff_i8(int8_t a, int8_t b) {return a > b ? (uint8_t)a - (uint8_t)b : (uint8_t)b - (uint8_t)a;} static inline unsigned long guf_absdiff_long(long a, long b) {return a > b ? (unsigned long)a - (unsigned long)b : (unsigned long)b - (unsigned long)a;}
static inline uint16_t guf_absdiff_i16(int16_t a, int16_t b) {return a > b ? (uint16_t)a - (uint16_t)b : (uint16_t)b - (uint16_t)a;} static inline unsigned long long guf_absdiff_long_long(long long a, long long b) {return a > b ? (unsigned long long)a - (unsigned long long)b : (unsigned long long)b - (unsigned long long)a;}
static inline uint32_t guf_absdiff_i32(int32_t a, int32_t b) {return a > b ? (uint32_t)a - (uint32_t)b : (uint32_t)b - (uint32_t)a;}
static inline uint64_t guf_absdiff_i64(int64_t a, int64_t b) {return a > b ? (uint64_t)a - (uint64_t)b : (uint64_t)b - (uint64_t)a;}
static inline size_t guf_absdiff_ptrdiff_t(ptrdiff_t a, ptrdiff_t b) {return a > b ? (size_t)a - (size_t)b : (size_t)b - (size_t)a;} static inline size_t guf_absdiff_ptrdiff_t(ptrdiff_t a, ptrdiff_t b) {return a > b ? (size_t)a - (size_t)b : (size_t)b - (size_t)a;}
static inline uint_least8_t guf_absdiff_least_i8(int_least8_t a, int_least8_t b) {return a > b ? GUF_UWRAP_8( (uint_least8_t)a - (uint_least8_t)b) : GUF_UWRAP_8( (uint_least8_t)b - (uint_least8_t)a);}
static inline uint_least16_t guf_absdiff_least_i16(int_least16_t a, int_least16_t b) {return a > b ? GUF_UWRAP_16((uint_least16_t)a - (uint_least16_t)b) : GUF_UWRAP_16((uint_least16_t)b - (uint_least16_t)a);}
static inline uint_least32_t guf_absdiff_least_i32(int_least32_t a, int_least32_t b) {return a > b ? GUF_UWRAP_32((uint_least32_t)a - (uint_least32_t)b) : GUF_UWRAP_32((uint_least32_t)b - (uint_least32_t)a);}
static inline uint_least64_t guf_absdiff_least_i64(int_least64_t a, int_least64_t b) {return a > b ? GUF_UWRAP_64((uint_least64_t)a - (uint_least64_t)b) : GUF_UWRAP_64((uint_least64_t)b - (uint_least64_t)a);}
#if defined(UINT8_MAX) && defined(INT8_MAX)
static inline uint8_t guf_absdiff_i8(int8_t a, int8_t b) {return a > b ? (uint8_t)a - (uint8_t)b : (uint8_t)b - (uint8_t)a;}
#endif
#if defined(UINT16_MAX) && defined(INT16_MAX)
static inline uint16_t guf_absdiff_i16(int16_t a, int16_t b) {return a > b ? (uint16_t)a - (uint16_t)b : (uint16_t)b - (uint16_t)a;}
#endif
#if defined(UINT32_MAX) && defined(INT32_MAX)
static inline uint32_t guf_absdiff_i32(int32_t a, int32_t b) {return a > b ? (uint32_t)a - (uint32_t)b : (uint32_t)b - (uint32_t)a;}
#endif
#if defined(UINT64_MAX) && defined(INT64_MAX)
static inline uint64_t guf_absdiff_i64(int64_t a, int64_t b) {return a > b ? (uint64_t)a - (uint64_t)b : (uint64_t)b - (uint64_t)a;}
#endif
static inline bool guf_add_is_overflow_size_t(size_t a, size_t b) static inline bool guf_add_is_overflow_size_t(size_t a, size_t b)
{ {
@ -164,12 +287,24 @@ static inline bool guf_size_calc_safe(ptrdiff_t count, ptrdiff_t sizeof_elem, pt
// cf. https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 (last-retrieved 2025-03-19) // cf. https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 (last-retrieved 2025-03-19)
static inline bool guf_is_pow2_u8(uint8_t x) { return x && !(x & (x - 1)); } static inline bool guf_is_pow2_uchar(unsigned char x) { return x && !(x & (x - 1)); }
static inline bool guf_is_pow2_u16(uint16_t x) { return x && !(x & (x - 1)); } static inline bool guf_is_pow2_ushort(unsigned short x) { return x && !(x & (x - 1)); }
static inline bool guf_is_pow2_u32(uint32_t x) { return x && !(x & (x - 1)); } static inline bool guf_is_pow2_unsigned(unsigned x) { return x && !(x & (x - 1)); }
static inline bool guf_is_pow2_u64(uint64_t x) { return x && !(x & (x - 1)); } static inline bool guf_is_pow2_ulong(unsigned long x) { return x && !(x & (x - 1)); }
static inline bool guf_is_pow2_ulong_long(unsigned long long x) { return x && !(x & (x - 1)); }
static inline bool guf_is_pow2_size_t(size_t x) { return x && !(x & (x - 1)); } static inline bool guf_is_pow2_size_t(size_t x) { return x && !(x & (x - 1)); }
#ifdef UINT8_MAX
static inline bool guf_is_pow2_u8(uint8_t x) { return x && !(x & (x - 1)); }
#endif
#ifdef UINT16_MAX
static inline bool guf_is_pow2_u16(uint16_t x) { return x && !(x & (x - 1)); }
#endif
#ifdef UINT32_MAX
static inline bool guf_is_pow2_u32(uint32_t x) { return x && !(x & (x - 1)); }
#endif
#if UINT64_MAX
static inline bool guf_is_pow2_u64(uint64_t x) { return x && !(x & (x - 1)); }
#endif
static bool guf_nearly_zero_f32(float x, float eps) {return fabsf(x) <= eps;} static bool guf_nearly_zero_f32(float x, float eps) {return fabsf(x) <= eps;}
static bool guf_nearly_one_f32(float x, float eps) {return fabsf(x - 1) <= eps;} static bool guf_nearly_one_f32(float x, float eps) {return fabsf(x - 1) <= eps;}

3834
src/guf_math_ckdint.h Normal file → Executable file

File diff suppressed because it is too large Load Diff

436
src/guf_rand.h Normal file → Executable file
View File

@ -26,29 +26,24 @@
// State for xoshiro128** 1.1 // State for xoshiro128** 1.1
typedef struct guf_rand32_state { typedef struct guf_rand32_state {
uint32_t s[4]; // Must not be all zero. uint_least32_t s[4]; // Must not be all zero.
} guf_rand32_state; } guf_rand32_state;
// State for xoshiro256** 1.0 // State for xoshiro256** 1.0
#ifdef UINT64_MAX
typedef struct guf_rand64_state { typedef struct guf_rand64_state {
uint64_t s[4]; // Must not be all zero. uint_least64_t s[4]; // Must not be all zero.
} guf_rand64_state; } guf_rand64_state;
#endif
#ifdef GUF_RAND_32_BIT #ifdef GUF_RAND_32_BIT
// Use guf_rand32_state (i.e. xoshiro128** 1.1) as default. // Use guf_rand32_state (i.e. xoshiro128** 1.1) as default.
#define GUF_RAND_MAX UINT32_MAX #define GUF_RAND_MAX GUF_UINT32_MAX
typedef guf_rand32_state guf_randstate; typedef guf_rand32_state guf_randstate;
typedef uint32_t guf_rand_seed_t; typedef uint_least32_t guf_rand_seed_t;
#else #else
// Use guf_rand64_state (i.e. xoshiro256** 1.0) as default. // Use guf_rand64_state (i.e. xoshiro256** 1.0) as default.
#ifndef UINT64_MAX #define GUF_RAND_MAX GUF_UINT64_MAX
#error "guf_rand.h: Platform does not support uint64_t (define GUF_RAND_32_BIT to fix)"
#endif
#define GUF_RAND_MAX UINT64_MAX
typedef guf_rand64_state guf_randstate; typedef guf_rand64_state guf_randstate;
typedef uint64_t guf_rand_seed_t; typedef uint_least64_t guf_rand_seed_t;
#endif #endif
/* /*
@ -60,76 +55,59 @@ typedef struct guf_rand32_state {
(If you want to initialise the guf_randstate struct manually, you have to ensure yourself the four state-integers aren't all zero.) (If you want to initialise the guf_randstate struct manually, you have to ensure yourself the four state-integers aren't all zero.)
*/ */
GUF_RAND_KWRDS void guf_randstate_init(guf_randstate *state, guf_rand_seed_t seed); GUF_RAND_KWRDS void guf_randstate_init(guf_randstate *state, guf_rand_seed_t seed);
#ifdef UINT64_MAX GUF_RAND_KWRDS void guf_rand64_state_init(guf_rand64_state *state, uint_least64_t seed);
GUF_RAND_KWRDS void guf_rand64_state_init(guf_rand64_state *state, uint64_t seed); GUF_RAND_KWRDS void guf_rand32_state_init(guf_rand32_state *state, uint_least32_t seed);
#endif
GUF_RAND_KWRDS void guf_rand32_state_init(guf_rand32_state *state, uint32_t seed);
/* /*
- guf_randstate_jump(state) - guf_randstate_jump(state)
-> void; advance the rng-state as if 2^128 (or 2^64 for rand32_state) calls to guf_rand_u32 had occured. -> void; advance the rng-state as if 2^128 (or 2^64 for rand32_state) calls to guf_rand_u32 had occured.
Can be used to generate 2^128 (or 2^64 for rand32_state) non-overlapping subsequences for parallel computations. Can be used to generate 2^128 (or 2^64 for rand32_state) non-overlapping subsequences for parallel computations.
*/ */
GUF_RAND_KWRDS void guf_randstate_jump(guf_randstate *state); // Equivalent to 2^128 (or 2^64) calls to guf_rand_u32 GUF_RAND_KWRDS void guf_randstate_jump(guf_randstate *state); // Equivalent to 2^128 (or 2^64) calls to guf_rand_u32/u64
#ifdef UINT64_MAX
GUF_RAND_KWRDS void guf_rand64_state_jump(guf_rand64_state *state); // Equivalent to 2^128 calls to guf_rand64_u64 GUF_RAND_KWRDS void guf_rand64_state_jump(guf_rand64_state *state); // Equivalent to 2^128 calls to guf_rand64_u64
#endif
GUF_RAND_KWRDS void guf_rand32_state_jump(guf_rand32_state *state); // Equivalent to 2^64 calls to guf_rand32_u32 GUF_RAND_KWRDS void guf_rand32_state_jump(guf_rand32_state *state); // Equivalent to 2^64 calls to guf_rand32_u32
// Uniform distributions in default ranges: // Uniform distributions in default ranges:
/* /*
- guf_rand_splitmix64(state) -> uint64_t in range [0, UINT64_MAX] - guf_rand_splitmix64(state) -> u64 in range [0, UINT64_MAX]
(Very simple rng with only 64-bits of state; used for "scrambling" 64-bit seeds in guf_randstate_init.) (Very simple rng with only 64-bits of state; used for "scrambling" 64-bit seeds in guf_randstate_init.)
- guf_rand_splitmix32(state) -> uint32_t in range [0, UINT32_MAX] - guf_rand_splitmix32(state) -> u32 in range [0, UINT32_MAX]
(Very simple rng with only 32-bits of state; used for "scrambling" 32-bit seeds in guf_randstate_init.) (Very simple rng with only 32-bits of state; used for "scrambling" 32-bit seeds in guf_randstate_init.)
*/ */
#ifdef UINT64_MAX GUF_RAND_KWRDS uint_least64_t guf_rand_splitmix64(uint_least64_t *state);
GUF_RAND_KWRDS uint64_t guf_rand_splitmix64(uint64_t *state); GUF_RAND_KWRDS uint_least32_t guf_rand_splitmix32(uint_least32_t *state);
#endif
GUF_RAND_KWRDS uint32_t guf_rand_splitmix32(uint32_t *state);
/* /*
- guf_rand_u32(state) -> uint32_t in range [0, UINT32_MAX] - guf_rand_u32(state) -> u32 in range [0, UINT32_MAX]
*/ */
GUF_RAND_KWRDS uint32_t guf_rand_u32(guf_randstate *state); GUF_RAND_KWRDS uint_least32_t guf_rand_u32(guf_randstate *state);
#ifdef UINT64_MAX GUF_RAND_KWRDS uint_least32_t guf_rand64_u32(guf_rand64_state *state);
GUF_RAND_KWRDS uint32_t guf_rand64_u32(guf_rand64_state *state); GUF_RAND_KWRDS uint_least32_t guf_rand32_u32(guf_rand32_state *state);
#endif
GUF_RAND_KWRDS uint32_t guf_rand32_u32(guf_rand32_state *state);
/* /*
- guf_rand_u64(state) -> uint64_t (or uint_least64_t) in range [0, UINT64_MAX] - guf_rand_u64(state) -> uint64_t (or uint_least64_t) in range [0, UINT64_MAX]
NOTE: May be slow on 32-bit platforms. NOTE: May be slow on 32-bit platforms.
NOTE: If uint64_t is not available (optional according to the standards), use uint_least64_t (always available in C99 and above). NOTE: If uint64_t is not available (optional according to the standards), use uint_least64_t (always available in C99 and above).
*/ */
#ifdef UINT64_MAX GUF_RAND_KWRDS uint_least64_t guf_rand_u64(guf_randstate *state);
GUF_RAND_KWRDS uint64_t guf_rand_u64(guf_randstate *state);
GUF_RAND_KWRDS uint64_t guf_rand32_u64(guf_rand32_state *state);
GUF_RAND_KWRDS uint64_t guf_rand64_u64(guf_rand64_state *state);
#else
GUF_RAND_KWRDS uint_least64_t guf_rand_u64(guf_randstate *state)
GUF_RAND_KWRDS uint_least64_t guf_rand32_u64(guf_rand32_state *state); GUF_RAND_KWRDS uint_least64_t guf_rand32_u64(guf_rand32_state *state);
#endif GUF_RAND_KWRDS uint_least64_t guf_rand64_u64(guf_rand64_state *state);
/* /*
- guf_rand_f64(state) -> double in range [0.0, 1.0) - guf_rand_f64(state) -> double in range [0.0, 1.0)
NOTE: May be slow on 32-bit platforms (as it calls guf_rand_u64) NOTE: May be slow on 32-bit platforms (as it calls guf_rand_u64)
*/ */
GUF_RAND_KWRDS double guf_rand_f64(guf_randstate *state); GUF_RAND_KWRDS double guf_rand_f64(guf_randstate *state);
#ifdef UINT64_MAX
GUF_RAND_KWRDS double guf_rand64_f64(guf_rand64_state *state); GUF_RAND_KWRDS double guf_rand64_f64(guf_rand64_state *state);
#endif
GUF_RAND_KWRDS double guf_rand32_f64(guf_rand32_state *state); GUF_RAND_KWRDS double guf_rand32_f64(guf_rand32_state *state);
/* /*
- guf_rand_f32(state) -> float in range [0.f, 1.f) - guf_rand_f32(state) -> float in range [0.f, 1.f)
*/ */
GUF_RAND_KWRDS float guf_rand_f32(guf_randstate *state); GUF_RAND_KWRDS float guf_rand_f32(guf_randstate *state);
#ifdef UINT64_MAX
GUF_RAND_KWRDS float guf_rand64_f32(guf_rand64_state *state); GUF_RAND_KWRDS float guf_rand64_f32(guf_rand64_state *state);
#endif
GUF_RAND_KWRDS float guf_rand32_f32(guf_rand32_state *state); GUF_RAND_KWRDS float guf_rand32_f32(guf_rand32_state *state);
@ -142,39 +120,34 @@ GUF_RAND_KWRDS float guf_rand32_f32(guf_rand32_state *state);
*/ */
GUF_RAND_KWRDS float guf_randrange_f32(guf_randstate *state, float min, float end); GUF_RAND_KWRDS float guf_randrange_f32(guf_randstate *state, float min, float end);
GUF_RAND_KWRDS double guf_randrange_f64(guf_randstate *state, double min, double end); GUF_RAND_KWRDS double guf_randrange_f64(guf_randstate *state, double min, double end);
#ifdef UINT64_MAX
GUF_RAND_KWRDS float guf_rand64_range_f32(guf_rand64_state *state, float min, float end); GUF_RAND_KWRDS float guf_rand64_range_f32(guf_rand64_state *state, float min, float end);
GUF_RAND_KWRDS double guf_rand64_range_f64(guf_rand64_state *state, double min, double end); GUF_RAND_KWRDS double guf_rand64_range_f64(guf_rand64_state *state, double min, double end);
#endif
GUF_RAND_KWRDS float guf_rand32_range_f32(guf_rand32_state *state, float min, float end); GUF_RAND_KWRDS float guf_rand32_range_f32(guf_rand32_state *state, float min, float end);
GUF_RAND_KWRDS double guf_rand32_range_f64(guf_rand32_state *state, double min, double end); GUF_RAND_KWRDS double guf_rand32_range_f64(guf_rand32_state *state, double min, double end);
/* /*
- guf_randrange_i32(state, min, max) -> int32_t in range [min, max] (contrary to the float equivalents, max *is* inclusive) - guf_randrange_i32(state, min, max) -> i32 in range [min, max] (contrary to the float equivalents, max *is* inclusive)
- guf_randrange_u32(state, min, max) -> uint32_t in range [min, max] (contrary to the float equivalents, max *is* inclusive) - guf_randrange_u32(state, min, max) -> u32 in range [min, max] (contrary to the float equivalents, max *is* inclusive)
NOTE: guf_randrange_u32 may be slow on 32-bit platforms (as it calls guf_rand_f64). NOTE: guf_randrange_u32 may be slow on 32-bit platforms (as it calls guf_rand_f64).
This does not apply to guf_randrange_i32 (as it doesn't call guf_rand_f64). This does not apply to guf_randrange_i32 (as it doesn't call guf_rand_f64).
*/ */
GUF_RAND_KWRDS int32_t guf_randrange_i32(guf_randstate *state, int32_t min, int32_t max); GUF_RAND_KWRDS int_least32_t guf_randrange_i32(guf_randstate *state, int_least32_t min, int_least32_t max);
GUF_RAND_KWRDS uint32_t guf_randrange_u32(guf_randstate *state, uint32_t min, uint32_t max); // NOTE: may be slow on 32-bit platforms. GUF_RAND_KWRDS uint_least32_t guf_randrange_u32(guf_randstate *state, uint_least32_t min, uint_least32_t max); // NOTE: may be slow on 32-bit platforms (as it calls guf_rand_f64).
#ifdef UINT64_MAX
GUF_RAND_KWRDS int32_t guf_rand64_range_i32(guf_rand64_state *state, int32_t min, int32_t max); GUF_RAND_KWRDS int_least32_t guf_rand64_range_i32(guf_rand64_state *state, int_least32_t min, int_least32_t max);
GUF_RAND_KWRDS uint32_t guf_rand64_range_u32(guf_rand64_state *state, uint32_t min, uint32_t max); GUF_RAND_KWRDS uint_least32_t guf_rand64_range_u32(guf_rand64_state *state, uint_least32_t min, uint_least32_t max);
#endif
GUF_RAND_KWRDS int32_t guf_rand32_range_i32(guf_rand32_state *state, int32_t min, int32_t max); GUF_RAND_KWRDS int_least32_t guf_rand32_range_i32(guf_rand32_state *state, int_least32_t min, int_least32_t max);
GUF_RAND_KWRDS uint32_t guf_rand32_range_u32(guf_rand32_state *state, uint32_t min, uint32_t max); // NOTE: may be slow on 32-bit platforms. GUF_RAND_KWRDS uint_least32_t guf_rand32_range_u32(guf_rand32_state *state, uint_least32_t min, uint_least32_t max); // NOTE: may be slow on 32-bit platforms (as it calls guf_rand_f64).
/* /*
- guf_randrange_i64(state, min, max) -> int64_t in range [min, max] (contrary to the float equivalents, max *is* inclusive) - guf_randrange_i64(state, min, max) -> int64_t in range [min, max] (contrary to the float equivalents, max *is* inclusive)
NOTE: The Generic version is only available if GUF_RAND_32_BIT is undefined and the platform supports uint64_t.
(The specific guf_rand64_range_i64 version is available as long as the platform supports uint64_t.)
*/ */
#if defined(UINT64_MAX) && !defined(GUF_RAND_32_BIT) GUF_RAND_KWRDS int_least64_t guf_randrange_i64(guf_randstate *state, int_least64_t min, int_least64_t max);
GUF_RAND_KWRDS int64_t guf_randrange_i64(guf_randstate *state, int64_t min, int64_t max); GUF_RAND_KWRDS int_least64_t guf_rand64_range_i64(guf_rand64_state *state, int_least64_t min, int_least64_t max);
#endif GUF_RAND_KWRDS int_least64_t guf_rand32_range_i64(guf_rand32_state *state, int_least64_t min, int_least64_t max);
#ifdef UINT64_MAX
GUF_RAND_KWRDS int64_t guf_rand64_range_i64(guf_rand64_state *state, int64_t min, int64_t max);
#endif
// Bernoulli-trials: // Bernoulli-trials:
@ -187,11 +160,11 @@ GUF_RAND_KWRDS uint32_t guf_rand32_range_u32(guf_rand32_state *state, uint32_t m
GUF_RAND_KWRDS bool guf_rand_bernoulli_trial_f32(guf_randstate *state, float p); GUF_RAND_KWRDS bool guf_rand_bernoulli_trial_f32(guf_randstate *state, float p);
GUF_RAND_KWRDS bool guf_rand_bernoulli_trial_f64(guf_randstate *state, double p); GUF_RAND_KWRDS bool guf_rand_bernoulli_trial_f64(guf_randstate *state, double p);
GUF_RAND_KWRDS bool guf_rand_flip(guf_randstate *state); GUF_RAND_KWRDS bool guf_rand_flip(guf_randstate *state);
#ifdef UINT64_MAX
GUF_RAND_KWRDS bool guf_rand64_bernoulli_trial_f32(guf_rand64_state *state, float p); GUF_RAND_KWRDS bool guf_rand64_bernoulli_trial_f32(guf_rand64_state *state, float p);
GUF_RAND_KWRDS bool guf_rand64_bernoulli_trial_f64(guf_rand64_state *state, double p); GUF_RAND_KWRDS bool guf_rand64_bernoulli_trial_f64(guf_rand64_state *state, double p);
GUF_RAND_KWRDS bool guf_rand64_flip(guf_rand64_state *state); GUF_RAND_KWRDS bool guf_rand64_flip(guf_rand64_state *state);
#endif
GUF_RAND_KWRDS bool guf_rand32_bernoulli_trial_f32(guf_rand32_state *state, float p); GUF_RAND_KWRDS bool guf_rand32_bernoulli_trial_f32(guf_rand32_state *state, float p);
GUF_RAND_KWRDS bool guf_rand32_bernoulli_trial_f64(guf_rand32_state *state, double p); GUF_RAND_KWRDS bool guf_rand32_bernoulli_trial_f64(guf_rand32_state *state, double p);
GUF_RAND_KWRDS bool guf_rand32_flip(guf_rand32_state *state); GUF_RAND_KWRDS bool guf_rand32_flip(guf_rand32_state *state);
@ -209,12 +182,12 @@ GUF_RAND_KWRDS void guf_rand_normal_sample_f64(guf_randstate *state, double mean
GUF_RAND_KWRDS void guf_rand_normal_sample_f32(guf_randstate *state, float mean, float std_dev, float *result, ptrdiff_t n); GUF_RAND_KWRDS void guf_rand_normal_sample_f32(guf_randstate *state, float mean, float std_dev, float *result, ptrdiff_t n);
GUF_RAND_KWRDS double guf_rand_normal_sample_one_f64(guf_randstate *state, double mean, double std_dev); GUF_RAND_KWRDS double guf_rand_normal_sample_one_f64(guf_randstate *state, double mean, double std_dev);
GUF_RAND_KWRDS float guf_rand_normal_sample_one_f32(guf_randstate *state, float mean, float std_dev); GUF_RAND_KWRDS float guf_rand_normal_sample_one_f32(guf_randstate *state, float mean, float std_dev);
#ifdef UINT64_MAX
GUF_RAND_KWRDS void guf_rand64_normal_sample_f32(guf_rand64_state *state, float mean, float std_dev, float *result, ptrdiff_t n); GUF_RAND_KWRDS void guf_rand64_normal_sample_f32(guf_rand64_state *state, float mean, float std_dev, float *result, ptrdiff_t n);
GUF_RAND_KWRDS void guf_rand64_normal_sample_f64(guf_rand64_state *state, double mean, double std_dev, double *result, ptrdiff_t n); GUF_RAND_KWRDS void guf_rand64_normal_sample_f64(guf_rand64_state *state, double mean, double std_dev, double *result, ptrdiff_t n);
GUF_RAND_KWRDS float guf_rand64_normal_sample_one_f32(guf_rand64_state *state, float mean, float std_dev); GUF_RAND_KWRDS float guf_rand64_normal_sample_one_f32(guf_rand64_state *state, float mean, float std_dev);
GUF_RAND_KWRDS double guf_rand64_normal_sample_one_f64(guf_rand64_state *state, double mean, double std_dev); GUF_RAND_KWRDS double guf_rand64_normal_sample_one_f64(guf_rand64_state *state, double mean, double std_dev);
#endif
GUF_RAND_KWRDS void guf_rand32_normal_sample_f32(guf_rand32_state *state, float mean, float std_dev, float *result, ptrdiff_t n); GUF_RAND_KWRDS void guf_rand32_normal_sample_f32(guf_rand32_state *state, float mean, float std_dev, float *result, ptrdiff_t n);
GUF_RAND_KWRDS void guf_rand32_normal_sample_f64(guf_rand32_state *state, double mean, double std_dev, double *result, ptrdiff_t n); GUF_RAND_KWRDS void guf_rand32_normal_sample_f64(guf_rand32_state *state, double mean, double std_dev, double *result, ptrdiff_t n);
GUF_RAND_KWRDS float guf_rand32_normal_sample_one_f32(guf_rand32_state *state, float mean, float std_dev); GUF_RAND_KWRDS float guf_rand32_normal_sample_one_f32(guf_rand32_state *state, float mean, float std_dev);
@ -222,6 +195,8 @@ GUF_RAND_KWRDS double guf_rand32_normal_sample_one_f64(guf_rand32_state *state,
#endif #endif
// #define GUF_RAND_IMPL_STATIC /* DEBUG */
#if defined(GUF_RAND_IMPL) || defined(GUF_RAND_IMPL_STATIC) #if defined(GUF_RAND_IMPL) || defined(GUF_RAND_IMPL_STATIC)
#include <math.h> #include <math.h>
#include <float.h> #include <float.h>
@ -231,35 +206,34 @@ GUF_RAND_KWRDS double guf_rand32_normal_sample_one_f64(guf_rand32_state *state,
#define GUF_MATH_CKDINT_IMPL_STATIC #define GUF_MATH_CKDINT_IMPL_STATIC
#include "guf_math_ckdint.h" #include "guf_math_ckdint.h"
#ifdef UINT64_MAX
/* /*
splitmix64 written in 2015 by Sebastiano Vigna (vigna@acm.org) (released as public domain) splitmix64 written in 2015 by Sebastiano Vigna (vigna@acm.org) (released as public domain)
cf. https://prng.di.unimi.it/splitmix64.c (last-retrieved 2025-02-11) cf. https://prng.di.unimi.it/splitmix64.c (last-retrieved 2025-02-11)
*/ */
GUF_RAND_KWRDS uint64_t guf_rand_splitmix64(uint64_t *state) GUF_RAND_KWRDS uint_least64_t guf_rand_splitmix64(uint_least64_t *state)
{ {
GUF_ASSERT(state); GUF_ASSERT(state);
uint64_t z = ((*state) += 1u * 0x9e3779b97f4a7c15); *state = GUF_UWRAP_64(*state);
z = (z ^ (z >> 30u)) * 0xbf58476d1ce4e5b9; uint_least64_t z = ( *state = GUF_UWRAP_64(*state + 0x9e3779b97f4a7c15ull) );
z = (z ^ (z >> 27u)) * 0x94d049bb133111eb; z = GUF_UWRAP_64( GUF_UWRAP_64(z ^ (z >> 30u)) * 0xbf58476d1ce4e5b9ull );
return z ^ (z >> 31u); z = GUF_UWRAP_64( GUF_UWRAP_64(z ^ (z >> 27u)) * 0x94d049bb133111ebull );
return GUF_UWRAP_64(z ^ (z >> 31u));
} }
#endif
/* /*
splitmix32 written in 2016 by Kaito Udagawa (released under CC0 <http://creativecommons.org/publicdomain/zero/1.0/>) splitmix32 written in 2016 by Kaito Udagawa (released under CC0 <http://creativecommons.org/publicdomain/zero/1.0/>)
cf. https://github.com/umireon/my-random-stuff/blob/master/xorshift/splitmix32.c (last-retrieved 2025-03-28) cf. https://github.com/umireon/my-random-stuff/blob/master/xorshift/splitmix32.c (last-retrieved 2025-03-28)
*/ */
GUF_RAND_KWRDS uint32_t guf_rand_splitmix32(uint32_t *state) GUF_RAND_KWRDS uint_least32_t guf_rand_splitmix32(uint_least32_t *state)
{ {
GUF_ASSERT(state); GUF_ASSERT(state);
uint32_t z = (*state += 1u * 0x9e3779b9); uint_least32_t z = ( *state = GUF_UWRAP_32(*state + 0x9e3779b9ul) );
z = (z ^ (z >> 16u)) * 0x85ebca6b; z = GUF_UWRAP_32( GUF_UWRAP_32(z ^ (z >> 16u)) * 0x85ebca6bul );
z = (z ^ (z >> 13u)) * 0xc2b2ae35; z = GUF_UWRAP_32( GUF_UWRAP_32(z ^ (z >> 13u)) * 0xc2b2ae35ul );
return z ^ (z >> 16u); return GUF_UWRAP_32(z ^ (z >> 16u));
} }
GUF_RAND_KWRDS void guf_rand32_state_init(guf_rand32_state *state, uint32_t seed) GUF_RAND_KWRDS void guf_rand32_state_init(guf_rand32_state *state, uint_least32_t seed)
{ {
for (size_t i = 0; i < GUF_ARR_SIZE(state->s); ++i) { for (size_t i = 0; i < GUF_ARR_SIZE(state->s); ++i) {
state->s[i] = guf_rand_splitmix32(&seed); state->s[i] = guf_rand_splitmix32(&seed);
@ -273,8 +247,7 @@ GUF_RAND_KWRDS void guf_rand32_state_init(guf_rand32_state *state, uint32_t seed
} }
} }
#ifdef UINT64_MAX GUF_RAND_KWRDS void guf_rand64_state_init(guf_rand64_state *state, uint_least64_t seed)
GUF_RAND_KWRDS void guf_rand64_state_init(guf_rand64_state *state, uint64_t seed)
{ {
for (size_t i = 0; i < GUF_ARR_SIZE(state->s); ++i) { for (size_t i = 0; i < GUF_ARR_SIZE(state->s); ++i) {
state->s[i] = guf_rand_splitmix64(&seed); state->s[i] = guf_rand_splitmix64(&seed);
@ -287,7 +260,6 @@ GUF_RAND_KWRDS void guf_rand32_state_init(guf_rand32_state *state, uint32_t seed
} }
} }
} }
#endif
GUF_RAND_KWRDS void guf_randstate_init(guf_randstate *state, guf_rand_seed_t seed) GUF_RAND_KWRDS void guf_randstate_init(guf_randstate *state, guf_rand_seed_t seed)
{ {
@ -298,7 +270,7 @@ GUF_RAND_KWRDS void guf_randstate_init(guf_randstate *state, guf_rand_seed_t see
#endif #endif
} }
GUF_RAND_KWRDS uint32_t guf_rand32_u32(guf_rand32_state *state) GUF_RAND_KWRDS uint_least32_t guf_rand32_u32(guf_rand32_state *state)
{ {
GUF_ASSERT(state); GUF_ASSERT(state);
GUF_ASSERT(state->s[0] || state->s[1] || state->s[2] || state->s[3]); GUF_ASSERT(state->s[0] || state->s[1] || state->s[2] || state->s[3]);
@ -306,25 +278,30 @@ GUF_RAND_KWRDS uint32_t guf_rand32_u32(guf_rand32_state *state)
xoshiro128** 1.1 (public domain) written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) xoshiro128** 1.1 (public domain) written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
cf. https://prng.di.unimi.it/xoshiro128starstar.c (last-retrieved 2025-02-11) cf. https://prng.di.unimi.it/xoshiro128starstar.c (last-retrieved 2025-02-11)
*/ */
const uint32_t result = guf_rotl_u32(state->s[1] * 5u, 7) * 9u; const uint_least32_t result = GUF_UWRAP_32( guf_rotl32_least_u32(state->s[1] * 5u, 7) * 9u );
const uint32_t t = state->s[1] << 9u; const uint_least32_t t = GUF_UWRAP_32( state->s[1] << 9u );
state->s[2] ^= state->s[0]; state->s[2] ^= state->s[0];
state->s[3] ^= state->s[1]; state->s[3] ^= state->s[1];
state->s[1] ^= state->s[2]; state->s[1] ^= state->s[2];
state->s[0] ^= state->s[3]; state->s[0] ^= state->s[3];
state->s[2] ^= t; state->s[2] ^= t;
state->s[3] = guf_rotl_u32(state->s[3], 11); state->s[3] = guf_rotl32_least_u32(state->s[3], 11);
state->s[0] = GUF_UWRAP_32(state->s[0]);
state->s[1] = GUF_UWRAP_32(state->s[1]);
state->s[2] = GUF_UWRAP_32(state->s[2]);
state->s[3] = GUF_UWRAP_32(state->s[3]);
return result; return result;
} }
#ifdef UINT64_MAX GUF_RAND_KWRDS uint_least32_t guf_rand64_u32(guf_rand64_state *state)
GUF_RAND_KWRDS uint32_t guf_rand64_u32(guf_rand64_state *state)
{ {
return (uint32_t)(guf_rand64_u64(state) >> 32u); return (uint_least32_t)GUF_UWRAP_32( (guf_rand64_u64(state) >> 32u) );
} }
#endif
GUF_RAND_KWRDS uint32_t guf_rand_u32(guf_randstate *state) GUF_RAND_KWRDS uint_least32_t guf_rand_u32(guf_randstate *state)
{ {
GUF_ASSERT(state); GUF_ASSERT(state);
GUF_ASSERT(state->s[0] || state->s[1] || state->s[2] || state->s[3]); GUF_ASSERT(state->s[0] || state->s[1] || state->s[2] || state->s[3]);
@ -336,8 +313,7 @@ GUF_RAND_KWRDS uint32_t guf_rand_u32(guf_randstate *state)
} }
#ifdef UINT64_MAX GUF_RAND_KWRDS uint_least64_t guf_rand64_u64(guf_rand64_state *state)
GUF_RAND_KWRDS uint64_t guf_rand64_u64(guf_rand64_state *state)
{ {
GUF_ASSERT(state); GUF_ASSERT(state);
GUF_ASSERT(state->s[0] || state->s[1] || state->s[2] || state->s[3]); GUF_ASSERT(state->s[0] || state->s[1] || state->s[2] || state->s[3]);
@ -345,41 +321,37 @@ GUF_RAND_KWRDS uint32_t guf_rand_u32(guf_randstate *state)
xoshiro256** 1.0 (public domain) written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) xoshiro256** 1.0 (public domain) written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
cf. https://prng.di.unimi.it/xoshiro256starstar.c (last-retrieved 2025-02-11) cf. https://prng.di.unimi.it/xoshiro256starstar.c (last-retrieved 2025-02-11)
*/ */
const uint64_t result = guf_rotl_u64(state->s[1] * 5u, 7) * 9u; const uint_least64_t result = GUF_UWRAP_64( guf_rotl64_least_u64(state->s[1] * 5u, 7) * 9u );
const uint64_t t = state->s[1] << 17u; const uint_least64_t t = GUF_UWRAP_64( state->s[1] << 17u );
state->s[2] ^= state->s[0]; state->s[2] ^= state->s[0];
state->s[3] ^= state->s[1]; state->s[3] ^= state->s[1];
state->s[1] ^= state->s[2]; state->s[1] ^= state->s[2];
state->s[0] ^= state->s[3]; state->s[0] ^= state->s[3];
state->s[2] ^= t; state->s[2] ^= t;
state->s[3] = guf_rotl_u64(state->s[3], 45); state->s[3] = guf_rotl64_least_u64(state->s[3], 45);
state->s[0] = GUF_UWRAP_64(state->s[0]);
state->s[1] = GUF_UWRAP_64(state->s[1]);
state->s[2] = GUF_UWRAP_64(state->s[2]);
state->s[3] = GUF_UWRAP_64(state->s[3]);
return result; return result;
} }
#endif
#ifdef UINT64_MAX
GUF_RAND_KWRDS uint64_t guf_rand32_u64(guf_rand32_state *state)
#else
GUF_RAND_KWRDS uint_least64_t guf_rand32_u64(guf_rand32_state *state) GUF_RAND_KWRDS uint_least64_t guf_rand32_u64(guf_rand32_state *state)
#endif
{ {
GUF_ASSERT(state); GUF_ASSERT(state);
GUF_ASSERT(state->s[0] || state->s[1] || state->s[2] || state->s[3]); GUF_ASSERT(state->s[0] || state->s[1] || state->s[2] || state->s[3]);
const uint32_t lower_bits = guf_rand32_u32(state); const uint_least32_t lower_bits = guf_rand32_u32(state);
const uint32_t upper_bits = guf_rand32_u32(state); const uint_least32_t upper_bits = guf_rand32_u32(state);
#ifdef UINT64_MAX //GUF_ASSERT( lower_bits <= GUF_UINT32_MAX && upper_bits <= GUF_UINT32_MAX );
return ((uint64_t)upper_bits << 32u) | (uint64_t)lower_bits; // TODO: not sure if that's a good idea... GUF_ASSERT( ( ((uint_least64_t)upper_bits << 32u) | (uint_least64_t)lower_bits ) <= GUF_UINT32_MAX);
#else
return ((uint_least64_t)upper_bits << 32u) | (uint_least64_t)lower_bits; // TODO: not sure if that's a good idea... return ((uint_least64_t)upper_bits << 32u) | (uint_least64_t)lower_bits; // TODO: not sure if that's a good idea...
#endif
} }
#ifdef UINT64_MAX
GUF_RAND_KWRDS uint64_t guf_rand_u64(guf_randstate *state)
#else
GUF_RAND_KWRDS uint_least64_t guf_rand_u64(guf_randstate *state) GUF_RAND_KWRDS uint_least64_t guf_rand_u64(guf_randstate *state)
#endif
{ {
#ifdef GUF_RAND_32_BIT #ifdef GUF_RAND_32_BIT
return guf_rand32_u64(state); return guf_rand32_u64(state);
@ -395,59 +367,63 @@ GUF_RAND_KWRDS uint_least64_t guf_rand_u64(guf_randstate *state)
GUF_RAND_KWRDS void guf_rand32_state_jump(guf_rand32_state *state) GUF_RAND_KWRDS void guf_rand32_state_jump(guf_rand32_state *state)
{ {
GUF_ASSERT(state); GUF_ASSERT(state);
static const uint32_t JUMP[] = { 0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b }; static const uint_least32_t JUMP[] = { 0x8764000b, 0xf542d2d3, 0x6fa035c3, 0x77f2db5b };
uint32_t s0 = 0; uint_least32_t s0 = 0;
uint32_t s1 = 0; uint_least32_t s1 = 0;
uint32_t s2 = 0; uint_least32_t s2 = 0;
uint32_t s3 = 0; uint_least32_t s3 = 0;
for (size_t i = 0; i < sizeof JUMP / sizeof *JUMP; ++i) { for (size_t i = 0; i < sizeof JUMP / sizeof *JUMP; ++i) {
for (int b = 0; b < 32; ++b) { for (int b = 0; b < 32; ++b) {
if (JUMP[i] & UINT32_C(1) << b) { if (1u * JUMP[i] & UINT32_C(1) << b) {
s0 ^= state->s[0]; s0 ^= state->s[0];
s1 ^= state->s[1]; s1 ^= state->s[1];
s2 ^= state->s[2]; s2 ^= state->s[2];
s3 ^= state->s[3]; s3 ^= state->s[3];
s0 = GUF_UWRAP_32(s0);
s1 = GUF_UWRAP_32(s1);
s2 = GUF_UWRAP_32(s2);
s3 = GUF_UWRAP_32(s3);
} }
guf_rand32_u32(state); guf_rand32_u32(state);
} }
} }
state->s[0] = s0; state->s[0] = GUF_UWRAP_32(s0);
state->s[1] = s1; state->s[1] = GUF_UWRAP_32(s1);
state->s[2] = s2; state->s[2] = GUF_UWRAP_32(s2);
state->s[3] = s3; state->s[3] = GUF_UWRAP_32(s3);
} }
#ifdef UINT64_MAX
/* /*
Equivalent to 2^128 calls to guf_rand64_u64(); can be used to generate 2^128 Equivalent to 2^128 calls to guf_rand64_u64(); can be used to generate 2^128
non-overlapping subsequences for parallel computations. non-overlapping subsequences for parallel computations.
*/ */
GUF_RAND_KWRDS void guf_rand64_state_jump(guf_rand64_state *state) GUF_RAND_KWRDS void guf_rand64_state_jump(guf_rand64_state *state)
{ {
static const uint64_t JUMP[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c }; static const uint_least64_t JUMP[] = { 0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c };
uint64_t s0 = 0; uint_least64_t s0 = 0;
uint64_t s1 = 0; uint_least64_t s1 = 0;
uint64_t s2 = 0; uint_least64_t s2 = 0;
uint64_t s3 = 0; uint_least64_t s3 = 0;
for (size_t i = 0; i < sizeof JUMP / sizeof *JUMP; ++i) { for (size_t i = 0; i < sizeof JUMP / sizeof *JUMP; ++i) {
for (int b = 0; b < 64; ++b) { for (int b = 0; b < 64; ++b) {
if (JUMP[i] & UINT64_C(1) << b) { if (1u * JUMP[i] & UINT64_C(1) << b) {
s0 ^= state->s[0]; s0 ^= state->s[0];
s1 ^= state->s[1]; s1 ^= state->s[1];
s2 ^= state->s[2]; s2 ^= state->s[2];
s3 ^= state->s[3]; s3 ^= state->s[3];
s0 = GUF_UWRAP_64(s0);
s1 = GUF_UWRAP_64(s1);
s2 = GUF_UWRAP_64(s2);
s3 = GUF_UWRAP_64(s3);
} }
guf_rand64_u64(state); guf_rand64_u64(state);
} }
} }
state->s[0] = s0; state->s[0] = GUF_UWRAP_64(s0);
state->s[1] = s1; state->s[1] = GUF_UWRAP_64(s1);
state->s[2] = s2; state->s[2] = GUF_UWRAP_64(s2);
state->s[3] = s3; state->s[3] = GUF_UWRAP_64(s3);
} }
#endif
/* /*
Equivalent to 2^128 calls to guf_rand() (or 2^64 calls if GUF_RAND_32_BIT); it can be used to generate 2^128 (or 2^64) Equivalent to 2^128 calls to guf_rand() (or 2^64 calls if GUF_RAND_32_BIT); it can be used to generate 2^128 (or 2^64)
@ -463,37 +439,35 @@ GUF_RAND_KWRDS void guf_randstate_jump(guf_randstate *state)
#endif #endif
} }
#ifdef UINT64_MAX
GUF_RAND_KWRDS double guf_rand64_f64(guf_rand64_state *state) GUF_RAND_KWRDS double guf_rand64_f64(guf_rand64_state *state)
{ {
// cf. https://prng.di.unimi.it/ and https://dotat.at/@/2023-06-23-random-double.html (last-retrieved 2025-02-11) // cf. https://prng.di.unimi.it/ and https://dotat.at/@/2023-06-23-random-double.html (last-retrieved 2025-02-11)
return (guf_rand64_u64(state) >> 11u) * 0x1.0p-53; // 11 == 64 - 53 (double has a 53-bit mantissa/significand) return (double)(guf_rand64_u64(state) >> 11u) * 0x1.0p-53; // 11 == 64 - 53 (double has a 53-bit mantissa/significand)
} }
#endif
GUF_RAND_KWRDS double guf_rand32_f64(guf_rand32_state *state) GUF_RAND_KWRDS double guf_rand32_f64(guf_rand32_state *state)
{ {
// cf. https://prng.di.unimi.it/ and https://dotat.at/@/2023-06-23-random-double.html (last-retrieved 2025-02-11) // cf. https://prng.di.unimi.it/ and https://dotat.at/@/2023-06-23-random-double.html (last-retrieved 2025-02-11)
return (guf_rand32_u64(state) >> 11u) * 0x1.0p-53; // 11 == 64 - 53 (double has a 53-bit mantissa/significand) return (double)(guf_rand32_u64(state) >> 11u) * 0x1.0p-53; // 11 == 64 - 53 (double has a 53-bit mantissa/significand)
} }
// Generate double in the unit interval [0, 1) // Generate double in the unit interval [0, 1)
GUF_RAND_KWRDS double guf_rand_f64(guf_randstate *state) GUF_RAND_KWRDS double guf_rand_f64(guf_randstate *state)
{ {
// cf. https://prng.di.unimi.it/ and https://dotat.at/@/2023-06-23-random-double.html (last-retrieved 2025-02-11) // cf. https://prng.di.unimi.it/ and https://dotat.at/@/2023-06-23-random-double.html (last-retrieved 2025-02-11)
return (guf_rand_u64(state) >> 11u) * 0x1.0p-53; // 11 == 64 - 53 (double has a 53-bit mantissa/significand) return (double)(guf_rand_u64(state) >> 11u) * 0x1.0p-53; // 11 == 64 - 53 (double has a 53-bit mantissa/significand)
} }
#ifdef UINT64_MAX
GUF_RAND_KWRDS float guf_rand64_f32(guf_rand64_state *state) GUF_RAND_KWRDS float guf_rand64_f32(guf_rand64_state *state)
{ {
return (guf_rand64_u64(state) >> 40u) * 0x1.0p-24f; // 40 == 64 - 24; (float has a 24-bit mantissa/significand) return (float)(guf_rand64_u64(state) >> 40u) * 0x1.0p-24f; // 40 == 64 - 24; (float has a 24-bit mantissa/significand)
} }
#endif
GUF_RAND_KWRDS float guf_rand32_f32(guf_rand32_state *state) GUF_RAND_KWRDS float guf_rand32_f32(guf_rand32_state *state)
{ {
return (guf_rand32_u32(state) >> 8u) * 0x1.0p-24f; // 8 == 32 - 24; (float has a 24-bit mantissa/significand) return (float)(guf_rand32_u32(state) >> 8u) * 0x1.0p-24f; // 8 == 32 - 24; (float has a 24-bit mantissa/significand)
} }
// Generate float in the unit interval [0, 1) // Generate float in the unit interval [0, 1)
@ -512,13 +486,12 @@ GUF_RAND_KWRDS bool guf_rand32_bernoulli_trial_f32(guf_rand32_state *state, floa
p = guf_clamp_f32(p, 0, 1); p = guf_clamp_f32(p, 0, 1);
return guf_rand32_f32(state) < p; // never true for p = 0, always true for p = 1 since guf_rand_f64 is in range [0, 1) return guf_rand32_f32(state) < p; // never true for p = 0, always true for p = 1 since guf_rand_f64 is in range [0, 1)
} }
#ifdef UINT64_MAX
GUF_RAND_KWRDS bool guf_rand64_bernoulli_trial_f32(guf_rand64_state *state, float p) GUF_RAND_KWRDS bool guf_rand64_bernoulli_trial_f32(guf_rand64_state *state, float p)
{ {
p = guf_clamp_f32(p, 0, 1); p = guf_clamp_f32(p, 0, 1);
return guf_rand64_f32(state) < p; // never true for p = 0, always true for p = 1 since guf_rand_f64 is in range [0, 1) return guf_rand64_f32(state) < p; // never true for p = 0, always true for p = 1 since guf_rand_f64 is in range [0, 1)
} }
#endif
GUF_RAND_KWRDS bool guf_rand_bernoulli_trial_f32(guf_randstate *state, float p) GUF_RAND_KWRDS bool guf_rand_bernoulli_trial_f32(guf_randstate *state, float p)
{ {
@ -532,13 +505,11 @@ GUF_RAND_KWRDS bool guf_rand32_bernoulli_trial_f64(guf_rand32_state *state, doub
return guf_rand32_f64(state) < p; // never true for p = 0, always true for p = 1 since guf_rand_f64 is in range [0, 1) return guf_rand32_f64(state) < p; // never true for p = 0, always true for p = 1 since guf_rand_f64 is in range [0, 1)
} }
#ifdef UINT64_MAX
GUF_RAND_KWRDS bool guf_rand64_bernoulli_trial_f64(guf_rand64_state *state, double p) GUF_RAND_KWRDS bool guf_rand64_bernoulli_trial_f64(guf_rand64_state *state, double p)
{ {
p = guf_clamp_f64(p, 0, 1); p = guf_clamp_f64(p, 0, 1);
return guf_rand64_f64(state) < p; // never true for p = 0, always true for p = 1 since guf_rand_f64 is in range [0, 1) return guf_rand64_f64(state) < p; // never true for p = 0, always true for p = 1 since guf_rand_f64 is in range [0, 1)
} }
#endif
GUF_RAND_KWRDS bool guf_rand_bernoulli_trial_f64(guf_randstate *state, double p) GUF_RAND_KWRDS bool guf_rand_bernoulli_trial_f64(guf_randstate *state, double p)
{ {
@ -546,17 +517,16 @@ GUF_RAND_KWRDS bool guf_rand_bernoulli_trial_f64(guf_randstate *state, double p)
return guf_rand_f64(state) < p; // never true for p = 0, always true for p = 1 since guf_rand_f64 is in range [0, 1) return guf_rand_f64(state) < p; // never true for p = 0, always true for p = 1 since guf_rand_f64 is in range [0, 1)
} }
GUF_RAND_KWRDS bool guf_rand32_flip(guf_rand32_state *state) GUF_RAND_KWRDS bool guf_rand32_flip(guf_rand32_state *state)
{ {
return guf_rand32_bernoulli_trial_f32(state, 0.5f); return guf_rand32_bernoulli_trial_f32(state, 0.5f);
} }
#ifdef UINT64_MAX
GUF_RAND_KWRDS bool guf_rand64_flip(guf_rand64_state *state) GUF_RAND_KWRDS bool guf_rand64_flip(guf_rand64_state *state)
{ {
return guf_rand64_bernoulli_trial_f64(state, 0.5); return guf_rand64_bernoulli_trial_f64(state, 0.5);
} }
#endif
GUF_RAND_KWRDS bool guf_rand_flip(guf_randstate *state) GUF_RAND_KWRDS bool guf_rand_flip(guf_randstate *state)
{ {
@ -567,6 +537,7 @@ GUF_RAND_KWRDS bool guf_rand_flip(guf_randstate *state)
#endif #endif
} }
GUF_RAND_KWRDS double guf_rand32_range_f64(guf_rand32_state *state, double min, double end) GUF_RAND_KWRDS double guf_rand32_range_f64(guf_rand32_state *state, double min, double end)
{ {
if (min == (double)INFINITY) { if (min == (double)INFINITY) {
@ -583,7 +554,6 @@ GUF_RAND_KWRDS double guf_rand32_range_f64(guf_rand32_state *state, double min,
return guf_rand32_f64(state) * (end - min) + min; return guf_rand32_f64(state) * (end - min) + min;
} }
#ifdef UINT64_MAX
GUF_RAND_KWRDS double guf_rand64_range_f64(guf_rand64_state *state, double min, double end) GUF_RAND_KWRDS double guf_rand64_range_f64(guf_rand64_state *state, double min, double end)
{ {
if (min == (double)INFINITY) { if (min == (double)INFINITY) {
@ -599,7 +569,6 @@ GUF_RAND_KWRDS double guf_rand32_range_f64(guf_rand32_state *state, double min,
GUF_ASSERT_RELEASE(end >= min); GUF_ASSERT_RELEASE(end >= min);
return guf_rand64_f64(state) * (end - min) + min; return guf_rand64_f64(state) * (end - min) + min;
} }
#endif
// returns uniformly-distributed random double in range [min, end) (or min if min == end) // returns uniformly-distributed random double in range [min, end) (or min if min == end)
GUF_RAND_KWRDS double guf_randrange_f64(guf_randstate *state, double min, double end) GUF_RAND_KWRDS double guf_randrange_f64(guf_randstate *state, double min, double end)
@ -611,6 +580,7 @@ GUF_RAND_KWRDS double guf_randrange_f64(guf_randstate *state, double min, double
#endif #endif
} }
// returns uniformly-distributed random float in range [min, end) (or min if min == end) // returns uniformly-distributed random float in range [min, end) (or min if min == end)
GUF_RAND_KWRDS float guf_rand32_range_f32(guf_rand32_state *state, float min, float end) GUF_RAND_KWRDS float guf_rand32_range_f32(guf_rand32_state *state, float min, float end)
{ {
@ -628,7 +598,6 @@ GUF_RAND_KWRDS float guf_rand32_range_f32(guf_rand32_state *state, float min, fl
return guf_rand32_f32(state) * (end - min) + min; return guf_rand32_f32(state) * (end - min) + min;
} }
#ifdef UINT64_MAX
// returns uniformly-distributed random float in range [min, end) (or min if min == end) // returns uniformly-distributed random float in range [min, end) (or min if min == end)
GUF_RAND_KWRDS float guf_rand64_range_f32(guf_rand64_state *state, float min, float end) GUF_RAND_KWRDS float guf_rand64_range_f32(guf_rand64_state *state, float min, float end)
{ {
@ -645,7 +614,6 @@ GUF_RAND_KWRDS float guf_rand32_range_f32(guf_rand32_state *state, float min, fl
GUF_ASSERT_RELEASE(end >= min); GUF_ASSERT_RELEASE(end >= min);
return guf_rand64_f32(state) * (end - min) + min; return guf_rand64_f32(state) * (end - min) + min;
} }
#endif
// returns uniformly-distributed random float in range [min, end) (or min if min == end) // returns uniformly-distributed random float in range [min, end) (or min if min == end)
GUF_RAND_KWRDS float guf_randrange_f32(guf_randstate *state, float min, float end) GUF_RAND_KWRDS float guf_randrange_f32(guf_randstate *state, float min, float end)
@ -658,9 +626,8 @@ GUF_RAND_KWRDS float guf_randrange_f32(guf_randstate *state, float min, float en
} }
#ifdef UINT64_MAX // returns uniformly-distributed random i32 in range [min, max] (max is inclusive as opposed to the f32/f64 versions)
// returns uniformly-distributed random int32_t in range [min, max] (max is inclusive as opposed to the f32/f64 versions) GUF_RAND_KWRDS int_least32_t guf_rand64_range_i32(guf_rand64_state *state, int_least32_t min, int_least32_t max)
GUF_RAND_KWRDS int32_t guf_rand64_range_i32(guf_rand64_state *state, int32_t min, int32_t max)
{ {
GUF_ASSERT_RELEASE(max >= min); GUF_ASSERT_RELEASE(max >= min);
if (min == max) { if (min == max) {
@ -670,23 +637,23 @@ GUF_RAND_KWRDS float guf_randrange_f32(guf_randstate *state, float min, float en
// cf. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random (last-retrieved 2025-02-12) // cf. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random (last-retrieved 2025-02-12)
const double result = floor(guf_rand64_f64(state) * (delta + 1.0) + min); const double result = floor(guf_rand64_f64(state) * (delta + 1.0) + min);
GUF_ASSERT(result >= min && result <= max); GUF_ASSERT(result >= min && result <= max);
return (int32_t)result; GUF_ASSERT((int_least32_t)result <= GUF_INT32_MAX && (int_least32_t)result >= GUF_INT32_MIN);
return (int_least32_t)result;
} }
#endif
// returns uniformly-distributed random int32_t in range [min, max] (max is inclusive as opposed to the f32/f64 versions) // returns uniformly-distributed random i32 in range [min, max] (max is inclusive as opposed to the f32/f64 versions)
GUF_RAND_KWRDS int32_t guf_rand32_range_i32(guf_rand32_state *state, int32_t min, int32_t max) GUF_RAND_KWRDS int_least32_t guf_rand32_range_i32(guf_rand32_state *state, int_least32_t min, int_least32_t max)
{ {
GUF_ASSERT_RELEASE(max >= min); GUF_ASSERT_RELEASE(max >= min);
if (min == max) { if (min == max) {
return min; return min;
} }
const uint32_t rand_max_i32 = UINT32_MAX >> 1u; // 2^31 - 1 (== INT32_MAX) const uint_least32_t rand_max_i32 = GUF_UWRAP_32(GUF_UINT32_MAX >> 1u); // 2^31 - 1 (== INT32_MAX)
const uint32_t delta = guf_absdiff_i32(max, min); const uint_least32_t delta = guf_absdiff_least_i32(max, min);
if (delta > rand_max_i32) { if (delta > rand_max_i32) {
guf_panic(GUF_ERR_INT_OVERFLOW, GUF_ERR_MSG("in function guf_rand32_range_i32: interval [min, max] larger than INT32_MAX")); guf_panic(GUF_ERR_INT_OVERFLOW, GUF_ERR_MSG("in function guf_rand32_range_i32: interval [min, max] larger than 2^31 - 1"));
return -1; return -1;
} }
/* /*
@ -696,32 +663,34 @@ GUF_RAND_KWRDS int32_t guf_rand32_range_i32(guf_rand32_state *state, int32_t min
cf. https://c-faq.com/lib/randrange.html (last-retrieved 2025-02-11) cf. https://c-faq.com/lib/randrange.html (last-retrieved 2025-02-11)
https://stackoverflow.com/a/6852396 (last-retrieved 2025-02-11) https://stackoverflow.com/a/6852396 (last-retrieved 2025-02-11)
*/ */
const uint32_t num_rand_vals = rand_max_i32 + 1u; // 2^31 const uint_least32_t num_rand_vals = GUF_UWRAP_32(rand_max_i32 + 1u); // 2^31
const uint32_t num_bins = (delta + 1u); const uint_least32_t num_bins = GUF_UWRAP_32(delta + 1u);
const uint32_t bin_size = num_rand_vals / num_bins; // bin_size = floor(num_rand_vals / num_bins) const uint_least32_t bin_size = GUF_UWRAP_32(num_rand_vals / num_bins); // bin_size = floor(num_rand_vals / num_bins)
const uint32_t limit = num_rand_vals - (num_rand_vals % num_bins); // limit == bin_size * num_bins const uint_least32_t limit = GUF_UWRAP_32(num_rand_vals - (num_rand_vals % num_bins)); // limit == bin_size * num_bins
GUF_ASSERT(limit == bin_size * num_bins); GUF_ASSERT(limit == 1u * GUF_UWRAP_32(bin_size * num_bins));
/* /*
since (num_rand_vals % num_bins) is at most 2^30 + 1 (I think...), the minimum limit is 2^31 - (2^30 + 1), since (num_rand_vals % num_bins) is at most 2^30 + 1 (I think...), the minimum limit is 2^31 - (2^30 + 1),
which means in the worst case, the chance of having to iterate (i.e. step >= limit) which means in the worst case, the chance of having to iterate (i.e. step >= limit)
is 1 - (2^31 - (2^30 + 1)) / 2^31 == 0.5 is 1 - (2^31 - (2^30 + 1)) / 2^31 == 0.5
*/ */
uint32_t step; uint_least32_t step;
do { do {
step = guf_rand32_u32(state) >> 1u; // [0, 2^31 - 1] step = GUF_UWRAP_32(guf_rand32_u32(state) >> 1u); // [0, 2^31 - 1]
} while (step >= limit); } while (step >= limit);
step = step / bin_size; step = GUF_UWRAP_32(step / bin_size);
GUF_ASSERT(guf_ckd_add_least_i32(min, step) == GUF_MATH_CKD_SUCCESS);
GUF_ASSERT(guf_ckd_add_i32(min, step) == GUF_MATH_CKD_SUCCESS); const int_least32_t rnd = min + (int_least32_t)step;
const int32_t rnd = min + (int32_t)step;
GUF_ASSERT(rnd >= min && rnd <= max); GUF_ASSERT(rnd >= min && rnd <= max);
//GUF_ASSERT(rnd <= GUF_INT32_MAX && rnd >= GUF_INT32_MIN);
return rnd; return rnd;
} }
// returns uniformly-distributed random int32_t in range [min, max] (max is inclusive as opposed to the f32/f64 versions) // returns uniformly-distributed random i32 in range [min, max] (max is inclusive as opposed to the f32/f64 versions)
GUF_RAND_KWRDS int32_t guf_randrange_i32(guf_randstate *state, int32_t min, int32_t max) GUF_RAND_KWRDS int_least32_t guf_randrange_i32(guf_randstate *state, int_least32_t min, int_least32_t max)
{ {
#ifdef GUF_RAND_32_BIT #ifdef GUF_RAND_32_BIT
return guf_rand32_range_i32(state, min, max); return guf_rand32_range_i32(state, min, max);
@ -730,38 +699,45 @@ GUF_RAND_KWRDS int32_t guf_randrange_i32(guf_randstate *state, int32_t min, int3
#endif #endif
} }
GUF_RAND_KWRDS uint32_t guf_rand32_range_u32(guf_rand32_state *state, uint32_t min, uint32_t max) GUF_RAND_KWRDS uint_least32_t guf_rand32_range_u32(guf_rand32_state *state, uint_least32_t min, uint_least32_t max)
{ {
/* /*
The method used in guf_rand32_range_i32 above (which uses only 32-bit integer arithmetic) could overflow here, The method used in guf_rand32_range_i32 above (which uses only 32-bit integer arithmetic) could overflow here,
so we use the floating-point method since we have to use 64-bit arithmetic anyways. so we use the floating-point method since we have to use 64-bit arithmetic anyways.
*/ */
min = GUF_UWRAP_32(min);
max = GUF_UWRAP_32(max);
GUF_ASSERT_RELEASE(max >= min); GUF_ASSERT_RELEASE(max >= min);
if (min == max) { if (min == max) {
return min; return min;
} }
const double delta = (double)max - (double)min; const double delta = (double)max - (double)min;
const double result = floor(guf_rand32_f64(state) * (delta + 1.0) + min); // NOTE: guf_rand32_f64 is slow for 32-bit platforms... const double result = floor(guf_rand32_f64(state) * (delta + 1.0) + min); // NOTE: guf_rand32_f64 is slow for 32-bit platforms...
GUF_ASSERT(result >= min && result <= max); GUF_ASSERT(result >= min && result <= max);
return (uint32_t)result; GUF_ASSERT((uint_least32_t)result <= GUF_UINT32_MAX);
return (uint_least32_t)result;
} }
#ifdef UINT64_MAX GUF_RAND_KWRDS uint_least32_t guf_rand64_range_u32(guf_rand64_state *state, uint_least32_t min, uint_least32_t max)
GUF_RAND_KWRDS uint32_t guf_rand64_range_u32(guf_rand64_state *state, uint32_t min, uint32_t max)
{ {
min = GUF_UWRAP_32(min);
max = GUF_UWRAP_32(max);
GUF_ASSERT_RELEASE(max >= min); GUF_ASSERT_RELEASE(max >= min);
if (min == max) { if (min == max) {
return min; return min;
} }
const double delta = (double)max - (double)min; const double delta = (double)max - (double)min;
const double result = floor(guf_rand64_f64(state) * (delta + 1.0) + min); const double result = floor(guf_rand64_f64(state) * (delta + 1.0) + min);
GUF_ASSERT(result >= min && result <= max); GUF_ASSERT(result >= min && result <= max);
return (uint32_t)result; GUF_ASSERT((uint_least32_t)result <= GUF_UINT32_MAX);
return (uint_least32_t)result;
} }
#endif
GUF_RAND_KWRDS uint32_t guf_randrange_u32(guf_randstate *state, uint32_t min, uint32_t max) GUF_RAND_KWRDS uint_least32_t guf_randrange_u32(guf_randstate *state, uint_least32_t min, uint_least32_t max)
{ {
#ifdef GUF_RAND_32_BIT #ifdef GUF_RAND_32_BIT
return guf_rand32_range_u32(state, min, max); return guf_rand32_range_u32(state, min, max);
@ -770,19 +746,18 @@ GUF_RAND_KWRDS uint32_t guf_randrange_u32(guf_randstate *state, uint32_t min, ui
#endif #endif
} }
#ifdef UINT64_MAX GUF_RAND_KWRDS int_least64_t guf_rand64_range_i64(guf_rand64_state *state, int_least64_t min, int_least64_t max)
GUF_RAND_KWRDS int64_t guf_rand64_range_i64(guf_rand64_state *state, int64_t min, int64_t max)
{ {
GUF_ASSERT_RELEASE(max >= min); GUF_ASSERT_RELEASE(max >= min);
if (min == max) { if (min == max) {
return min; return min;
} }
const uint64_t rand_max_i64 = UINT64_MAX >> 1u; // 2^63 - 1 (== INT64_MAX) const uint_least64_t rand_max_i64 = GUF_UWRAP_64(GUF_UINT64_MAX >> 1u); // 2^63 - 1 (== INT64_MAX)
const uint64_t delta = guf_absdiff_i64(max, min); const uint_least64_t delta = guf_absdiff_least_i64(max, min);
if (delta > rand_max_i64) { if (delta > rand_max_i64) {
guf_panic(GUF_ERR_INT_OVERFLOW, GUF_ERR_MSG("in function guf_randrange_i64: interval [min, max] larger than INT64_MAX")); guf_panic(GUF_ERR_INT_OVERFLOW, GUF_ERR_MSG("in function guf_randrange_i64: interval [min, max] larger than 2^63 - 1"));
return -1; return -1;
} }
/* /*
@ -791,37 +766,84 @@ GUF_RAND_KWRDS uint32_t guf_randrange_u32(guf_randstate *state, uint32_t min, ui
cf. https://c-faq.com/lib/randrange.html (last-retrieved 2025-02-11) cf. https://c-faq.com/lib/randrange.html (last-retrieved 2025-02-11)
https://stackoverflow.com/a/6852396 (last-retrieved 2025-02-11) https://stackoverflow.com/a/6852396 (last-retrieved 2025-02-11)
*/ */
const uint64_t num_rand_vals = rand_max_i64 + 1u; // 2^63 const uint_least64_t num_rand_vals = GUF_UWRAP_64(rand_max_i64 + 1u); // 2^63
const uint64_t num_bins = (delta + 1u); const uint_least64_t num_bins = GUF_UWRAP_64(delta + 1u);
const uint64_t bin_size = num_rand_vals / num_bins; // bin_size = floor(num_rand_vals / num_bins) const uint_least64_t bin_size = GUF_UWRAP_64(num_rand_vals / num_bins); // bin_size = floor(num_rand_vals / num_bins)
const uint64_t limit = num_rand_vals - (num_rand_vals % num_bins); // limit == bin_size * num_bins const uint_least64_t limit = GUF_UWRAP_64(num_rand_vals - (num_rand_vals % num_bins)); // limit == bin_size * num_bins
GUF_ASSERT(limit == bin_size * num_bins); GUF_ASSERT(limit == 1u * GUF_UWRAP_64(bin_size * num_bins));
/* /*
since (num_rand_vals % num_bins) is at most 2^62 + 1 (I think...), the minimum limit is 2^63 - (2^62 + 1), since (num_rand_vals % num_bins) is at most 2^62 + 1 (I think...), the minimum limit is 2^63 - (2^62 + 1),
which means in the worst case, the chance of having to iterate (i.e. step >= limit) which means in the worst case, the chance of having to iterate (i.e. step >= limit)
is 1 - (2^63 - (2^62 + 1)) / 2^63 == 0.5 is 1 - (2^63 - (2^62 + 1)) / 2^63 == 0.5
*/ */
uint64_t step; uint_least64_t step;
do { do {
step = guf_rand64_u64(state) >> 1; // [0, 2^63 - 1] step = GUF_UWRAP_64(guf_rand64_u64(state) >> 1); // [0, 2^63 - 1]
} while (step >= limit); } while (step >= limit);
step = step / bin_size; step = GUF_UWRAP_64(step / bin_size);
GUF_ASSERT(guf_ckd_add_i64(min, step) == GUF_MATH_CKD_SUCCESS); GUF_ASSERT(guf_ckd_add_least_i64(min, step) == GUF_MATH_CKD_SUCCESS);
const int64_t rnd = min + (int64_t)step;
const int_least64_t rnd = min + (int_least64_t)step;
GUF_ASSERT(rnd >= min && rnd <= max); GUF_ASSERT(rnd >= min && rnd <= max);
return rnd; return rnd;
} }
#endif
#if !defined(GUF_RAND_32_BIT) && defined(UINT64_MAX) GUF_RAND_KWRDS int_least64_t guf_rand32_range_i64(guf_rand32_state *state, int_least64_t min, int_least64_t max)
// returns uniformly-distributed random int64_t in range [min, max] (max is inclusive as opposed to the f32/f64 versions)
GUF_RAND_KWRDS int64_t guf_randrange_i64(guf_randstate *state, int64_t min, int64_t max)
{ {
return guf_rand64_range_i64(state, min, max); GUF_ASSERT_RELEASE(max >= min);
if (min == max) {
return min;
} }
const uint_least64_t rand_max_i64 = GUF_UWRAP_64(GUF_UINT64_MAX >> 1u); // 2^63 - 1 (== INT64_MAX)
const uint_least64_t delta = guf_absdiff_least_i64(max, min);
if (delta > rand_max_i64) {
guf_panic(GUF_ERR_INT_OVERFLOW, GUF_ERR_MSG("in function guf_randrange_i64: interval [min, max] larger than 2^63 - 1"));
return -1;
}
/*
We should not use the same approach as in guf_rand64_range_i32 because (max - min) might be close to 2^63 - 1
cf. https://c-faq.com/lib/randrange.html (last-retrieved 2025-02-11)
https://stackoverflow.com/a/6852396 (last-retrieved 2025-02-11)
*/
const uint_least64_t num_rand_vals = GUF_UWRAP_64(rand_max_i64 + 1u); // 2^63
const uint_least64_t num_bins = GUF_UWRAP_64(delta + 1u);
const uint_least64_t bin_size = GUF_UWRAP_64(num_rand_vals / num_bins); // bin_size = floor(num_rand_vals / num_bins)
const uint_least64_t limit = GUF_UWRAP_64(num_rand_vals - (num_rand_vals % num_bins)); // limit == bin_size * num_bins
GUF_ASSERT(limit == 1u * GUF_UWRAP_64(bin_size * num_bins));
/*
since (num_rand_vals % num_bins) is at most 2^62 + 1 (I think...), the minimum limit is 2^63 - (2^62 + 1),
which means in the worst case, the chance of having to iterate (i.e. step >= limit)
is 1 - (2^63 - (2^62 + 1)) / 2^63 == 0.5
*/
uint_least64_t step;
do {
step = GUF_UWRAP_64(guf_rand32_u64(state) >> 1); // [0, 2^63 - 1]
} while (step >= limit);
step = GUF_UWRAP_64(step / bin_size);
GUF_ASSERT(guf_ckd_add_least_i64(min, step) == GUF_MATH_CKD_SUCCESS);
const int_least64_t rnd = min + (int_least64_t)step;
GUF_ASSERT(rnd >= min && rnd <= max);
return rnd;
}
// returns uniformly-distributed random int64_t in range [min, max] (max is inclusive as opposed to the f32/f64 versions)
GUF_RAND_KWRDS int_least64_t guf_randrange_i64(guf_randstate *state, int_least64_t min, int_least64_t max)
{
#ifdef GUF_RAND_32_BIT
return guf_rand32_range_i64(state, min, max);
#else
return guf_rand64_range_i64(state, min, max);
#endif #endif
}
// Box-Müller-transform transcribed from wikipedia, cf. https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform (last-retrieved 2025-02-12) // Box-Müller-transform transcribed from wikipedia, cf. https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform (last-retrieved 2025-02-12)
GUF_RAND_KWRDS void guf_rand32_normal_sample_f64(guf_rand32_state *state, double mean, double std_dev, double *result, ptrdiff_t n) GUF_RAND_KWRDS void guf_rand32_normal_sample_f64(guf_rand32_state *state, double mean, double std_dev, double *result, ptrdiff_t n)
@ -846,7 +868,6 @@ GUF_RAND_KWRDS void guf_rand32_normal_sample_f64(guf_rand32_state *state, double
} }
} }
#ifdef UINT64_MAX
// Box-Müller-transform transcribed from wikipedia, cf. https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform (last-retrieved 2025-02-12) // Box-Müller-transform transcribed from wikipedia, cf. https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform (last-retrieved 2025-02-12)
GUF_RAND_KWRDS void guf_rand64_normal_sample_f64(guf_rand64_state *state, double mean, double std_dev, double *result, ptrdiff_t n) GUF_RAND_KWRDS void guf_rand64_normal_sample_f64(guf_rand64_state *state, double mean, double std_dev, double *result, ptrdiff_t n)
{ {
@ -869,7 +890,6 @@ GUF_RAND_KWRDS void guf_rand32_normal_sample_f64(guf_rand32_state *state, double
} }
} }
} }
#endif
// Box-Müller-transform transcribed from wikipedia, cf. https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform (last-retrieved 2025-02-12) // Box-Müller-transform transcribed from wikipedia, cf. https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform (last-retrieved 2025-02-12)
GUF_RAND_KWRDS void guf_rand_normal_sample_f64(guf_randstate *state, double mean, double std_dev, double *result, ptrdiff_t n) GUF_RAND_KWRDS void guf_rand_normal_sample_f64(guf_randstate *state, double mean, double std_dev, double *result, ptrdiff_t n)
@ -903,7 +923,6 @@ GUF_RAND_KWRDS void guf_rand32_normal_sample_f32(guf_rand32_state *state, float
} }
} }
#ifdef UINT64_MAX
GUF_RAND_KWRDS void guf_rand64_normal_sample_f32(guf_rand64_state *state, float mean, float std_dev, float *result, ptrdiff_t n) GUF_RAND_KWRDS void guf_rand64_normal_sample_f32(guf_rand64_state *state, float mean, float std_dev, float *result, ptrdiff_t n)
{ {
GUF_ASSERT_RELEASE(result); GUF_ASSERT_RELEASE(result);
@ -925,7 +944,6 @@ GUF_RAND_KWRDS void guf_rand32_normal_sample_f32(guf_rand32_state *state, float
} }
} }
} }
#endif
GUF_RAND_KWRDS void guf_rand_normal_sample_f32(guf_randstate *state, float mean, float std_dev, float *result, ptrdiff_t n) GUF_RAND_KWRDS void guf_rand_normal_sample_f32(guf_randstate *state, float mean, float std_dev, float *result, ptrdiff_t n)
{ {
@ -943,14 +961,12 @@ GUF_RAND_KWRDS double guf_rand32_normal_sample_one_f64(guf_rand32_state *state,
return result; return result;
} }
#ifdef UINT64_MAX
GUF_RAND_KWRDS double guf_rand64_normal_sample_one_f64(guf_rand64_state *state, double mean, double std_dev) GUF_RAND_KWRDS double guf_rand64_normal_sample_one_f64(guf_rand64_state *state, double mean, double std_dev)
{ {
double result; double result;
guf_rand64_normal_sample_f64(state, mean, std_dev, &result, 1); guf_rand64_normal_sample_f64(state, mean, std_dev, &result, 1);
return result; return result;
} }
#endif
GUF_RAND_KWRDS double guf_rand_normal_sample_one_f64(guf_randstate *state, double mean, double std_dev) GUF_RAND_KWRDS double guf_rand_normal_sample_one_f64(guf_randstate *state, double mean, double std_dev)
{ {
@ -968,14 +984,12 @@ GUF_RAND_KWRDS float guf_rand32_normal_sample_one_f32(guf_rand32_state *state, f
return result; return result;
} }
#ifdef UINT64_MAX
GUF_RAND_KWRDS float guf_rand64_normal_sample_one_f32(guf_rand64_state *state, float mean, float std_dev) GUF_RAND_KWRDS float guf_rand64_normal_sample_one_f32(guf_rand64_state *state, float mean, float std_dev)
{ {
float result; float result;
guf_rand64_normal_sample_f32(state, mean, std_dev, &result, 1); guf_rand64_normal_sample_f32(state, mean, std_dev, &result, 1);
return result; return result;
} }
#endif
GUF_RAND_KWRDS float guf_rand_normal_sample_one_f32(guf_randstate *state, float mean, float std_dev) GUF_RAND_KWRDS float guf_rand_normal_sample_one_f32(guf_randstate *state, float mean, float std_dev)
{ {

0
src/guf_sort.h Normal file → Executable file
View File

143
src/guf_str.h Normal file → Executable file
View File

@ -70,14 +70,14 @@ typedef struct guf_str_tok_state {
#define GUF_STR_TO_VIEW(GUF_STR_PTR) ((guf_str_view){.str = guf_str_const_cstr((GUF_STR_PTR)), .len = (ptrdiff_t)guf_str_len((GUF_STR_PTR))}) #define GUF_STR_TO_VIEW(GUF_STR_PTR) ((guf_str_view){.str = guf_str_const_cstr((GUF_STR_PTR)), .len = (ptrdiff_t)guf_str_len((GUF_STR_PTR))})
#define GUF_CSTR_TO_READONLY_STR(CSTR) ((guf_str){.allocator = NULL, .data.lng.c_str = (CSTR), .data.lng.size = strlen(CSTR) + 1, .data.lng.capacity = 0}) #define GUF_CSTR_TO_READONLY_STR(CSTR) ((guf_str){.allocator = NULL, .data.lng.c_str = (CSTR), .data.lng.size = strlen(CSTR) + 1, .data.lng.capacity = 0})
#define GUF_STR_UNINITIALISED (guf_str){.allocator = NULL, .data.shrt.size = 0, .data.shrt.c_str[0] = '\0'} #define GUF_STR_UNINITIALISED (guf_str){.allocator = NULL, .data.lng.size = 0, .data.lng.capacity = 0, .data.lng.c_str = NULL}
#ifdef __cplusplus #ifdef __cplusplus
// Standard C++ does not have compound literals like C99... // Standard C++ does not have compound literals like C99...
#define GUF_CSTR_TO_VIEW_CPP(CSTR) guf_str_view {.str = (CSTR), .len = (ptrdiff_t)strlen(CSTR)} #define GUF_CSTR_TO_VIEW_CPP(CSTR) guf_str_view {.str = (CSTR), .len = (ptrdiff_t)strlen(CSTR)}
#define GUF_CSTR_LIT_TO_VIEW_CPP(CSTR) guf_str_view {.str = (CSTR), .len = (ptrdiff_t)sizeof(CSTR) - 1} #define GUF_CSTR_LIT_TO_VIEW_CPP(CSTR) guf_str_view {.str = (CSTR), .len = (ptrdiff_t)sizeof(CSTR) - 1}
#define GUF_STR_UNINITIALISED_CPP guf_str{.allocator = NULL, .data.shrt.size = 0, .data.shrt.c_str[0] = '\0'} #define GUF_STR_UNINITIALISED_CPP guf_str{{0, 0, NULL}, 0}
#endif #endif
// 1.) guf_str_view: // 1.) guf_str_view:
@ -94,8 +94,8 @@ GUF_STR_KWRDS int guf_str_view_cmp(const void *str_view_a, const void *str_view_
// Hash functions. // Hash functions.
GUF_STR_KWRDS guf_hash_size_t guf_str_view_hash(const guf_str_view *sv); GUF_STR_KWRDS guf_hash_size_t guf_str_view_hash(const guf_str_view *sv);
GUF_STR_KWRDS uint64_t guf_str_view_hash64(const guf_str_view *sv); GUF_STR_KWRDS uint_fast64_t guf_str_view_hash64(const guf_str_view *sv);
GUF_STR_KWRDS uint32_t guf_str_view_hash32(const guf_str_view *sv); GUF_STR_KWRDS uint_fast32_t guf_str_view_hash32(const guf_str_view *sv);
// Return a new guf_str_view corresponding to the substring with leading/trailing ascii-whitespace chars removed from the left/right // Return a new guf_str_view corresponding to the substring with leading/trailing ascii-whitespace chars removed from the left/right
GUF_STR_KWRDS guf_str_view guf_str_view_trim_left_ascii(guf_str_view sv); GUF_STR_KWRDS guf_str_view guf_str_view_trim_left_ascii(guf_str_view sv);
@ -128,6 +128,10 @@ GUF_STR_KWRDS guf_str_tok_state guf_str_tok_state_new(guf_str_view str, guf_str_
*/ */
GUF_STR_KWRDS bool guf_str_tok_next(guf_str_tok_state *state, bool preserve_delims); GUF_STR_KWRDS bool guf_str_tok_next(guf_str_tok_state *state, bool preserve_delims);
GUF_STR_KWRDS unsigned guf_str_view_read_uint(guf_str_view *sv); // TODO: Handle overflow, signs etc.
GUF_STR_KWRDS uint_least64_t guf_str_view_read_u64(guf_str_view *sv);
// 2.) guf_str: // 2.) guf_str:
@ -137,6 +141,8 @@ GUF_STR_KWRDS guf_str *guf_str_init(guf_str *str, guf_str_view str_view, guf_all
GUF_STR_KWRDS guf_str *guf_str_init_empty(guf_str *str, guf_allocator *alloc); GUF_STR_KWRDS guf_str *guf_str_init_empty(guf_str *str, guf_allocator *alloc);
GUF_STR_KWRDS guf_str *guf_str_try_init_from_cstr(guf_str *str, const char* c_str, guf_allocator *alloc, guf_err *err); GUF_STR_KWRDS guf_str *guf_str_try_init_from_cstr(guf_str *str, const char* c_str, guf_allocator *alloc, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_init_from_cstr(guf_str *str, const char* c_str, guf_allocator *alloc); GUF_STR_KWRDS guf_str *guf_str_init_from_cstr(guf_str *str, const char* c_str, guf_allocator *alloc);
GUF_STR_KWRDS guf_str *guf_str_init_readonly(guf_str *str, guf_str_view sv);
GUF_STR_KWRDS guf_str guf_str_new_readonly(guf_str_view sv);
// Return an initialised guf_str (or GUF_STR_UNINITIALISED on error) // Return an initialised guf_str (or GUF_STR_UNINITIALISED on error)
GUF_STR_KWRDS guf_str guf_str_try_new(guf_str_view str_view, guf_allocator *alloc, guf_err *err); GUF_STR_KWRDS guf_str guf_str_try_new(guf_str_view str_view, guf_allocator *alloc, guf_err *err);
@ -151,8 +157,8 @@ GUF_STR_KWRDS int guf_str_cmp(const guf_str *a, const guf_str *b);
// Hash-functions. // Hash-functions.
GUF_STR_KWRDS guf_hash_size_t guf_str_hash(const guf_str *str); GUF_STR_KWRDS guf_hash_size_t guf_str_hash(const guf_str *str);
GUF_STR_KWRDS uint64_t guf_str_hash64(const guf_str *str); GUF_STR_KWRDS uint_fast64_t guf_str_hash64(const guf_str *str);
GUF_STR_KWRDS uint32_t guf_str_hash32(const guf_str *str); GUF_STR_KWRDS uint_fast32_t guf_str_hash32(const guf_str *str);
// Reserve at least min_capacity characters (excluding the null-terminator) (try to double the current capacity first; if that's not at least min_capacity, set the new capacity to min_capacity instead). // Reserve at least min_capacity characters (excluding the null-terminator) (try to double the current capacity first; if that's not at least min_capacity, set the new capacity to min_capacity instead).
GUF_STR_KWRDS guf_str *guf_str_try_reserve(guf_str *str, ptrdiff_t min_capacity, guf_err *err); GUF_STR_KWRDS guf_str *guf_str_try_reserve(guf_str *str, ptrdiff_t min_capacity, guf_err *err);
@ -206,6 +212,14 @@ GUF_STR_KWRDS guf_str *guf_str_append_one_char(guf_str *str, char c);
GUF_STR_KWRDS guf_str *guf_str_try_append(guf_str *str, guf_str_view sv, guf_err *err); GUF_STR_KWRDS guf_str *guf_str_try_append(guf_str *str, guf_str_view sv, guf_err *err);
GUF_STR_KWRDS guf_str *guf_str_append(guf_str *str, guf_str_view sv); GUF_STR_KWRDS guf_str *guf_str_append(guf_str *str, guf_str_view sv);
// Append from file:
#define GUF_READ_FILE_BUFSIZE 128
GUF_STR_KWRDS ptrdiff_t guf_str_try_append_file(guf_str *to_append, const char *fname, guf_err *err);
GUF_STR_KWRDS ptrdiff_t guf_str_append_file(guf_str *to_append, const char *fname);
// Append ints:
GUF_STR_KWRDS guf_str *guf_str_append_u64(guf_str *str, uint_least64_t n);
// Return a pointer to the null-terminated char array representing the string (works like std::string::c_str in C++) // Return a pointer to the null-terminated char array representing the string (works like std::string::c_str in C++)
GUF_STR_KWRDS const char *guf_str_const_cstr(const guf_str *str); GUF_STR_KWRDS const char *guf_str_const_cstr(const guf_str *str);
GUF_STR_KWRDS char *guf_str_try_get_cstr(guf_str *str, guf_err *err); // Error if str is readonly. GUF_STR_KWRDS char *guf_str_try_get_cstr(guf_str *str, guf_err *err); // Error if str is readonly.
@ -597,7 +611,7 @@ GUF_STR_KWRDS guf_str guf_str_new_uninitialised(void)
GUF_STR_KWRDS bool guf_str_is_uninit(const guf_str *str) GUF_STR_KWRDS bool guf_str_is_uninit(const guf_str *str)
{ {
GUF_ASSERT(str); GUF_ASSERT(str);
return !str->allocator && !str->data.shrt.size && str->data.shrt.c_str[0] == '\0'; return !str->allocator && !str->data.lng.size && !str->data.lng.capacity && str->data.lng.c_str == NULL;
} }
@ -719,6 +733,29 @@ GUF_STR_KWRDS guf_str *guf_str_init_from_cstr(guf_str *str, const char* c_str, g
return guf_str_try_init_from_cstr(str, c_str, alloc, NULL); return guf_str_try_init_from_cstr(str, c_str, alloc, NULL);
} }
GUF_STR_KWRDS guf_str *guf_str_init_readonly(guf_str *str, guf_str_view sv)
{
// #define GUF_STR_UNINITIALISED (guf_str){.allocator = NULL, .data.lng.size = 0, .data.lng.capacity = 0, .data.lng.c_str = NULL}
// #define GUF_CSTR_TO_READONLY_STR(CSTR) ((guf_str){.allocator = NULL, .data.lng.c_str = (CSTR), .data.lng.size = strlen(CSTR) + 1, .data.lng.capacity = 0})
GUF_ASSERT(str);
GUF_ASSERT(guf_str_view_is_valid(sv));
str->allocator = NULL;
str->data.lng.c_str = (char*)sv.str; // TODO: is this actually legal as long as we don't modify the pointer's contents?
str->data.lng.size = sv.len + 1;
str->data.lng.capacity = 0;
GUF_ASSERT(guf_str_is_readonly(str));
GUF_ASSERT(guf_str_is_valid(str));
return str;
}
GUF_STR_KWRDS guf_str guf_str_new_readonly(guf_str_view sv)
{
guf_str s;
guf_str_init_readonly(&s, sv);
return s;
}
GUF_STR_KWRDS guf_str_view guf_str_to_view(const guf_str *str) GUF_STR_KWRDS guf_str_view guf_str_to_view(const guf_str *str)
{ {
GUF_ASSERT(guf_str_is_valid(str)); GUF_ASSERT(guf_str_is_valid(str));
@ -1117,6 +1154,66 @@ GUF_STR_KWRDS guf_str *guf_str_append(guf_str *str, guf_str_view sv)
return guf_str_try_append(str, sv, NULL); return guf_str_try_append(str, sv, NULL);
} }
GUF_STR_KWRDS ptrdiff_t guf_str_try_append_file(guf_str *to_append, const char *fname, guf_err *err)
{
if (!fname) {
guf_err_set_or_panic(err, GUF_ERR_NULL_PTR, GUF_ERR_MSG("guf_str_read_file: fname is NULL"));
return 0;
}
FILE *f = fopen(fname, "r");
if (!f) {
guf_err_set_or_panic(err, GUF_ERR_IO, GUF_ERR_MSG("guf_str_read_file:: Failed to open file (fopen returned NULL)"));
return 0;
}
size_t chars_read = 0, total_chars_read = 0;
char buf[GUF_READ_FILE_BUFSIZE] = {'\0'};
while ((chars_read = fread(buf, sizeof buf[0], GUF_ARR_SIZE(buf), f)) && !ferror(f)) {
guf_str_append(to_append, (guf_str_view){.len = chars_read, .str = buf});
total_chars_read += chars_read;
if (feof(f)) {
break;
}
}
if (ferror(f)) {
fclose(f);
guf_err_set_or_panic(err, GUF_ERR_IO, GUF_ERR_MSG("read_file: Unexpected error while reading file"));
} else {
GUF_ASSERT(feof(f));
fclose(f);
}
return total_chars_read;
}
GUF_STR_KWRDS ptrdiff_t guf_str_append_file(guf_str *to_append, const char *fname) {
return guf_str_try_append_file(to_append, fname, NULL);
}
GUF_STR_KWRDS guf_str *guf_str_append_u64(guf_str *str, uint_least64_t n) {
char buf[20] = {'\0'};
int start_idx = GUF_ARR_SIZE(buf);
int num_digits = 0;
do {
GUF_ASSERT(start_idx > 0);
char c = (char)( (n % 10) + '0' );
buf[--start_idx] = c;
++num_digits;
} while ((n = n / 10));
GUF_ASSERT(num_digits == (int)GUF_ARR_SIZE(buf) - start_idx);
const guf_str_view num_sv = (guf_str_view) {.str = buf + start_idx, .len = num_digits};
return guf_str_append(str, num_sv);
}
GUF_STR_KWRDS guf_str *guf_str_try_substr(guf_str *str, ptrdiff_t pos, ptrdiff_t count, guf_err *err) GUF_STR_KWRDS guf_str *guf_str_try_substr(guf_str *str, ptrdiff_t pos, ptrdiff_t count, guf_err *err)
{ {
GUF_ASSERT(guf_str_is_valid(str)); GUF_ASSERT(guf_str_is_valid(str));
@ -1196,13 +1293,13 @@ GUF_STR_KWRDS guf_hash_size_t guf_str_hash(const guf_str *str)
return guf_str_view_hash(&(guf_str_view){.str = guf_str_const_cstr(str), .len = guf_str_len(str)}); return guf_str_view_hash(&(guf_str_view){.str = guf_str_const_cstr(str), .len = guf_str_len(str)});
} }
GUF_STR_KWRDS uint64_t guf_str_hash64(const guf_str *str) GUF_STR_KWRDS uint_fast64_t guf_str_hash64(const guf_str *str)
{ {
GUF_ASSERT(guf_str_is_valid(str)); GUF_ASSERT(guf_str_is_valid(str));
return guf_str_view_hash64(&(guf_str_view){.str = guf_str_const_cstr(str), .len = guf_str_len(str)}); return guf_str_view_hash64(&(guf_str_view){.str = guf_str_const_cstr(str), .len = guf_str_len(str)});
} }
GUF_STR_KWRDS uint32_t guf_str_hash32(const guf_str *str) GUF_STR_KWRDS uint_fast32_t guf_str_hash32(const guf_str *str)
{ {
GUF_ASSERT(guf_str_is_valid(str)); GUF_ASSERT(guf_str_is_valid(str));
return guf_str_view_hash32(&(guf_str_view){.str = guf_str_const_cstr(str), .len = guf_str_len(str)}); return guf_str_view_hash32(&(guf_str_view){.str = guf_str_const_cstr(str), .len = guf_str_len(str)});
@ -1457,7 +1554,7 @@ GUF_STR_KWRDS guf_hash_size_t guf_str_view_hash(const guf_str_view *sv)
return guf_hash(sv->str, sv->len, GUF_HASH_INIT); return guf_hash(sv->str, sv->len, GUF_HASH_INIT);
} }
GUF_STR_KWRDS uint64_t guf_str_view_hash64(const guf_str_view *sv) GUF_STR_KWRDS uint_fast64_t guf_str_view_hash64(const guf_str_view *sv)
{ {
GUF_ASSERT(sv); GUF_ASSERT(sv);
if (!sv->str || sv->len <= 0) { if (!sv->str || sv->len <= 0) {
@ -1466,7 +1563,7 @@ GUF_STR_KWRDS uint64_t guf_str_view_hash64(const guf_str_view *sv)
return guf_hash64(sv->str, sv->len, GUF_HASH64_INIT); return guf_hash64(sv->str, sv->len, GUF_HASH64_INIT);
} }
GUF_STR_KWRDS uint32_t guf_str_view_hash32(const guf_str_view *sv) GUF_STR_KWRDS uint_fast32_t guf_str_view_hash32(const guf_str_view *sv)
{ {
GUF_ASSERT(sv); GUF_ASSERT(sv);
if (!sv->str || sv->len <= 0) { if (!sv->str || sv->len <= 0) {
@ -1501,6 +1598,30 @@ GUF_STR_KWRDS bool guf_str_view_equal_val_arg(guf_str_view a_val, guf_str_view b
return guf_str_view_equal(&a_val, &b_val); return guf_str_view_equal(&a_val, &b_val);
} }
GUF_STR_KWRDS unsigned guf_str_view_read_uint(guf_str_view *sv)
{ // TODO: Handle overflow etc.
unsigned res = 0;
while (sv->len && sv->str[0] >= '0' && sv->str[0] <= '9') {
res *= 10;
res += (sv->str[0] - '0');
sv->len -= 1;
sv->str = sv->len > 0 ? sv->str + 1 : NULL;
}
return res;
}
GUF_STR_KWRDS uint_least64_t guf_str_view_read_u64(guf_str_view *sv)
{ // TODO: Handle overflow etc.
uint_least64_t res = 0;
while (sv->len && sv->str[0] >= '0' && sv->str[0] <= '9') {
res *= 10;
res += (sv->str[0] - '0');
sv->len -= 1;
sv->str = sv->len > 0 ? sv->str + 1 : NULL;
}
return res;
}
#undef GUF_STR_IMPL #undef GUF_STR_IMPL
#undef GUF_STR_IMPL_STATIC #undef GUF_STR_IMPL_STATIC
#endif /* end impl */ #endif /* end impl */

0
src/guf_str_view_type.h Normal file → Executable file
View File

41
src/guf_utf8.h Normal file → Executable file
View File

@ -16,7 +16,7 @@
// Corresponds to one unicode codepoint (NOTE: one guf_utf8_char does not necessarily correspond to one printable character, e.g. combining characters). // Corresponds to one unicode codepoint (NOTE: one guf_utf8_char does not necessarily correspond to one printable character, e.g. combining characters).
typedef struct guf_utf8_char { typedef struct guf_utf8_char {
char bytes[5]; char bytes[4];
} guf_utf8_char; } guf_utf8_char;
typedef enum guf_utf8_stat { typedef enum guf_utf8_stat {
@ -35,9 +35,9 @@ GUF_UTF8_KWRDS int guf_utf8_char_num_bytes(const guf_utf8_char *c);
GUF_UTF8_KWRDS bool guf_utf8_char_is_valid(const guf_utf8_char *c); GUF_UTF8_KWRDS bool guf_utf8_char_is_valid(const guf_utf8_char *c);
GUF_UTF8_KWRDS bool guf_utf8_char_is_whitespace(const guf_utf8_char *c); GUF_UTF8_KWRDS bool guf_utf8_char_is_whitespace(const guf_utf8_char *c);
GUF_UTF8_KWRDS guf_utf8_char guf_utf8_char_new(uint32_t codepoint); // Returns GUF_UTF8_REPLACEMENT_CHAR for invalid codepoints (and for GUF_UTF8_REPLACEMENT_CHAR_CODEPOINT). GUF_UTF8_KWRDS guf_utf8_char guf_utf8_char_new(uint_least32_t codepoint); // Returns GUF_UTF8_REPLACEMENT_CHAR for invalid codepoints (and for GUF_UTF8_REPLACEMENT_CHAR_CODEPOINT).
GUF_UTF8_KWRDS bool guf_utf8_encode(guf_utf8_char *result, uint32_t codepoint); // Returns false for invalid codepoints. GUF_UTF8_KWRDS bool guf_utf8_encode(guf_utf8_char *result, uint_least32_t codepoint); // Returns false for invalid codepoints.
GUF_UTF8_KWRDS int32_t guf_utf8_decode(const guf_utf8_char *utf8); // Returns -1 for invalid utf-8. GUF_UTF8_KWRDS int_least32_t guf_utf8_decode(const guf_utf8_char *utf8); // Returns -1 for invalid utf-8.
GUF_UTF8_KWRDS bool guf_utf8_equal(const guf_utf8_char *a, const guf_utf8_char *b); GUF_UTF8_KWRDS bool guf_utf8_equal(const guf_utf8_char *a, const guf_utf8_char *b);
@ -68,7 +68,7 @@ const char* const GUF_UTF8_COMMON_PUNCT[32] =
".", ",", ";", ":", "(", ")", "[", "]", "!", "?", "¿", "¡", "&", "+", "-", "/", "*", "\"", "'", "", "", "´", "»", "«", "`", "\\", "%", "", "", "", "", "_" ".", ",", ";", ":", "(", ")", "[", "]", "!", "?", "¿", "¡", "&", "+", "-", "/", "*", "\"", "'", "", "", "´", "»", "«", "`", "\\", "%", "", "", "", "", "_"
}; };
const guf_utf8_char GUF_UTF8_REPLACEMENT_CHAR = {.bytes = {'\xEF','\xBF','\xBD', '\0', '\0'}}; const guf_utf8_char GUF_UTF8_REPLACEMENT_CHAR = {.bytes = {'\xEF','\xBF','\xBD', '\0'}};
#ifndef GUF_FN_KEYWORDS #ifndef GUF_FN_KEYWORDS
#define GUF_FN_KEYWORDS #define GUF_FN_KEYWORDS
@ -93,7 +93,7 @@ GUF_UTF8_KWRDS bool guf_utf8_equal(const guf_utf8_char *a, const guf_utf8_char *
} }
// cf. https://datatracker.ietf.org/doc/html/rfc3629#section-3 (last-retrieved 2025-03-02) // cf. https://datatracker.ietf.org/doc/html/rfc3629#section-3 (last-retrieved 2025-03-02)
GUF_UTF8_KWRDS bool guf_utf8_encode(guf_utf8_char *result, uint32_t cp) GUF_UTF8_KWRDS bool guf_utf8_encode(guf_utf8_char *result, uint_least32_t cp)
{ {
GUF_ASSERT(result); GUF_ASSERT(result);
@ -113,15 +113,15 @@ GUF_UTF8_KWRDS bool guf_utf8_encode(guf_utf8_char *result, uint32_t cp)
first_byte_bits = 7; first_byte_bits = 7;
} else if (cp >= 0x80 && cp <= 0x7FF) { // binary: 110x.xxxx 10xx.xxxx } else if (cp >= 0x80 && cp <= 0x7FF) { // binary: 110x.xxxx 10xx.xxxx
num_bytes = 2; num_bytes = 2;
result->bytes[0] = 0xC0; result->bytes[0] = (char)0xC0;
first_byte_bits = 5; first_byte_bits = 5;
} else if (cp >= 0x800 && cp <= 0xFFFF) { // binary: 1110.xxxx 10xx.xxxx 10xx.xxxx } else if (cp >= 0x800 && cp <= 0xFFFF) { // binary: 1110.xxxx 10xx.xxxx 10xx.xxxx
num_bytes = 3; num_bytes = 3;
result->bytes[0] = 0xE0; result->bytes[0] = (char)0xE0;
first_byte_bits = 4; first_byte_bits = 4;
} else if (cp >= 0x10000 && cp <= 0x10FFFF) { // binary: 1111.0xxx 10xx.xxxx 10xx.xxxx 10xx.xxxx } else if (cp >= 0x10000 && cp <= 0x10FFFF) { // binary: 1111.0xxx 10xx.xxxx 10xx.xxxx 10xx.xxxx
num_bytes = 4; num_bytes = 4;
result->bytes[0] = 0xF0; result->bytes[0] = (char)0xF0;
first_byte_bits = 3; first_byte_bits = 3;
} }
@ -131,15 +131,15 @@ GUF_UTF8_KWRDS bool guf_utf8_encode(guf_utf8_char *result, uint32_t cp)
} }
for (int i = 1; i < num_bytes; ++i) { for (int i = 1; i < num_bytes; ++i) {
result->bytes[i] = 0x80; // binary: 10xx.xxxx result->bytes[i] = (char)0x80; // binary: 10xx.xxxx
} }
const int tail_byte_bits = 6; const int tail_byte_bits = 6;
int cp_bits = 0; int cp_bits = 0;
for (int byte_n = num_bytes - 1; byte_n >= 0 && cp > 0; --byte_n) { for (int byte_n = num_bytes - 1; byte_n >= 0 && cp > 0; --byte_n) {
const int bits = (byte_n == 0) ? first_byte_bits : tail_byte_bits; const int bits = (byte_n == 0) ? first_byte_bits : tail_byte_bits;
const uint32_t cp_mask = (UINT32_C(1) << bits) - 1; const uint_least32_t cp_mask = GUF_UWRAP_32( (UINT32_C(1) << bits) - 1 );
result->bytes[byte_n] = (char)((unsigned char)result->bytes[byte_n] | (cp & cp_mask)); result->bytes[byte_n] = (char)(1u * (unsigned char)result->bytes[byte_n] | (cp & cp_mask));
cp = cp >> bits; cp = cp >> bits;
cp_bits += bits; cp_bits += bits;
} }
@ -155,7 +155,7 @@ GUF_UTF8_KWRDS bool guf_utf8_encode(guf_utf8_char *result, uint32_t cp)
} }
} }
GUF_UTF8_KWRDS guf_utf8_char guf_utf8_char_new(uint32_t codepoint) GUF_UTF8_KWRDS guf_utf8_char guf_utf8_char_new(uint_least32_t codepoint)
{ {
guf_utf8_char result = GUF_UTF8_REPLACEMENT_CHAR; guf_utf8_char result = GUF_UTF8_REPLACEMENT_CHAR;
guf_utf8_encode(&result, codepoint); guf_utf8_encode(&result, codepoint);
@ -163,7 +163,7 @@ GUF_UTF8_KWRDS guf_utf8_char guf_utf8_char_new(uint32_t codepoint)
} }
// cf. https://datatracker.ietf.org/doc/html/rfc3629#section-3 (last-retrieved 2025-03-02) // cf. https://datatracker.ietf.org/doc/html/rfc3629#section-3 (last-retrieved 2025-03-02)
GUF_UTF8_KWRDS int32_t guf_utf8_decode(const guf_utf8_char *c) GUF_UTF8_KWRDS int_least32_t guf_utf8_decode(const guf_utf8_char *c)
{ {
if (!guf_utf8_char_is_valid(c)) { if (!guf_utf8_char_is_valid(c)) {
return -1; return -1;
@ -189,12 +189,12 @@ GUF_UTF8_KWRDS int32_t guf_utf8_decode(const guf_utf8_char *c)
return -1; return -1;
} }
uint32_t cp = 0; uint_least32_t cp = 0;
int cp_bits = 0; int cp_bits = 0;
for (int byte_n = num_bytes - 1; byte_n >= 0; --byte_n) { for (int byte_n = num_bytes - 1; byte_n >= 0; --byte_n) {
const int bits = (byte_n == 0) ? first_byte_bits : tail_byte_bits; const int bits = (byte_n == 0) ? first_byte_bits : tail_byte_bits;
const uint32_t byte_mask = (UINT32_C(1) << bits) - 1; const uint_least32_t byte_mask = GUF_UWRAP_32( (UINT32_C(1) << bits) - 1 );
cp |= ((uint32_t)c->bytes[byte_n] & byte_mask) << cp_bits; cp = GUF_UWRAP_32( cp | GUF_UWRAP_32( 1u * ((uint_least32_t)c->bytes[byte_n] & byte_mask) << cp_bits ) );
cp_bits += bits; cp_bits += bits;
} }
GUF_ASSERT(cp_bits == first_byte_bits + (num_bytes - 1) * tail_byte_bits); GUF_ASSERT(cp_bits == first_byte_bits + (num_bytes - 1) * tail_byte_bits);
@ -204,8 +204,11 @@ GUF_UTF8_KWRDS int32_t guf_utf8_decode(const guf_utf8_char *c)
if (!valid) { if (!valid) {
return -1; return -1;
} else { } else {
#ifdef INT32_MAX
GUF_ASSERT(cp <= INT32_MAX); GUF_ASSERT(cp <= INT32_MAX);
return (int32_t)cp; #endif
GUF_ASSERT(cp <= INT_LEAST32_MAX);
return (int_least32_t)cp;
} }
} }
@ -285,7 +288,7 @@ GUF_UTF8_KWRDS bool guf_utf8_char_is_valid(const guf_utf8_char *c)
for (int i = 0; i < num_bytes; ++i) { for (int i = 0; i < num_bytes; ++i) {
// "The octet values C0, C1, F5 to FF never appear.", cf. https://www.rfc-editor.org/rfc/rfc3629#page-5 // "The octet values C0, C1, F5 to FF never appear.", cf. https://www.rfc-editor.org/rfc/rfc3629#page-5
if (bytes[i] == 0xC0 || bytes[i] == 0xC1 || (bytes[i] >= 0xF5 && bytes[i] <= 0xFF)) { if (bytes[i] == 0xC0 || bytes[i] == 0xC1 || (bytes[i] >= 0xF5)) {
return false; return false;
} }
} }

0
src/guf_utils.h Normal file → Executable file
View File

0
src/test/data/bartleby.txt Normal file → Executable file
View File

0
src/test/data/utf8-test.txt Normal file → Executable file
View File

4
src/test/example.c Normal file → Executable file
View File

@ -113,7 +113,7 @@ int main(void)
floats = dbuf_float_new(&allocator); floats = dbuf_float_new(&allocator);
for (int i = 0; i <= 16; ++i) { for (int i = 0; i <= 16; ++i) {
dbuf_float_push_val(&floats, i % 2 ? i * -2.f : i * 2.f); dbuf_float_push_val(&floats, i % 2 ? (float)i * -2.f : (float)i * 2.f);
} }
// float *tmp = test_allocator.alloc(floats.size * sizeof(float), &test_allocator_ctx); // float *tmp = test_allocator.alloc(floats.size * sizeof(float), &test_allocator_ctx);
@ -132,7 +132,7 @@ int main(void)
dbuf_heap_cstr strings = dbuf_heap_cstr_new(&allocator); dbuf_heap_cstr strings = dbuf_heap_cstr_new(&allocator);
dbuf_heap_cstr_push_val_cpy(&strings, "Foo 1"); dbuf_heap_cstr_push_val_cpy(&strings, "Foo 1");
dbuf_heap_cstr_push_val_cpy(&strings, "Bar 2"); dbuf_heap_cstr_push_val_cpy(&strings, "Bar 2");
char *move_me = strdup("Baz 3"); char *move_me = guf_cstr_dup("Baz 3");
dbuf_heap_cstr_push(&strings, &move_me, GUF_CPY_MOVE); dbuf_heap_cstr_push(&strings, &move_me, GUF_CPY_MOVE);
GUF_ASSERT_RELEASE(move_me == NULL); GUF_ASSERT_RELEASE(move_me == NULL);

0
src/test/impls/alloc_libc_impl.c Normal file → Executable file
View File

0
src/test/impls/alloc_tracker_impl.c Normal file → Executable file
View File

0
src/test/impls/ckdint_impl.c Normal file → Executable file
View File

0
src/test/impls/dbuf_impl.c Normal file → Executable file
View File

0
src/test/impls/dbuf_impl.h Normal file → Executable file
View File

16
src/test/impls/dict_impl.c Normal file → Executable file
View File

@ -21,6 +21,22 @@
// #define GUF_DICT_32_BIT_HASH // #define GUF_DICT_32_BIT_HASH
#include "guf_dict.h" #include "guf_dict.h"
#define GUF_DICT_KEY_T guf_str
#define GUF_DICT_KEY_HASH guf_str_hash
#define GUF_DICT_KEY_T_EQ guf_str_equal
#define GUF_DICT_KEY_T_CMP guf_str_cmp
#define GUF_DICT_KEY_T_COPY guf_str_copy
#define GUF_DICT_KEY_T_MOVE guf_str_move
#define GUF_DICT_KEY_T_FREE guf_str_free
#define GUF_DICT_VAL_T int32_t
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_str_i32
#define GUF_DICT_IMPL
// #define GUF_DICT_64_BIT_IDX
// #define GUF_DICT_PROBE_LINEAR
// #define GUF_DICT_32_BIT_HASH
#include "guf_dict.h"
#define GUF_DICT_KEY_T int32_t #define GUF_DICT_KEY_T int32_t
#define GUF_DICT_KEY_HASH int32_hash #define GUF_DICT_KEY_HASH int32_hash
#define GUF_DICT_KEY_T_EQ int32_eq #define GUF_DICT_KEY_T_EQ int32_eq

15
src/test/impls/dict_impl.h Normal file → Executable file
View File

@ -26,6 +26,21 @@
// #define GUF_DICT_32_BIT_HASH // #define GUF_DICT_32_BIT_HASH
#include "guf_dict.h" #include "guf_dict.h"
#define GUF_DICT_KEY_T guf_str
#define GUF_DICT_KEY_HASH guf_str_hash
#define GUF_DICT_KEY_T_EQ guf_str_equal
#define GUF_DICT_KEY_T_CMP guf_str_cmp
#define GUF_DICT_KEY_T_COPY guf_str_copy
#define GUF_DICT_KEY_T_MOVE guf_str_move
#define GUF_DICT_KEY_T_FREE guf_str_free
#define GUF_DICT_VAL_T int32_t
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_str_i32
// #define GUF_DICT_64_BIT_IDX
// #define GUF_DICT_PROBE_LINEAR
// #define GUF_DICT_32_BIT_HASH
#include "guf_dict.h"
static inline guf_hash_size_t int32_hash(const int32_t *a) static inline guf_hash_size_t int32_hash(const int32_t *a)
{ {
return guf_hash(a, sizeof(int32_t), GUF_HASH_INIT); // TODO: byte order... return guf_hash(a, sizeof(int32_t), GUF_HASH_INIT); // TODO: byte order...

0
src/test/impls/init_impl.c Normal file → Executable file
View File

0
src/test/impls/linalg_impl.c Normal file → Executable file
View File

0
src/test/impls/rand_impl.c Normal file → Executable file
View File

0
src/test/impls/sort_impl.c Normal file → Executable file
View File

0
src/test/impls/sort_impl.h Normal file → Executable file
View File

0
src/test/impls/str_impl.c Normal file → Executable file
View File

0
src/test/test.cpp Normal file → Executable file
View File

6
src/test/test.hpp Normal file → Executable file
View File

@ -55,7 +55,7 @@ public:
bool passed {false}, done {false}; bool passed {false}, done {false};
size_t num_failed_checks {0}, num_passed_checks {0}; size_t num_failed_checks {0}, num_passed_checks {0};
Test(const std::string& name) : name{name} {} Test(const std::string& nm) : name{nm} {}
virtual ~Test() = default; virtual ~Test() = default;
size_t total_checks() const size_t total_checks() const
@ -67,7 +67,7 @@ public:
void before_run() void before_run()
{ {
time_start = std::chrono::high_resolution_clock::now(); time_start = std::chrono::steady_clock::now();
} }
void after_run() void after_run()
@ -75,7 +75,7 @@ public:
done = true; done = true;
passed = (num_failed_checks == 0); passed = (num_failed_checks == 0);
time_end = std::chrono::high_resolution_clock::now(); time_end = std::chrono::steady_clock::now();
runtime_ms = std::chrono::duration_cast<decltype(runtime_ms)>(time_end - time_start); runtime_ms = std::chrono::duration_cast<decltype(runtime_ms)>(time_end - time_start);
} }

57
src/test/test_ckdint.cpp Normal file → Executable file
View File

@ -27,6 +27,7 @@ void CkdIntTest::test_ckd()
const int32_t add_res = a + b; const int32_t add_res = a + b;
const guf_math_ckd_result ckd_add = guf_ckd_add_i8((int8_t)a, (int8_t)b); const guf_math_ckd_result ckd_add = guf_ckd_add_i8((int8_t)a, (int8_t)b);
TEST_CHECK(ckd_add == guf_ckd_add_i8((int8_t)b, (int8_t)a)); TEST_CHECK(ckd_add == guf_ckd_add_i8((int8_t)b, (int8_t)a));
TEST_CHECK(ckd_add == guf_ckd_add_least_i8((int_least8_t)a, (int_least8_t)b));
if (add_res > INT8_MAX) { if (add_res > INT8_MAX) {
TEST_CHECK(ckd_add == GUF_MATH_CKD_OVERFLOW_POS); TEST_CHECK(ckd_add == GUF_MATH_CKD_OVERFLOW_POS);
int8_t saturated, saturated2; int8_t saturated, saturated2;
@ -72,6 +73,7 @@ void CkdIntTest::test_ckd()
const int32_t sub_res = a - b; const int32_t sub_res = a - b;
const guf_math_ckd_result ckd_sub = guf_ckd_sub_i8((int8_t)a, (int8_t)b); const guf_math_ckd_result ckd_sub = guf_ckd_sub_i8((int8_t)a, (int8_t)b);
TEST_CHECK(ckd_sub == guf_ckd_sub_least_i8((int_least8_t)a, (int_least8_t)b));
if (sub_res > INT8_MAX) { if (sub_res > INT8_MAX) {
TEST_CHECK(ckd_sub == GUF_MATH_CKD_OVERFLOW_POS); TEST_CHECK(ckd_sub == GUF_MATH_CKD_OVERFLOW_POS);
int8_t saturated; int8_t saturated;
@ -100,6 +102,7 @@ void CkdIntTest::test_ckd()
const int32_t mul_res = a * b; const int32_t mul_res = a * b;
const guf_math_ckd_result ckd_mul = guf_ckd_mul_i8((int8_t)a, (int8_t)b); const guf_math_ckd_result ckd_mul = guf_ckd_mul_i8((int8_t)a, (int8_t)b);
TEST_CHECK(ckd_mul == guf_ckd_mul_least_i8((int_least8_t)a, (int_least8_t)b));
TEST_CHECK(ckd_mul == guf_ckd_mul_i8((int8_t)b, (int8_t)a)); TEST_CHECK(ckd_mul == guf_ckd_mul_i8((int8_t)b, (int8_t)a));
if (mul_res > INT8_MAX) { if (mul_res > INT8_MAX) {
TEST_CHECK(ckd_mul == GUF_MATH_CKD_OVERFLOW_POS); TEST_CHECK(ckd_mul == GUF_MATH_CKD_OVERFLOW_POS);
@ -220,6 +223,57 @@ void CkdIntTest::test_ckd()
guf_wrapping_mul_i32(4096, -314159265, &mul_i32_res); guf_wrapping_mul_i32(4096, -314159265, &mul_i32_res);
TEST_CHECK(mul_i32_res == 1693839360); TEST_CHECK(mul_i32_res == 1693839360);
int_least32_t mul_i32least_res = -12345;
TEST_CHECK(guf_wrapping_mul_least_i32(INT32_MAX, 2, &mul_i32least_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32least_res == -2);
mul_i32least_res = -12345;
TEST_CHECK(guf_wrapping_mul_least_i32(2, INT32_MAX, &mul_i32least_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32least_res == -2);
mul_i32least_res = -12345;
TEST_CHECK(guf_wrapping_mul_least_i32(INT32_MAX, -2, &mul_i32least_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(mul_i32least_res == 2);
mul_i32least_res = -12345;
TEST_CHECK(guf_wrapping_mul_least_i32(-2, INT32_MAX, &mul_i32least_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(mul_i32least_res == 2);
TEST_CHECK(guf_wrapping_mul_least_i32(42002718, 314159265, &mul_i32least_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32least_res == -972735522);
mul_i32least_res = -12345;
TEST_CHECK(guf_wrapping_mul_least_i32(314159265, 42002718, &mul_i32least_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32least_res == -972735522);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(42002718, 314159265, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == -972735522);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(-42002718, 314159265, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == 972735522);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(-88888888, 99999999, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == 1374494264);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(INT32_MIN, -1, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == INT32_MIN);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(-2147483648, 2147483640, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == 0);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(-2048, -314159265, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == -846919680);
mul_i32least_res = 12345;
guf_wrapping_mul_least_i32(4096, -314159265, &mul_i32least_res);
TEST_CHECK(mul_i32least_res == 1693839360);
ptrdiff_t ptrdiff_res = -1234; ptrdiff_t ptrdiff_res = -1234;
TEST_CHECK(guf_saturating_add_ptrdiff_t(PTRDIFF_MAX, 1, &ptrdiff_res) == GUF_MATH_CKD_OVERFLOW_POS); TEST_CHECK(guf_saturating_add_ptrdiff_t(PTRDIFF_MAX, 1, &ptrdiff_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(ptrdiff_res == PTRDIFF_MAX); TEST_CHECK(ptrdiff_res == PTRDIFF_MAX);
@ -241,6 +295,7 @@ void CkdIntTest::test_ckd_uint()
for (int32_t b = 0; b <= UINT8_MAX; ++b) { for (int32_t b = 0; b <= UINT8_MAX; ++b) {
const int32_t add_res = a + b; const int32_t add_res = a + b;
const guf_math_ckd_result ckd_add = guf_ckd_add_u8((uint8_t)a, (uint8_t)b); const guf_math_ckd_result ckd_add = guf_ckd_add_u8((uint8_t)a, (uint8_t)b);
GUF_ASSERT(ckd_add == guf_ckd_add_least_u8((uint_least8_t)a, (uint_least8_t)b));
if (add_res > UINT8_MAX) { if (add_res > UINT8_MAX) {
TEST_CHECK(ckd_add == GUF_MATH_CKD_OVERFLOW_POS); TEST_CHECK(ckd_add == GUF_MATH_CKD_OVERFLOW_POS);
uint8_t saturated; uint8_t saturated;
@ -262,6 +317,7 @@ void CkdIntTest::test_ckd_uint()
const int32_t sub_res = a - b; const int32_t sub_res = a - b;
const guf_math_ckd_result ckd_sub = guf_ckd_sub_u8((uint8_t)a, (uint8_t)b); const guf_math_ckd_result ckd_sub = guf_ckd_sub_u8((uint8_t)a, (uint8_t)b);
GUF_ASSERT(ckd_sub == guf_ckd_sub_least_u8((uint_least8_t)a, (uint_least8_t)b));
if (sub_res < 0) { if (sub_res < 0) {
TEST_CHECK(ckd_sub == GUF_MATH_CKD_OVERFLOW_NEG); TEST_CHECK(ckd_sub == GUF_MATH_CKD_OVERFLOW_NEG);
uint8_t saturated; uint8_t saturated;
@ -282,6 +338,7 @@ void CkdIntTest::test_ckd_uint()
const int32_t mul_res = a * b; const int32_t mul_res = a * b;
const guf_math_ckd_result ckd_mul = guf_ckd_mul_u8((uint8_t)a, (uint8_t)b); const guf_math_ckd_result ckd_mul = guf_ckd_mul_u8((uint8_t)a, (uint8_t)b);
GUF_ASSERT(ckd_mul == guf_ckd_mul_least_u8((uint_least8_t)a, (uint_least8_t)b));
if (mul_res > UINT8_MAX) { if (mul_res > UINT8_MAX) {
TEST_CHECK(ckd_mul == GUF_MATH_CKD_OVERFLOW_POS); TEST_CHECK(ckd_mul == GUF_MATH_CKD_OVERFLOW_POS);
uint8_t saturated; uint8_t saturated;

2
src/test/test_ckdint.hpp Normal file → Executable file
View File

@ -3,7 +3,7 @@
struct CkdIntTest : public Test struct CkdIntTest : public Test
{ {
CkdIntTest(const std::string& name) : Test(name) {}; CkdIntTest(const std::string& nm) : Test(nm) {};
void run() override; void run() override;
private: private:

17
src/test/test_dbuf.cpp Normal file → Executable file
View File

@ -551,6 +551,19 @@ void DbufCstringTest::test_find(int n)
void DbufStrTest::run() void DbufStrTest::run()
{ {
test_push_insert_erase(16); test_push_insert_erase(16);
test_push_insert_erase(16, 1);
test_push_insert_erase(16, 15);
test_push_insert_erase(16, 16);
test_push_insert_erase(16, 97);
test_push_insert_erase(16, 256);
test_push_insert_erase(500);
test_push_insert_erase(500, 1);
test_push_insert_erase(500, 499);
test_push_insert_erase(500, 500);
test_push_insert_erase(500, 97);
test_push_insert_erase(500, 256);
} }
@ -568,7 +581,7 @@ void DbufStrTest::test_push_insert_erase(size_t n, ptrdiff_t start_cap)
for (size_t i = 0; i < n; ++i) { for (size_t i = 0; i < n; ++i) {
std::string str; std::string str;
for (size_t c = 0; c < 512 + GUF_STR_SSO_BUF_CAP; ++c) { for (size_t c = 0; c < 512 + GUF_STR_SSO_BUF_CAP; ++c) {
str += c % 10 + '0'; str += (char)(c % 10 + '0');
} }
const guf_str_view sv_long = guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()}; const guf_str_view sv_long = guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()};
const guf_str_view sv_short = guf_str_view{.str = str.data(), .len = (ptrdiff_t)GUF_STR_SSO_BUF_CAP - 1}; const guf_str_view sv_short = guf_str_view{.str = str.data(), .len = (ptrdiff_t)GUF_STR_SSO_BUF_CAP - 1};
@ -629,7 +642,7 @@ void DbufStrTest::test_push_insert_erase(size_t n, ptrdiff_t start_cap)
std::string str; std::string str;
for (size_t c = 0; c < 512 + GUF_STR_SSO_BUF_CAP; ++c) { for (size_t c = 0; c < 512 + GUF_STR_SSO_BUF_CAP; ++c) {
str += c % 10 + '0'; str += (char)(c % 10 + '0');
} }
const guf_str_view sv_long = guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()}; const guf_str_view sv_long = guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()};
const guf_str_view sv_short = guf_str_view{.str = str.data(), .len = (ptrdiff_t)GUF_STR_SSO_BUF_CAP - 1}; const guf_str_view sv_short = guf_str_view{.str = str.data(), .len = (ptrdiff_t)GUF_STR_SSO_BUF_CAP - 1};

6
src/test/test_dbuf.hpp Normal file → Executable file
View File

@ -10,7 +10,7 @@ extern "C"
struct DbufIntTest : public Test struct DbufIntTest : public Test
{ {
DbufIntTest(const std::string& name) : Test(name) DbufIntTest(const std::string& nm) : Test(nm)
{ {
allocator_ctx.zero_init = false; allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 1, "DbufIntTest_allocator", NULL, NULL); guf_alloc_tracker_init(&allocator_ctx.tracker, 1, "DbufIntTest_allocator", NULL, NULL);
@ -31,7 +31,7 @@ private:
struct DbufCstringTest : public Test struct DbufCstringTest : public Test
{ {
DbufCstringTest(std::string name) : Test(name) DbufCstringTest(std::string nm) : Test(nm)
{ {
allocator_ctx.zero_init = false; allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 2, "DbufCstringTest_allocator", NULL, NULL); guf_alloc_tracker_init(&allocator_ctx.tracker, 2, "DbufCstringTest_allocator", NULL, NULL);
@ -52,7 +52,7 @@ private:
struct DbufStrTest : public Test struct DbufStrTest : public Test
{ {
DbufStrTest(std::string name) : Test(name) DbufStrTest(std::string nm) : Test(nm)
{ {
allocator_ctx.zero_init = false; allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 3, "DbufStrTest_allocator", NULL, NULL); guf_alloc_tracker_init(&allocator_ctx.tracker, 3, "DbufStrTest_allocator", NULL, NULL);

66
src/test/test_dict.cpp Normal file → Executable file
View File

@ -49,19 +49,23 @@ void DictSvToIntTest::insert_lookup(std::optional<ptrdiff_t> inital_dict_cap)
{ {
std::unordered_map<std::string_view, int32_t> word_cnt_map {}; std::unordered_map<std::string_view, int32_t> word_cnt_map {};
dict_sv_i32 word_cnt_dict {}; dict_sv_i32 word_cnt_dict {};
dict_str_i32 word_cnt_dict_str {};
if (inital_dict_cap) { if (inital_dict_cap) {
dict_sv_i32_init_with_capacity(&word_cnt_dict, &allocator, inital_dict_cap.value()); dict_sv_i32_init_with_capacity(&word_cnt_dict, &allocator, inital_dict_cap.value());
dict_str_i32_init_with_capacity(&word_cnt_dict_str, &allocator, inital_dict_cap.value());
} else { } else {
dict_sv_i32_init(&word_cnt_dict, &allocator); dict_sv_i32_init(&word_cnt_dict, &allocator);
dict_str_i32_init(&word_cnt_dict_str, &allocator);
} }
dbuf_str_view delims = dbuf_str_view_new(&allocator); dbuf_str_view delims = dbuf_str_view_new(&allocator);
for (size_t i = 0; i < GUF_ARR_SIZE(GUF_UTF8_WHITESPACE); ++i) { for (size_t i = 0; i < GUF_ARR_SIZE(GUF_UTF8_WHITESPACE); ++i) {
guf_str_view d = {.len = (ptrdiff_t)strlen(GUF_UTF8_WHITESPACE[i]), .str = GUF_UTF8_WHITESPACE[i]}; guf_str_view d = {.str = GUF_UTF8_WHITESPACE[i], .len = (ptrdiff_t)strlen(GUF_UTF8_WHITESPACE[i])};
dbuf_str_view_push_val(&delims, d); dbuf_str_view_push_val(&delims, d);
} }
for (size_t i = 0; i < GUF_ARR_SIZE(GUF_UTF8_COMMON_PUNCT); ++i) { for (size_t i = 0; i < GUF_ARR_SIZE(GUF_UTF8_COMMON_PUNCT); ++i) {
guf_str_view d = {.len = (ptrdiff_t)strlen(GUF_UTF8_COMMON_PUNCT[i]), .str = GUF_UTF8_COMMON_PUNCT[i]}; guf_str_view d = {.str = GUF_UTF8_COMMON_PUNCT[i], .len = (ptrdiff_t)strlen(GUF_UTF8_COMMON_PUNCT[i])};
dbuf_str_view_push_val(&delims, d); dbuf_str_view_push_val(&delims, d);
} }
@ -77,26 +81,43 @@ void DictSvToIntTest::insert_lookup(std::optional<ptrdiff_t> inital_dict_cap)
if (!dict_sv_i32_contains(&word_cnt_dict, &tok)) { if (!dict_sv_i32_contains(&word_cnt_dict, &tok)) {
dict_sv_i32_insert_val_arg(&word_cnt_dict, tok, 1, GUF_CPY_VALUE, GUF_CPY_VALUE); dict_sv_i32_insert_val_arg(&word_cnt_dict, tok, 1, GUF_CPY_VALUE, GUF_CPY_VALUE);
word_cnt_map.insert({sv, 1}); word_cnt_map.insert({sv, 1});
if (TEST_CHECK(!dict_str_i32_contains_val_arg(&word_cnt_dict_str, guf_str_new_readonly(tok)))) {
dict_str_i32_insert_val_arg(&word_cnt_dict_str, guf_str_new(tok, &allocator), 1, GUF_CPY_MOVE, GUF_CPY_VALUE);
}
} else { } else {
int32_t *cnt = dict_sv_i32_at_val_arg(&word_cnt_dict, tok); int32_t *cnt = dict_sv_i32_at_val_arg(&word_cnt_dict, tok);
if (TEST_CHECK(cnt)) { if (TEST_CHECK(cnt)) {
*cnt += 1; *cnt += 1;
} }
int32_t *cnt_2 = dict_str_i32_at_val_arg(&word_cnt_dict_str, guf_str_new_readonly(tok));
if (TEST_CHECK(cnt_2)) {
*cnt_2 += 1;
}
// else {
// std::cout << "tok: " << std::string_view{tok.str, (size_t)tok.len} << "\n";
// }
word_cnt_map.at(sv) += 1; word_cnt_map.at(sv) += 1;
} }
// printf("tok_len: %td ", tok.len); // printf("tok_len: %td ", tok.len);
// printf("'%.*s'\n", (int)tok.len, tok.str); // printf("'%.*s'\n", (int)tok.len, tok.str);
TEST_CHECK(dict_sv_i32_debug_valid_size(&word_cnt_dict)); TEST_CHECK(dict_sv_i32_debug_valid_size(&word_cnt_dict));
TEST_CHECK(dict_str_i32_debug_valid_size(&word_cnt_dict_str));
} }
dbuf_str_view_free(&delims, NULL); dbuf_str_view_free(&delims, NULL);
TEST_CHECK(dict_sv_i32_size(&word_cnt_dict) == std::ssize(word_cnt_map)); TEST_CHECK(dict_sv_i32_size(&word_cnt_dict) == std::ssize(word_cnt_map));
TEST_CHECK(dict_sv_i32_debug_valid_size(&word_cnt_dict)); TEST_CHECK(dict_sv_i32_debug_valid_size(&word_cnt_dict));
TEST_CHECK(dict_str_i32_size(&word_cnt_dict_str) == std::ssize(word_cnt_map));
TEST_CHECK(dict_str_i32_debug_valid_size(&word_cnt_dict_str));
for (const auto & [word, cnt] : word_cnt_map ) { for (const auto & [word, cnt] : word_cnt_map ) {
guf_str_view sv = {.str = word.data(), .len = (ptrdiff_t)word.size()}; guf_str_view sv = {.str = word.data(), .len = (ptrdiff_t)word.size()};
int32_t *res = dict_sv_i32_at(&word_cnt_dict, &sv); int32_t *res = dict_sv_i32_at(&word_cnt_dict, &sv);
int32_t *res2 = dict_str_i32_at_val_arg(&word_cnt_dict_str, guf_str_new_readonly(sv));
TEST_CHECK(res && *res == cnt); TEST_CHECK(res && *res == cnt);
TEST_CHECK(res2 && *res2 == cnt);
} }
ptrdiff_t i = 0; ptrdiff_t i = 0;
@ -104,7 +125,6 @@ void DictSvToIntTest::insert_lookup(std::optional<ptrdiff_t> inital_dict_cap)
const dict_sv_i32_kv *kv = kv_it.ptr; const dict_sv_i32_kv *kv = kv_it.ptr;
if (TEST_CHECK(kv)) { if (TEST_CHECK(kv)) {
const int32_t cnt = kv->val; const int32_t cnt = kv->val;
// printf("%.*s: %d\n", (int)kv->key.len, kv->key.str, cnt);
const std::string_view sv(kv->key.str, kv->key.len); const std::string_view sv(kv->key.str, kv->key.len);
if (TEST_CHECK(word_cnt_map.contains(sv))) { if (TEST_CHECK(word_cnt_map.contains(sv))) {
TEST_CHECK(word_cnt_map.at(sv) == cnt); TEST_CHECK(word_cnt_map.at(sv) == cnt);
@ -116,6 +136,24 @@ void DictSvToIntTest::insert_lookup(std::optional<ptrdiff_t> inital_dict_cap)
TEST_CHECK(i == std::ssize(word_cnt_map)); TEST_CHECK(i == std::ssize(word_cnt_map));
TEST_CHECK(dict_sv_i32_debug_valid_size(&word_cnt_dict)); TEST_CHECK(dict_sv_i32_debug_valid_size(&word_cnt_dict));
i = 0;
GUF_CNT_FOREACH(&word_cnt_dict_str, dict_str_i32, kv_it) {
const dict_str_i32_kv *kv = kv_it.ptr;
if (TEST_CHECK(kv)) {
const int32_t cnt = kv->val;
const std::string_view sv(guf_str_const_cstr(&kv->key), guf_str_len(&kv->key));
// std::cout << sv << "\n";
if (TEST_CHECK(word_cnt_map.contains(sv))) {
TEST_CHECK(word_cnt_map.at(sv) == cnt);
}
}
++i;
}
TEST_CHECK(i == dict_str_i32_size(&word_cnt_dict_str));
TEST_CHECK(i == std::ssize(word_cnt_map));
TEST_CHECK(dict_str_i32_debug_valid_size(&word_cnt_dict_str));
// std::cout << "load fac: " << dict_sv_i32_load_factor(&word_cnt_dict) << ", cap: " << word_cnt_dict.kv_indices_cap << " elem cap: " << word_cnt_dict.kv_elems.capacity << "\n"; // std::cout << "load fac: " << dict_sv_i32_load_factor(&word_cnt_dict) << ", cap: " << word_cnt_dict.kv_indices_cap << " elem cap: " << word_cnt_dict.kv_elems.capacity << "\n";
// std::cout << "size: " << dict_sv_i32_size(&word_cnt_dict) << ", max probelen: " << word_cnt_dict.max_probelen << "\n"; // std::cout << "size: " << dict_sv_i32_size(&word_cnt_dict) << ", max probelen: " << word_cnt_dict.max_probelen << "\n";
// std::cout << "mem usage: " << dict_sv_i32_memory_usage(&word_cnt_dict) << "\n"; // std::cout << "mem usage: " << dict_sv_i32_memory_usage(&word_cnt_dict) << "\n";
@ -334,7 +372,29 @@ void DictSvToIntTest::insert_lookup(std::optional<ptrdiff_t> inital_dict_cap)
TEST_CHECK(word_cnt_dict.kv_elems.size == 0); TEST_CHECK(word_cnt_dict.kv_elems.size == 0);
TEST_CHECK(dict_sv_i32_size(&word_cnt_dict) == 0); TEST_CHECK(dict_sv_i32_size(&word_cnt_dict) == 0);
std::string str;
for (size_t c = 0; c < GUF_STR_SSO_BUF_CAP * 4; ++c) {
str += c % 2 ? "AAA" : "aaa";
}
guf_str str_cpy = guf_str_new(guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()}, &allocator);
dict_str_i32_insert_val_arg(&word_cnt_dict_str, str_cpy, 42, GUF_CPY_DEEP, GUF_CPY_VALUE);
int32_t *foo = dict_str_i32_at_val_arg(&word_cnt_dict_str, guf_str_new_readonly(guf_str_view_from_str(&str_cpy)));
if (TEST_CHECK(foo)) {
TEST_CHECK(*foo == 42);
}
guf_str_append(&str_cpy, GUF_CSTR_LIT_TO_VIEW_CPP("Foobar"));
int32_t *foo2 = dict_str_i32_at_val_arg(&word_cnt_dict_str, guf_str_new_readonly(guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()}));
if (TEST_CHECK(foo2)) {
TEST_CHECK(*foo2 == 42);
}
guf_str_free(&str_cpy, NULL);
dict_sv_i32_free(&word_cnt_dict, NULL); dict_sv_i32_free(&word_cnt_dict, NULL);
dict_str_i32_free(&word_cnt_dict_str, NULL);
bool dbuf_null = !word_cnt_dict.kv_elems.data && !word_cnt_dict.kv_elems.allocator && !word_cnt_dict.kv_elems.capacity && !word_cnt_dict.kv_elems.size; bool dbuf_null = !word_cnt_dict.kv_elems.data && !word_cnt_dict.kv_elems.allocator && !word_cnt_dict.kv_elems.capacity && !word_cnt_dict.kv_elems.size;
TEST_CHECK(dbuf_null && !word_cnt_dict.kv_indices && !word_cnt_dict.kv_indices_cap && !word_cnt_dict.max_probelen && !word_cnt_dict.num_tombstones); TEST_CHECK(dbuf_null && !word_cnt_dict.kv_indices && !word_cnt_dict.kv_indices_cap && !word_cnt_dict.max_probelen && !word_cnt_dict.num_tombstones);
} }

2
src/test/test_dict.hpp Normal file → Executable file
View File

@ -12,7 +12,7 @@ extern "C"
struct DictSvToIntTest : public Test struct DictSvToIntTest : public Test
{ {
DictSvToIntTest(const std::string& name) : Test(name) DictSvToIntTest(const std::string& nm) : Test(nm)
{ {
allocator_ctx.zero_init = false; allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 3, "DictSvToIntTest_allocator", NULL, NULL); guf_alloc_tracker_init(&allocator_ctx.tracker, 3, "DictSvToIntTest_allocator", NULL, NULL);

8
src/test/test_str.cpp Normal file → Executable file
View File

@ -335,8 +335,8 @@ std::vector<std::string_view> StrTest::test_popsplit(std::string_view str, std::
} }
const guf_str_view delim_sv = guf_str_view{.len = (ptrdiff_t)delim.size(), .str = delim.data()}; const guf_str_view delim_sv = guf_str_view{.str = delim.data(), .len = (ptrdiff_t)delim.size()};
guf_str_view src = guf_str_view{.len = (ptrdiff_t)str.size(), .str = str.data()}; guf_str_view src = guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()};
size_t n = 0; size_t n = 0;
do { do {
const guf_str_view popped = guf_str_view_pop_split(&src, delim_sv); const guf_str_view popped = guf_str_view_pop_split(&src, delim_sv);
@ -356,10 +356,10 @@ std::vector<std::string_view> StrTest::test_popsplit(std::string_view str, std::
std::vector<std::string_view> StrTest::get_toks(std::string_view sv_in, const std::vector<std::string_view>& delims_in, bool preserve_delims, guf_str_tok_delim_opt opt) std::vector<std::string_view> StrTest::get_toks(std::string_view sv_in, const std::vector<std::string_view>& delims_in, bool preserve_delims, guf_str_tok_delim_opt opt)
{ {
const guf_str_view sv = guf_str_view{.len = (ptrdiff_t)sv_in.size(), .str = sv_in.data()}; const guf_str_view sv = guf_str_view{.str = sv_in.data(), .len = (ptrdiff_t)sv_in.size()};
std::vector<guf_str_view> delims; std::vector<guf_str_view> delims;
for (const auto delim : delims_in) { for (const auto delim : delims_in) {
delims.push_back(guf_str_view{.len = (ptrdiff_t)delim.size(), .str = delim.data()}); delims.push_back(guf_str_view{.str = delim.data(), .len = (ptrdiff_t)delim.size()});
} }
guf_str_tok_state tok_state = guf_str_tok_state_new(sv, delims.data(), std::ssize(delims), opt); guf_str_tok_state tok_state = guf_str_tok_state_new(sv, delims.data(), std::ssize(delims), opt);

2
src/test/test_str.hpp Normal file → Executable file
View File

@ -11,7 +11,7 @@ extern "C"
struct StrTest : public Test struct StrTest : public Test
{ {
StrTest(const std::string& name) : Test(name) StrTest(const std::string& nm) : Test(nm)
{ {
allocator_ctx.zero_init = false; allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 4, "StrTest_allocator", NULL, NULL); guf_alloc_tracker_init(&allocator_ctx.tracker, 4, "StrTest_allocator", NULL, NULL);

14
src/test/test_utf8.cpp Normal file → Executable file
View File

@ -27,11 +27,11 @@ void UTF8Test::run()
push_check_name("count_words"); push_check_name("count_words");
dbuf_str_view delims = dbuf_str_view_new(&allocator); dbuf_str_view delims = dbuf_str_view_new(&allocator);
for (size_t i = 0; i < GUF_ARR_SIZE(GUF_UTF8_WHITESPACE); ++i) { for (size_t i = 0; i < GUF_ARR_SIZE(GUF_UTF8_WHITESPACE); ++i) {
guf_str_view d = {.len = (ptrdiff_t)strlen(GUF_UTF8_WHITESPACE[i]), .str = GUF_UTF8_WHITESPACE[i]}; guf_str_view d = {.str = GUF_UTF8_WHITESPACE[i], .len = (ptrdiff_t)strlen(GUF_UTF8_WHITESPACE[i])};
dbuf_str_view_push_val(&delims, d); dbuf_str_view_push_val(&delims, d);
} }
for (size_t i = 0; i < GUF_ARR_SIZE(GUF_UTF8_COMMON_PUNCT); ++i) { for (size_t i = 0; i < GUF_ARR_SIZE(GUF_UTF8_COMMON_PUNCT); ++i) {
guf_str_view d = {.len = (ptrdiff_t)strlen(GUF_UTF8_COMMON_PUNCT[i]), .str = GUF_UTF8_COMMON_PUNCT[i]}; guf_str_view d = {.str = GUF_UTF8_COMMON_PUNCT[i], .len = (ptrdiff_t)strlen(GUF_UTF8_COMMON_PUNCT[i])};
dbuf_str_view_push_val(&delims, d); dbuf_str_view_push_val(&delims, d);
} }
int words = count_words(TEST_DATA_DIR "/" "utf8-test.txt", &delims); int words = count_words(TEST_DATA_DIR "/" "utf8-test.txt", &delims);
@ -317,35 +317,31 @@ void UTF8Test::encode_decode()
TEST_CHECK(guf_utf8_encode(&utf8, 0x1F308)); // "🌈" (Rainbow) TEST_CHECK(guf_utf8_encode(&utf8, 0x1F308)); // "🌈" (Rainbow)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 4); TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 4);
TEST_CHECK(utf8.bytes[0] == '\xF0' && utf8.bytes[1] == '\x9F' && utf8.bytes[2] == '\x8C' && utf8.bytes[3] == '\x88'); TEST_CHECK(utf8.bytes[0] == '\xF0' && utf8.bytes[1] == '\x9F' && utf8.bytes[2] == '\x8C' && utf8.bytes[3] == '\x88');
TEST_CHECK(utf8.bytes[4] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x1F308); TEST_CHECK(guf_utf8_decode(&utf8) == 0x1F308);
TEST_CHECK(guf_utf8_encode(&utf8, 0x130B8)); // "𓂸" (Egyptian Hieroglyph D052) TEST_CHECK(guf_utf8_encode(&utf8, 0x130B8)); // "𓂸" (Egyptian Hieroglyph D052)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 4); TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 4);
TEST_CHECK(utf8.bytes[0] == '\xF0' && utf8.bytes[1] == '\x93' && utf8.bytes[2] == '\x82' && utf8.bytes[3] == '\xB8'); TEST_CHECK(utf8.bytes[0] == '\xF0' && utf8.bytes[1] == '\x93' && utf8.bytes[2] == '\x82' && utf8.bytes[3] == '\xB8');
TEST_CHECK(utf8.bytes[4] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x130B8); TEST_CHECK(guf_utf8_decode(&utf8) == 0x130B8);
TEST_CHECK(guf_utf8_encode(&utf8, 0x1F97A)); // "🥺" (Face with Pleading Eyes) TEST_CHECK(guf_utf8_encode(&utf8, 0x1F97A)); // "🥺" (Face with Pleading Eyes)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 4); TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 4);
TEST_CHECK(utf8.bytes[0] == '\xF0' && utf8.bytes[1] == '\x9F' && utf8.bytes[2] == '\xA5' && utf8.bytes[3] == '\xBA'); TEST_CHECK(utf8.bytes[0] == '\xF0' && utf8.bytes[1] == '\x9F' && utf8.bytes[2] == '\xA5' && utf8.bytes[3] == '\xBA');
TEST_CHECK(utf8.bytes[4] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x1F97A); TEST_CHECK(guf_utf8_decode(&utf8) == 0x1F97A);
TEST_CHECK(guf_utf8_encode(&utf8, 0x1F980)); // "🦀" (Crab) TEST_CHECK(guf_utf8_encode(&utf8, 0x1F980)); // "🦀" (Crab)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 4); TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 4);
TEST_CHECK(utf8.bytes[0] == '\xF0' && utf8.bytes[1] == '\x9F' && utf8.bytes[2] == '\xA6' && utf8.bytes[3] == '\x80'); TEST_CHECK(utf8.bytes[0] == '\xF0' && utf8.bytes[1] == '\x9F' && utf8.bytes[2] == '\xA6' && utf8.bytes[3] == '\x80');
TEST_CHECK(utf8.bytes[4] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x1F980); TEST_CHECK(guf_utf8_decode(&utf8) == 0x1F980);
// Invalid characters: // Invalid characters:
utf8 = {.bytes = {'\xC0', '\x80', 0, 0, 0}}; utf8 = {.bytes = {'\xC0', '\x80', 0, 0}};
TEST_CHECK(guf_utf8_decode(&utf8) < 0); TEST_CHECK(guf_utf8_decode(&utf8) < 0);
utf8 = {.bytes = {'\xC0', 0, 0, 0, 0}}; utf8 = {.bytes = {'\xC0', 0, 0, 0}};
TEST_CHECK(guf_utf8_decode(&utf8) < 0); TEST_CHECK(guf_utf8_decode(&utf8) < 0);
utf8 = {.bytes = {'\x80', 0, 0, 0, 0}}; utf8 = {.bytes = {'\x80', 0, 0, 0}};
TEST_CHECK(guf_utf8_decode(&utf8) < 0); TEST_CHECK(guf_utf8_decode(&utf8) < 0);
// "The definition of UTF-8 prohibits encoding character numbers between U+D800 and U+DFFF" (surrogate pairs). // "The definition of UTF-8 prohibits encoding character numbers between U+D800 and U+DFFF" (surrogate pairs).

2
src/test/test_utf8.hpp Normal file → Executable file
View File

@ -9,7 +9,7 @@ extern "C"
struct UTF8Test : public Test struct UTF8Test : public Test
{ {
UTF8Test(const std::string& name) : Test(name) UTF8Test(const std::string& nm) : Test(nm)
{ {
allocator_ctx.zero_init = false; allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 5, "UTF8Test_allocator", NULL, NULL); guf_alloc_tracker_init(&allocator_ctx.tracker, 5, "UTF8Test_allocator", NULL, NULL);

6
todo.txt Normal file → Executable file
View File

@ -1,6 +1,12 @@
- fix readonly str/uninit?
- dict: allow set
- guf_stack, guf_queue, guf_dqueue, guf_prio_queue (using a heap), guf_ringbuf - guf_stack, guf_queue, guf_dqueue, guf_prio_queue (using a heap), guf_ringbuf
- sort: add cpp #ifdef to remove restrict from declaration - sort: add cpp #ifdef to remove restrict from declaration
- str: start_with, ends_with, find
- guf_wrapping_mul_TYPE: Not 100 % sure if it does not depend on implementation defined behaviour, but it shouldn't - guf_wrapping_mul_TYPE: Not 100 % sure if it does not depend on implementation defined behaviour, but it shouldn't
- https://en.cppreference.com/w/c/types/integer (apparently the signed fixed width integer types are guaranteed to be two's complement...) - https://en.cppreference.com/w/c/types/integer (apparently the signed fixed width integer types are guaranteed to be two's complement...)

288
tools/ckdint-gen.py Normal file → Executable file
View File

@ -12,7 +12,10 @@ class IntType:
INT_TYPE_ABBR: str INT_TYPE_ABBR: str
INT_MIN: str INT_MIN: str
INT_MAX: str INT_MAX: str
UINT_TYPE: str = "size_t" UINT_TYPE: str
UINT_MAX: str
is_optional: bool = False
@dataclass @dataclass
class UintType: class UintType:
@ -20,15 +23,17 @@ class UintType:
INT_TYPE_ABBR: str INT_TYPE_ABBR: str
INT_MIN: str INT_MIN: str
INT_MAX: str INT_MAX: str
is_optional: bool = False
def generate_ckdint_functions(int_types: list, uint_types: list) -> Tuple[str, str]: def generate_ckdint_functions(int_types: list, uint_types: list) -> Tuple[str, str]:
ckd_add_sub_uint_header = textwrap.dedent(""" ckd_add_sub_uint_header = textwrap.dedent("""\
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b);
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_{type_abbr}({type} a, {type} b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_{type_abbr}({type} a, {type} b);
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_{type_abbr}({type} a, {type} b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_{type_abbr}({type} a, {type} b);
""") """)
ckd_add_sub_uint = textwrap.dedent("""
ckd_add_sub_uint = textwrap.dedent("""\
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b) GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b)
{{ {{
if (b > 0 && a > {int_max} - b) {{ if (b > 0 && a > {int_max} - b) {{
@ -52,13 +57,43 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> Tuple[str, s
}} }}
""") """)
ckd_add_sub_uint_LEAST = textwrap.dedent("""\
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b)
{{
a = GUF_UWRAP_{bits}(a);
b = GUF_UWRAP_{bits}(b);
if (b > 0 && a > {int_max} - b) {{
return GUF_MATH_CKD_OVERFLOW_POS;
}} else {{
return GUF_MATH_CKD_SUCCESS;
}}
}}
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_{type_abbr}({type} a, {type} b)
{{
a = GUF_UWRAP_{bits}(a);
b = GUF_UWRAP_{bits}(b);
if (b > a) {{
return GUF_MATH_CKD_OVERFLOW_NEG;
}} else {{
return GUF_MATH_CKD_SUCCESS;
}}
}}
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_{type_abbr}({type} a, {type} b)
{{
a = GUF_UWRAP_{bits}(a);
b = GUF_UWRAP_{bits}(b);
const {type} c = GUF_UWRAP_{bits}( 1u * a * b );
return a != 0 && ((1u * c / a) != b) ? GUF_MATH_CKD_OVERFLOW_POS : GUF_MATH_CKD_SUCCESS;
}}
""")
ckd_add_sub_int_header = textwrap.dedent(""" ckd_add_sub_int_header = textwrap.dedent("""\
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b);
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_{type_abbr}({type} a, {type} b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_{type_abbr}({type} a, {type} b);
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_{type_abbr}({type} a, {type} b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_{type_abbr}({type} a, {type} b);
""") """)
ckd_add_sub_int = textwrap.dedent("""
ckd_add_sub_int = textwrap.dedent("""\
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b) GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b)
{{ {{
if (b > 0 && a > {int_max} - b) {{ if (b > 0 && a > {int_max} - b) {{
@ -107,7 +142,7 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> Tuple[str, s
saturating_wrapping_int_header = textwrap.dedent(""" saturating_wrapping_int_header = textwrap.dedent("""\
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result);
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_{type_abbr}({type} a, {type} b, {type} *result);
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_{type_abbr}({type} a, {type} b, {type} *result);
@ -116,7 +151,8 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> Tuple[str, s
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_{type_abbr}({type} a, {type} b, {type} *result);
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_{type_abbr}({type} a, {type} b, {type} *result);
""") """)
saturating_wrapping_int = textwrap.dedent("""
saturating_wrapping_int = textwrap.dedent("""\
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result) GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result)
{{ {{
const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b); const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b);
@ -187,10 +223,10 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> Tuple[str, s
*result = a + b; *result = a + b;
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
*result = (a + {int_min}) + (b + {int_min}); *result = ({type})( (a + ({type}){int_min}) + (b + ({type}){int_min}) );
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
*result = (a - {int_min}) + (b - {int_min}); *result = ({type})( (a - ({type}){int_min}) + (b - ({type}){int_min}) );
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -208,11 +244,11 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> Tuple[str, s
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
GUF_ASSERT(b < 0); GUF_ASSERT(b < 0);
*result = (a + {int_min}) - (b - {int_min}); // TODO: not sure *result = ({type})( (a + ({type}){int_min}) - (b - ({type}){int_min}) ); // TODO: not sure
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
GUF_ASSERT(b > 0); GUF_ASSERT(b > 0);
*result = (a - {int_min}) - (b + {int_min}); // TODO: not sure *result = ({type})( (a - ({type}){int_min}) - (b + ({type}){int_min}) ); // TODO: not sure
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -232,9 +268,10 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> Tuple[str, s
case GUF_MATH_CKD_OVERFLOW_NEG: {{ case GUF_MATH_CKD_OVERFLOW_NEG: {{
{uint_type} res = 1u * ({uint_type})a * ({uint_type})b; {uint_type} res = 1u * ({uint_type})a * ({uint_type})b;
if (res > {int_max}) {{ // This is the fix for implementation defined conversion from unsigned to signed. if (res > {int_max}) {{ // This is the fix for implementation defined conversion from unsigned to signed.
const {uint_type} mod = (1u + ({uint_type}){int_max}); const {uint_type} mod = ({uint_type}){int_max} + 1u;
res = mod > 0 ? (1u * res % mod) : res; GUF_ASSERT(mod > 0);
*result = {int_min} + ({type})res; res = 1u * res % mod;
*result = ({type}){int_min} + ({type})res;
}} else {{ }} else {{
*result = ({type})res; *result = ({type})res;
}} }}
@ -248,7 +285,8 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> Tuple[str, s
}} }}
""") """)
saturating_wrapping_uint_header = textwrap.dedent("""
saturating_wrapping_uint_header = textwrap.dedent("""\
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result);
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_{type_abbr}({type} a, {type} b, {type} *result);
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_{type_abbr}({type} a, {type} b, {type} *result);
@ -258,7 +296,7 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> Tuple[str, s
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_{type_abbr}({type} a, {type} b, {type} *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_{type_abbr}({type} a, {type} b, {type} *result);
""") """)
saturating_wrapping_uint = textwrap.dedent(""" saturating_wrapping_uint = textwrap.dedent("""\
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result) GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result)
{{ {{
const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b); const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b);
@ -340,29 +378,190 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> Tuple[str, s
}} }}
""") """)
saturating_wrapping_uint_LEAST = textwrap.dedent("""\
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b);
GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_OVERFLOW_POS);
if (result) {{
switch (check) {{
case GUF_MATH_CKD_SUCCESS:
*result = a + b;
break;
case GUF_MATH_CKD_OVERFLOW_POS:
*result = {int_max};
break;
default:
GUF_ASSERT(false);
}}
}}
return check;
}}
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_sub_{type_abbr}(a, b);
GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_OVERFLOW_NEG);
if (result) {{
switch (check) {{
case GUF_MATH_CKD_SUCCESS:
*result = a - b;
break;
case GUF_MATH_CKD_OVERFLOW_NEG:
*result = 0;
break;
default:
GUF_ASSERT(false);
}}
}}
return check;
}}
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_mul_{type_abbr}(a, b);
GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_OVERFLOW_POS);
if (result) {{
switch (check) {{
case GUF_MATH_CKD_SUCCESS:
*result = a * b;
break;
case GUF_MATH_CKD_OVERFLOW_POS:
*result = {int_max};
break;
default:
GUF_ASSERT(false);
}}
}}
return check;
}}
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_{type_abbr}({type} a, {type} b, {type} *result)
{{
a = GUF_UWRAP_{bits}(a);
b = GUF_UWRAP_{bits}(b);
const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b);
if (result) {{
*result = GUF_UWRAP_{bits}( 1u * a + b );
}}
return check;
}}
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_{type_abbr}({type} a, {type} b, {type} *result)
{{
a = GUF_UWRAP_{bits}(a);
b = GUF_UWRAP_{bits}(b);
const guf_math_ckd_result check = guf_ckd_sub_{type_abbr}(a, b);
if (result) {{
*result = GUF_UWRAP_{bits}( 1u * a - b );
}}
return check;
}}
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_{type_abbr}({type} a, {type} b, {type} *result)
{{
a = GUF_UWRAP_{bits}(a);
b = GUF_UWRAP_{bits}(b);
const guf_math_ckd_result check = guf_ckd_mul_{type_abbr}(a, b);
if (result) {{
*result = GUF_UWRAP_{bits}( 1u * a * b );
}}
return check;
}}
""")
text_result = "// Signed integer arithmetic checks (generated with libguf/tools/ckdint-gen.py)\n" text_result = "// Signed integer arithmetic checks (generated with libguf/tools/ckdint-gen.py)\n"
text_result_header = text_result text_result_header = text_result
for type in int_types: for type in int_types:
text_result += ckd_add_sub_int.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) end = "\n"
text_result_header += ckd_add_sub_int_header.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) if type.is_optional:
text_result += f"#ifdef {type.INT_MAX}\n"
text_result_header += f"#ifdef {type.INT_MAX}\n"
end = ""
text_result += ckd_add_sub_int.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX, uint_max = type.UINT_MAX) + end
text_result_header += ckd_add_sub_int_header.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX, uint_max = type.UINT_MAX) + end
if type.is_optional:
text_result += "#endif\n\n"
text_result_header += "#endif\n\n"
text_result += "\n// Unsigned integer arithmetic checks (generated with libguf/tools/ckdint-gen.py) \n" text_result += "\n// Unsigned integer arithmetic checks (generated with libguf/tools/ckdint-gen.py) \n"
text_result_header += "\n// Unsigned integer arithmetic checks (generated with libguf/tools/ckdint-gen.py) \n" text_result_header += "\n// Unsigned integer arithmetic checks (generated with libguf/tools/ckdint-gen.py) \n"
for type in uint_types: for type in uint_types:
text_result += ckd_add_sub_uint.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) end = "\n"
text_result_header += ckd_add_sub_uint_header.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) if type.is_optional:
text_result += f"#ifdef {type.INT_MAX}\n"
text_result_header += f"#ifdef {type.INT_MAX}\n"
end = ""
if "uint_least" in type.INT_TYPE:
bits = 0
if "least8" in type.INT_TYPE:
bits = 8
elif "least16" in type.INT_TYPE:
bits = 16
elif "least32" in type.INT_TYPE:
bits = 32
elif "least64" in type.INT_TYPE:
bits = 64
else:
assert(False)
text_result += ckd_add_sub_uint_LEAST.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX, bits = str(bits))
else:
text_result += ckd_add_sub_uint.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) + end
text_result_header += ckd_add_sub_uint_header.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) + end
if type.is_optional:
text_result += "#endif\n\n"
text_result_header += "#endif\n\n"
text_result += "\n\n// Signed saturating/wrapping arithmetic (generated with libguf/tools/ckdint-gen.py)\n" text_result += "\n\n// Signed saturating/wrapping arithmetic (generated with libguf/tools/ckdint-gen.py)\n"
text_result_header += "\n\n// Signed saturating/wrapping arithmetic (generated with libguf/tools/ckdint-gen.py)\n" text_result_header += "\n\n// Signed saturating/wrapping arithmetic (generated with libguf/tools/ckdint-gen.py)\n"
for type in int_types: for type in int_types:
text_result += saturating_wrapping_int.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX, uint_type = type.UINT_TYPE) + "\n" end = "\n"
text_result_header += saturating_wrapping_int_header.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX, uint_type = type.UINT_TYPE) + "\n" if type.is_optional:
text_result += f"#ifdef {type.INT_MAX}\n"
text_result_header += f"#ifdef {type.INT_MAX}\n"
end = ""
text_result += saturating_wrapping_int.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX, uint_type = type.UINT_TYPE, uint_max = type.UINT_MAX) + end
text_result_header += saturating_wrapping_int_header.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX, uint_type = type.UINT_TYPE, uint_max = type.UINT_MAX) + end
if type.is_optional:
text_result += "#endif\n\n"
text_result_header += "#endif\n\n"
text_result += "\n// Unsigned saturating/wrapping arithmetic (generated with libguf/tools/ckdint-gen.py)\n" text_result += "\n// Unsigned saturating/wrapping arithmetic (generated with libguf/tools/ckdint-gen.py)\n"
text_result_header += "\n// Unsigned saturating/wrapping arithmetic (generated with libguf/tools/ckdint-gen.py)\n" text_result_header += "\n// Unsigned saturating/wrapping arithmetic (generated with libguf/tools/ckdint-gen.py)\n"
for type in uint_types: for type in uint_types:
text_result += saturating_wrapping_uint.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) + "\n" end = "\n"
text_result_header += saturating_wrapping_uint_header.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) + "\n" if type.is_optional:
text_result += f"#ifdef {type.INT_MAX}\n"
text_result_header += f"#ifdef {type.INT_MAX}\n"
end = ""
if "uint_least" in type.INT_TYPE:
bits = 0
if "least8" in type.INT_TYPE:
bits = 8
elif "least16" in type.INT_TYPE:
bits = 16
elif "least32" in type.INT_TYPE:
bits = 32
elif "least64" in type.INT_TYPE:
bits = 64
else:
assert(False)
text_result += saturating_wrapping_uint_LEAST.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX, bits = str(bits)) + end
else:
text_result += saturating_wrapping_uint.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) + end
text_result_header += saturating_wrapping_uint_header.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) + end
if type.is_optional:
text_result += "#endif\n\n"
text_result_header += "#endif\n\n"
return (text_result_header, text_result) return (text_result_header, text_result)
@ -371,28 +570,41 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> Tuple[str, s
if __name__ == "__main__": if __name__ == "__main__":
int_types = [ int_types = [
IntType(INT_TYPE = "int", INT_TYPE_ABBR = "int", INT_MIN = "INT_MIN", INT_MAX = "INT_MAX", UINT_TYPE = "unsigned"), IntType(INT_TYPE = "int", INT_TYPE_ABBR = "int", INT_MIN = "INT_MIN", INT_MAX = "INT_MAX", UINT_TYPE = "unsigned", UINT_MAX = "UINT_MAX"),
IntType(INT_TYPE = "long", INT_TYPE_ABBR = "long", INT_MIN = "LONG_MIN", INT_MAX = "LONG_MAX", UINT_TYPE = "unsigned long"), IntType(INT_TYPE = "long", INT_TYPE_ABBR = "long", INT_MIN = "LONG_MIN", INT_MAX = "LONG_MAX", UINT_TYPE = "unsigned long", UINT_MAX = "ULONG_MAX"),
IntType(INT_TYPE = "long long", INT_TYPE_ABBR = "long_long", INT_MIN = "LLONG_MIN", INT_MAX = "LLONG_MAX", UINT_TYPE = "unsigned long long"), IntType(INT_TYPE = "long long", INT_TYPE_ABBR = "long_long", INT_MIN = "LLONG_MIN", INT_MAX = "LLONG_MAX", UINT_TYPE = "unsigned long long", UINT_MAX = "ULLONG_MAX"),
IntType(INT_TYPE = "int8_t", INT_TYPE_ABBR = "i8", INT_MIN = "INT8_MIN", INT_MAX = "INT8_MAX", UINT_TYPE = "uint8_t"), # TODO: size_t is not necessarily the unsigned ptrdiff_t equivalent
IntType(INT_TYPE = "int16_t", INT_TYPE_ABBR = "i16", INT_MIN = "INT16_MIN", INT_MAX = "INT16_MAX", UINT_TYPE = "uint16_t"), IntType(INT_TYPE = "ptrdiff_t", INT_TYPE_ABBR = "ptrdiff_t", INT_MIN = "PTRDIFF_MIN", INT_MAX = "PTRDIFF_MAX", UINT_TYPE = "size_t", UINT_MAX = "SIZE_MAX"),
IntType(INT_TYPE = "int32_t", INT_TYPE_ABBR = "i32", INT_MIN = "INT32_MIN", INT_MAX = "INT32_MAX", UINT_TYPE = "uint32_t"),
IntType(INT_TYPE = "int64_t", INT_TYPE_ABBR = "i64", INT_MIN = "INT64_MIN", INT_MAX = "INT64_MAX", UINT_TYPE = "uint64_t"), IntType(INT_TYPE = "int_least8_t", INT_TYPE_ABBR = "least_i8", INT_MIN = "GUF_INT8_MIN", INT_MAX = "GUF_INT8_MAX", UINT_TYPE = "uint_least8_t", UINT_MAX = "GUF_UINT8_MAX"),
IntType(INT_TYPE = "ptrdiff_t", INT_TYPE_ABBR = "ptrdiff_t", INT_MIN = "PTRDIFF_MIN", INT_MAX = "PTRDIFF_MAX", UINT_TYPE = "size_t"), # TODO: size_t is not necessarily the unsigned ptrdiff_t equivalent IntType(INT_TYPE = "int_least16_t", INT_TYPE_ABBR = "least_i16", INT_MIN = "GUF_INT16_MIN", INT_MAX = "GUF_INT16_MAX", UINT_TYPE = "uint_least16_t", UINT_MAX = "GUF_UINT16_MAX"),
IntType(INT_TYPE = "int_least32_t", INT_TYPE_ABBR = "least_i32", INT_MIN = "GUF_INT32_MIN", INT_MAX = "GUF_INT32_MAX", UINT_TYPE = "uint_least32_t", UINT_MAX = "GUF_UINT32_MAX"),
IntType(INT_TYPE = "int_least64_t", INT_TYPE_ABBR = "least_i64", INT_MIN = "GUF_INT64_MIN", INT_MAX = "GUF_INT64_MAX", UINT_TYPE = "uint_least64_t", UINT_MAX = "GUF_UINT64_MAX"),
IntType(INT_TYPE = "int8_t", INT_TYPE_ABBR = "i8", INT_MIN = "INT8_MIN", INT_MAX = "INT8_MAX", UINT_TYPE = "uint8_t", UINT_MAX = "GUF_UINT8_MAX", is_optional = True),
IntType(INT_TYPE = "int16_t", INT_TYPE_ABBR = "i16", INT_MIN = "INT16_MIN", INT_MAX = "INT16_MAX", UINT_TYPE = "uint16_t", UINT_MAX = "GUF_UINT16_MAX", is_optional = True),
IntType(INT_TYPE = "int32_t", INT_TYPE_ABBR = "i32", INT_MIN = "INT32_MIN", INT_MAX = "INT32_MAX", UINT_TYPE = "uint32_t", UINT_MAX = "GUF_UINT32_MAX", is_optional = True),
IntType(INT_TYPE = "int64_t", INT_TYPE_ABBR = "i64", INT_MIN = "INT64_MIN", INT_MAX = "INT64_MAX", UINT_TYPE = "uint64_t", UINT_MAX = "GUF_UINT64_MAX", is_optional = True),
] ]
uint_types = [ uint_types = [
UintType(INT_TYPE = "unsigned char", INT_TYPE_ABBR = "uchar", INT_MIN = "0", INT_MAX = "UCHAR_MAX"), UintType(INT_TYPE = "unsigned char", INT_TYPE_ABBR = "uchar", INT_MIN = "0", INT_MAX = "UCHAR_MAX"),
UintType(INT_TYPE = "unsigned", INT_TYPE_ABBR = "unsigned", INT_MIN = "0", INT_MAX = "UINT_MAX"), UintType(INT_TYPE = "unsigned", INT_TYPE_ABBR = "unsigned", INT_MIN = "0", INT_MAX = "UINT_MAX"),
UintType(INT_TYPE = "unsigned long", INT_TYPE_ABBR = "ulong", INT_MIN = "ULONG_MIN", INT_MAX = "ULONG_MAX"), UintType(INT_TYPE = "unsigned long", INT_TYPE_ABBR = "ulong", INT_MIN = "0", INT_MAX = "ULONG_MAX"),
UintType(INT_TYPE = "unsigned long long", INT_TYPE_ABBR = "ulong_long", INT_MIN = "ULLONG_MIN", INT_MAX = "ULLONG_MAX"), UintType(INT_TYPE = "unsigned long long", INT_TYPE_ABBR = "ulong_long", INT_MIN = "0", INT_MAX = "ULLONG_MAX"),
UintType(INT_TYPE = "uint8_t", INT_TYPE_ABBR = "u8", INT_MIN = "0", INT_MAX = "UINT8_MAX"),
UintType(INT_TYPE = "uint16_t", INT_TYPE_ABBR = "u16", INT_MIN = "0", INT_MAX = "UINT16_MAX"),
UintType(INT_TYPE = "uint32_t", INT_TYPE_ABBR = "u32", INT_MIN = "0", INT_MAX = "UINT32_MAX"),
UintType(INT_TYPE = "uint64_t", INT_TYPE_ABBR = "u64", INT_MIN = "0", INT_MAX = "UINT64_MAX"),
UintType(INT_TYPE = "size_t", INT_TYPE_ABBR = "size_t", INT_MIN = "0", INT_MAX = "SIZE_MAX"), UintType(INT_TYPE = "size_t", INT_TYPE_ABBR = "size_t", INT_MIN = "0", INT_MAX = "SIZE_MAX"),
UintType(INT_TYPE = "uint_least8_t", INT_TYPE_ABBR = "least_u8", INT_MIN = "0", INT_MAX = "GUF_UINT8_MAX"),
UintType(INT_TYPE = "uint_least16_t", INT_TYPE_ABBR = "least_u16", INT_MIN = "0", INT_MAX = "GUF_UINT16_MAX"),
UintType(INT_TYPE = "uint_least32_t", INT_TYPE_ABBR = "least_u32", INT_MIN = "0", INT_MAX = "GUF_UINT32_MAX"),
UintType(INT_TYPE = "uint_least64_t", INT_TYPE_ABBR = "least_u64", INT_MIN = "0", INT_MAX = "GUF_UINT64_MAX"),
UintType(INT_TYPE = "uint8_t", INT_TYPE_ABBR = "u8", INT_MIN = "0", INT_MAX = "UINT8_MAX", is_optional = True),
UintType(INT_TYPE = "uint16_t", INT_TYPE_ABBR = "u16", INT_MIN = "0", INT_MAX = "UINT16_MAX", is_optional = True),
UintType(INT_TYPE = "uint32_t", INT_TYPE_ABBR = "u32", INT_MIN = "0", INT_MAX = "UINT32_MAX", is_optional = True),
UintType(INT_TYPE = "uint64_t", INT_TYPE_ABBR = "u64", INT_MIN = "0", INT_MAX = "UINT64_MAX", is_optional = True),
] ]
code_header, code_impl = generate_ckdint_functions(int_types = int_types, uint_types= uint_types) code_header, code_impl = generate_ckdint_functions(int_types = int_types, uint_types= uint_types)

41
tools/intwrap-gen.py Executable file
View File

@ -0,0 +1,41 @@
"""
Generate typesafe unsigned integer wrapping functions
"""
def gen_uwrap_code(uint_type: str, uint_type_abbr: str, wrap_width: int, wrap_max_uint: str) -> str:
template = "static inline {uint_type} guf_wrap{wrap_width}_{uint_type_abbr}({uint_type} a) {{ return a & {wrap_max_uint}; }}\n"
return template.format(uint_type = uint_type, uint_type_abbr = uint_type_abbr, wrap_width = wrap_width, wrap_max_uint = wrap_max_uint)
if __name__ == "__main__":
uint_types = [
{"uint_type": "uint_least8_t", "uint_type_abbr": "uint_least8_t", "wrap_width": 8, "wrap_max_uint": "GUF_UINT8_MAX"},
{"uint_type": "uint_fast8_t", "uint_type_abbr": "uint_fast8_t", "wrap_width": 8, "wrap_max_uint": "GUF_UINT8_MAX"},
"\n",
{"uint_type": "uint_least16_t", "uint_type_abbr": "uint_least16_t", "wrap_width": 16, "wrap_max_uint": "GUF_UINT16_MAX"},
{"uint_type": "uint_fast16_t", "uint_type_abbr": "uint_fast16_t", "wrap_width": 16, "wrap_max_uint": "GUF_UINT16_MAX"},
"\n",
{"uint_type": "uint_least32_t", "uint_type_abbr": "uint_least32_t", "wrap_width": 32, "wrap_max_uint": "GUF_UINT32_MAX"},
{"uint_type": "uint_fast32_t", "uint_type_abbr": "uint_fast32_t", "wrap_width": 32, "wrap_max_uint": "GUF_UINT32_MAX"},
"\n",
{"uint_type": "uint_least64_t", "uint_type_abbr": "uint_least64_t", "wrap_width": 64, "wrap_max_uint": "GUF_UINT64_MAX"},
{"uint_type": "uint_fast64_t", "uint_type_abbr": "uint_fast64_t", "wrap_width": 64, "wrap_max_uint": "GUF_UINT64_MAX"},
"\n",
{"uint_type": "unsigned char", "uint_type_abbr": "uchar", "wrap_width": 8, "wrap_max_uint": "GUF_UINT8_MAX"},
{"uint_type": "unsigned short", "uint_type_abbr": "ushort", "wrap_width": 16, "wrap_max_uint": "GUF_UINT16_MAX"},
{"uint_type": "unsigned long", "uint_type_abbr": "ulong", "wrap_width": 32, "wrap_max_uint": "GUF_UINT32_MAX"},
{"uint_type": "unsigned long long", "uint_type_abbr": "ulong_long", "wrap_width": 64, "wrap_max_uint": "GUF_UINT64_MAX"},
]
code = "// Typesafe unsigned integer wrapping functions (generated with tools/intwrap-gen.py)\n"
for uint_t in uint_types:
if isinstance(uint_t, str):
code += uint_t
else:
code += gen_uwrap_code(**uint_t)
print(code)

45
tools/min_max_clamp-gen.py Normal file → Executable file
View File

@ -11,6 +11,7 @@ class IntType:
INT_TYPE_ABBR: str INT_TYPE_ABBR: str
INT_MIN: str INT_MIN: str
INT_MAX: str INT_MAX: str
is_optional: bool = False
@dataclass @dataclass
class UintType: class UintType:
@ -18,10 +19,11 @@ class UintType:
INT_TYPE_ABBR: str INT_TYPE_ABBR: str
INT_MIN: str INT_MIN: str
INT_MAX: str INT_MAX: str
is_optional: bool = False
def gen_min_max_clamp(int_types: list, uint_types: list) -> str: def gen_min_max_clamp(int_types: list, uint_types: list) -> str:
template = textwrap.dedent(""" template = textwrap.dedent("""\
static inline {type} guf_min_{type_abbr}({type} a, {type} b) {{ return a < b ? a : b; }} static inline {type} guf_min_{type_abbr}({type} a, {type} b) {{ return a < b ? a : b; }}
static inline {type} guf_max_{type_abbr}({type} a, {type} b) {{ return a > b ? a : b; }} static inline {type} guf_max_{type_abbr}({type} a, {type} b) {{ return a > b ? a : b; }}
static inline {type} guf_clamp_{type_abbr}({type} x, {type} min, {type} max) {{ if (x < min) {{return min;}} if (x > max) {{return max;}} return x; }} static inline {type} guf_clamp_{type_abbr}({type} x, {type} min, {type} max) {{ if (x < min) {{return min;}} if (x > max) {{return max;}} return x; }}
@ -29,12 +31,21 @@ def gen_min_max_clamp(int_types: list, uint_types: list) -> str:
result = "\n// Signed min, max, clamp functions (generated with libguf/tools/min_max_clamp-gen.py)\n" result = "\n// Signed min, max, clamp functions (generated with libguf/tools/min_max_clamp-gen.py)\n"
for t in int_types: for t in int_types:
if t.is_optional:
result += "#ifdef {int_max}\n".format(int_max = t.INT_MAX)
result += template.format(type = t.INT_TYPE, type_abbr = t.INT_TYPE_ABBR) result += template.format(type = t.INT_TYPE, type_abbr = t.INT_TYPE_ABBR)
if t.is_optional:
result += "#endif\n"
result += "\n"
result += "\n// Unsigned min, max, clamp functions (generated with libguf/tools/min_max_clamp-gen.py)\n" result += "\n// Unsigned min, max, clamp functions (generated with libguf/tools/min_max_clamp-gen.py)\n"
for t in uint_types: for t in uint_types:
if t.is_optional:
result +="#ifdef {int_max}\n".format(int_max = t.INT_MAX)
result += template.format(type = t.INT_TYPE, type_abbr = t.INT_TYPE_ABBR) result += template.format(type = t.INT_TYPE, type_abbr = t.INT_TYPE_ABBR)
if t.is_optional:
result += "#endif\n"
result += "\n"
return result return result
@ -46,14 +57,22 @@ if __name__ == "__main__":
IntType(INT_TYPE = "long", INT_TYPE_ABBR = "long", INT_MIN = "LONG_MIN", INT_MAX = "LONG_MAX"), IntType(INT_TYPE = "long", INT_TYPE_ABBR = "long", INT_MIN = "LONG_MIN", INT_MAX = "LONG_MAX"),
IntType(INT_TYPE = "long long", INT_TYPE_ABBR = "long_long", INT_MIN = "LLONG_MIN", INT_MAX = "LLONG_MAX"), IntType(INT_TYPE = "long long", INT_TYPE_ABBR = "long_long", INT_MIN = "LLONG_MIN", INT_MAX = "LLONG_MAX"),
IntType(INT_TYPE = "int8_t", INT_TYPE_ABBR = "i8", INT_MIN = "INT8_MIN", INT_MAX = "INT8_MAX"),
IntType(INT_TYPE = "int16_t", INT_TYPE_ABBR = "i16", INT_MIN = "INT16_MIN", INT_MAX = "INT16_MAX"),
IntType(INT_TYPE = "int32_t", INT_TYPE_ABBR = "i32", INT_MIN = "INT32_MIN", INT_MAX = "INT32_MAX"),
IntType(INT_TYPE = "int64_t", INT_TYPE_ABBR = "i64", INT_MIN = "INT64_MIN", INT_MAX = "INT64_MAX"),
IntType(INT_TYPE = "ptrdiff_t", INT_TYPE_ABBR = "ptrdiff_t", INT_MIN = "PTRDIFF_MIN", INT_MAX = "PTRDIFF_MAX"), IntType(INT_TYPE = "ptrdiff_t", INT_TYPE_ABBR = "ptrdiff_t", INT_MIN = "PTRDIFF_MIN", INT_MAX = "PTRDIFF_MAX"),
IntType(INT_TYPE = "int_fast8_t", INT_TYPE_ABBR = "i8_fast", INT_MIN = "GUF_INT8_MIN", INT_MAX = "GUF_INT8_MAX"),
IntType(INT_TYPE = "int_fast16_t", INT_TYPE_ABBR = "i16_fast", INT_MIN = "GUF_INT16_MIN", INT_MAX = "GUF_INT16_MAX"),
IntType(INT_TYPE = "int_fast32_t", INT_TYPE_ABBR = "i32_fast", INT_MIN = "GUF_INT32_MIN", INT_MAX = "GUF_INT32_MAX"),
IntType(INT_TYPE = "int_fast64_t", INT_TYPE_ABBR = "i64_fast", INT_MIN = "GUF_INT64_MIN", INT_MAX = "GUF_INT64_MAX"),
IntType(INT_TYPE = "float", INT_TYPE_ABBR = "f32", INT_MIN = "-FLT_MAX", INT_MAX = "FLT_MAX"), IntType(INT_TYPE = "float", INT_TYPE_ABBR = "f32", INT_MIN = "-FLT_MAX", INT_MAX = "FLT_MAX"),
IntType(INT_TYPE = "double", INT_TYPE_ABBR = "f64", INT_MIN = "-DBL_MAX", INT_MAX = "DBL_MAX"), IntType(INT_TYPE = "double", INT_TYPE_ABBR = "f64", INT_MIN = "-DBL_MAX", INT_MAX = "DBL_MAX"),
IntType(INT_TYPE = "int8_t", INT_TYPE_ABBR = "i8", INT_MIN = "INT8_MIN", INT_MAX = "INT8_MAX", is_optional=True),
IntType(INT_TYPE = "int16_t", INT_TYPE_ABBR = "i16", INT_MIN = "INT16_MIN", INT_MAX = "INT16_MAX", is_optional=True),
IntType(INT_TYPE = "int32_t", INT_TYPE_ABBR = "i32", INT_MIN = "INT32_MIN", INT_MAX = "INT32_MAX", is_optional=True),
IntType(INT_TYPE = "int64_t", INT_TYPE_ABBR = "i64", INT_MIN = "INT64_MIN", INT_MAX = "INT64_MAX", is_optional=True),
] ]
uint_types = [ uint_types = [
@ -62,11 +81,17 @@ if __name__ == "__main__":
UintType(INT_TYPE = "unsigned long", INT_TYPE_ABBR = "ulong", INT_MIN = "0", INT_MAX = "ULONG_MAX"), UintType(INT_TYPE = "unsigned long", INT_TYPE_ABBR = "ulong", INT_MIN = "0", INT_MAX = "ULONG_MAX"),
UintType(INT_TYPE = "unsigned long long", INT_TYPE_ABBR = "ulong_long", INT_MIN = "0", INT_MAX = "ULLONG_MAX"), UintType(INT_TYPE = "unsigned long long", INT_TYPE_ABBR = "ulong_long", INT_MIN = "0", INT_MAX = "ULLONG_MAX"),
UintType(INT_TYPE = "uint8_t", INT_TYPE_ABBR = "u8", INT_MIN = "0", INT_MAX = "UINT8_MAX"),
UintType(INT_TYPE = "uint16_t", INT_TYPE_ABBR = "u16", INT_MIN = "0", INT_MAX = "UINT16_MAX"),
UintType(INT_TYPE = "uint32_t", INT_TYPE_ABBR = "u32", INT_MIN = "0", INT_MAX = "UINT32_MAX"),
UintType(INT_TYPE = "uint64_t", INT_TYPE_ABBR = "u64", INT_MIN = "0", INT_MAX = "UINT64_MAX"),
UintType(INT_TYPE = "size_t", INT_TYPE_ABBR = "size_t", INT_MIN = "0", INT_MAX = "SIZE_MAX"), UintType(INT_TYPE = "size_t", INT_TYPE_ABBR = "size_t", INT_MIN = "0", INT_MAX = "SIZE_MAX"),
UintType(INT_TYPE = "uint_fast8_t", INT_TYPE_ABBR = "u8_fast", INT_MIN = "0", INT_MAX = "GUF_UINT8_MAX"),
UintType(INT_TYPE = "uint_fast16_t", INT_TYPE_ABBR = "u16_fast", INT_MIN = "0", INT_MAX = "GUF_UINT16_MAX"),
UintType(INT_TYPE = "uint_fast32_t", INT_TYPE_ABBR = "u32_fast", INT_MIN = "0", INT_MAX = "GUF_UINT32_MAX"),
UintType(INT_TYPE = "uint_fast64_t", INT_TYPE_ABBR = "u64_fast", INT_MIN = "0", INT_MAX = "GUF_UINT64_MAX"),
UintType(INT_TYPE = "uint8_t", INT_TYPE_ABBR = "u8", INT_MIN = "0", INT_MAX = "UINT8_MAX", is_optional=True),
UintType(INT_TYPE = "uint16_t", INT_TYPE_ABBR = "u16", INT_MIN = "0", INT_MAX = "UINT16_MAX", is_optional=True),
UintType(INT_TYPE = "uint32_t", INT_TYPE_ABBR = "u32", INT_MIN = "0", INT_MAX = "UINT32_MAX", is_optional=True),
UintType(INT_TYPE = "uint64_t", INT_TYPE_ABBR = "u64", INT_MIN = "0", INT_MAX = "UINT64_MAX", is_optional=True),
] ]