Update libguf

This commit is contained in:
zeichensystem 2025-12-23 21:13:41 +01:00
parent 3343b36e29
commit 97f21e24dc
49 changed files with 5698 additions and 497 deletions

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

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;

View File

@ -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)) {
@ -598,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);

View File

@ -215,16 +215,16 @@ static inline unsigned long long guf_uabs_long_long(long long x) {if (x >= 0
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) #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 + 1u;} else {return -x;}} 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 #endif
#if defined(UINT16_MAX) && defined(INT16_MAX) #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 + 1u;} else {return -x;}} 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 #endif
#if defined(UINT32_MAX) && defined(INT32_MAX) #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 + 1u;} else {return -x;}} 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 #endif
#if defined(UINT64_MAX) && defined(INT64_MAX) #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 + 1u;} else {return -x;}} 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 #endif
// absdiff functions with unsigned result (cannot fail) // absdiff functions with unsigned result (cannot fail)

View File

@ -81,8 +81,8 @@
typedef enum guf_math_ckd_result {GUF_MATH_CKD_SUCCESS = 0, GUF_MATH_CKD_OVERFLOW_POS, GUF_MATH_CKD_OVERFLOW_NEG} guf_math_ckd_result; typedef enum guf_math_ckd_result {GUF_MATH_CKD_SUCCESS = 0, GUF_MATH_CKD_OVERFLOW_POS, GUF_MATH_CKD_OVERFLOW_NEG} guf_math_ckd_result;
#if !defined(GUF_MATH_CKDINT_IMPL_STATIC) && !defined(GUF_MATH_CKDINT_IMPL)
#if !defined(GUF_MATH_CKDINT_IMPL_STATIC) && !defined(GUF_MATH_CKDINT_IMPL)
// Signed integer arithmetic checks (generated with libguf/tools/ckdint-gen.py) // Signed integer arithmetic checks (generated with libguf/tools/ckdint-gen.py)
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_int(int a, int b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_int(int a, int b);
GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_int(int a, int b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_int(int a, int b);
@ -1365,10 +1365,10 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_int(int a, int b, int
*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 = (int)( (a + (int)INT_MIN) + (b + (int)INT_MIN) );
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
*result = (a - INT_MIN) + (b - INT_MIN); *result = (int)( (a - (int)INT_MIN) + (b - (int)INT_MIN) );
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -1386,11 +1386,11 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_int(int a, int b, int
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 = (int)( (a + (int)INT_MIN) - (b - (int)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 = (int)( (a - (int)INT_MIN) - (b + (int)INT_MIN) ); // TODO: not sure
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -1413,7 +1413,7 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_int(int a, int b, int
const unsigned mod = (unsigned)INT_MAX + 1u; const unsigned mod = (unsigned)INT_MAX + 1u;
GUF_ASSERT(mod > 0); GUF_ASSERT(mod > 0);
res = 1u * res % mod; res = 1u * res % mod;
*result = INT_MIN + (int)res; *result = (int)INT_MIN + (int)res;
} else { } else {
*result = (int)res; *result = (int)res;
} }
@ -1496,10 +1496,10 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_long(long a, long b,
*result = a + b; *result = a + b;
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
*result = (a + LONG_MIN) + (b + LONG_MIN); *result = (long)( (a + (long)LONG_MIN) + (b + (long)LONG_MIN) );
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
*result = (a - LONG_MIN) + (b - LONG_MIN); *result = (long)( (a - (long)LONG_MIN) + (b - (long)LONG_MIN) );
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -1517,11 +1517,11 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_long(long a, long b,
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
GUF_ASSERT(b < 0); GUF_ASSERT(b < 0);
*result = (a + LONG_MIN) - (b - LONG_MIN); // TODO: not sure *result = (long)( (a + (long)LONG_MIN) - (b - (long)LONG_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 - LONG_MIN) - (b + LONG_MIN); // TODO: not sure *result = (long)( (a - (long)LONG_MIN) - (b + (long)LONG_MIN) ); // TODO: not sure
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -1544,7 +1544,7 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_long(long a, long b,
const unsigned long mod = (unsigned long)LONG_MAX + 1u; const unsigned long mod = (unsigned long)LONG_MAX + 1u;
GUF_ASSERT(mod > 0); GUF_ASSERT(mod > 0);
res = 1u * res % mod; res = 1u * res % mod;
*result = LONG_MIN + (long)res; *result = (long)LONG_MIN + (long)res;
} else { } else {
*result = (long)res; *result = (long)res;
} }
@ -1627,10 +1627,10 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_long_long(long long a
*result = a + b; *result = a + b;
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
*result = (a + LLONG_MIN) + (b + LLONG_MIN); *result = (long long)( (a + (long long)LLONG_MIN) + (b + (long long)LLONG_MIN) );
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
*result = (a - LLONG_MIN) + (b - LLONG_MIN); *result = (long long)( (a - (long long)LLONG_MIN) + (b - (long long)LLONG_MIN) );
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -1648,11 +1648,11 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_long_long(long long a
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
GUF_ASSERT(b < 0); GUF_ASSERT(b < 0);
*result = (a + LLONG_MIN) - (b - LLONG_MIN); // TODO: not sure *result = (long long)( (a + (long long)LLONG_MIN) - (b - (long long)LLONG_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 - LLONG_MIN) - (b + LLONG_MIN); // TODO: not sure *result = (long long)( (a - (long long)LLONG_MIN) - (b + (long long)LLONG_MIN) ); // TODO: not sure
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -1675,7 +1675,7 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_long_long(long long a
const unsigned long long mod = (unsigned long long)LLONG_MAX + 1u; const unsigned long long mod = (unsigned long long)LLONG_MAX + 1u;
GUF_ASSERT(mod > 0); GUF_ASSERT(mod > 0);
res = 1u * res % mod; res = 1u * res % mod;
*result = LLONG_MIN + (long long)res; *result = (long long)LLONG_MIN + (long long)res;
} else { } else {
*result = (long long)res; *result = (long long)res;
} }
@ -1758,10 +1758,10 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_ptrdiff_t(ptrdiff_t a
*result = a + b; *result = a + b;
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
*result = (a + PTRDIFF_MIN) + (b + PTRDIFF_MIN); *result = (ptrdiff_t)( (a + (ptrdiff_t)PTRDIFF_MIN) + (b + (ptrdiff_t)PTRDIFF_MIN) );
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
*result = (a - PTRDIFF_MIN) + (b - PTRDIFF_MIN); *result = (ptrdiff_t)( (a - (ptrdiff_t)PTRDIFF_MIN) + (b - (ptrdiff_t)PTRDIFF_MIN) );
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -1779,11 +1779,11 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_ptrdiff_t(ptrdiff_t a
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
GUF_ASSERT(b < 0); GUF_ASSERT(b < 0);
*result = (a + PTRDIFF_MIN) - (b - PTRDIFF_MIN); // TODO: not sure *result = (ptrdiff_t)( (a + (ptrdiff_t)PTRDIFF_MIN) - (b - (ptrdiff_t)PTRDIFF_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 - PTRDIFF_MIN) - (b + PTRDIFF_MIN); // TODO: not sure *result = (ptrdiff_t)( (a - (ptrdiff_t)PTRDIFF_MIN) - (b + (ptrdiff_t)PTRDIFF_MIN) ); // TODO: not sure
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -1806,7 +1806,7 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_ptrdiff_t(ptrdiff_t a
const size_t mod = (size_t)PTRDIFF_MAX + 1u; const size_t mod = (size_t)PTRDIFF_MAX + 1u;
GUF_ASSERT(mod > 0); GUF_ASSERT(mod > 0);
res = 1u * res % mod; res = 1u * res % mod;
*result = PTRDIFF_MIN + (ptrdiff_t)res; *result = (ptrdiff_t)PTRDIFF_MIN + (ptrdiff_t)res;
} else { } else {
*result = (ptrdiff_t)res; *result = (ptrdiff_t)res;
} }
@ -1889,10 +1889,10 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_least_i8(int_least8_t
*result = a + b; *result = a + b;
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
*result = (a + GUF_INT8_MIN) + (b + GUF_INT8_MIN); *result = (int_least8_t)( (a + (int_least8_t)GUF_INT8_MIN) + (b + (int_least8_t)GUF_INT8_MIN) );
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
*result = (a - GUF_INT8_MIN) + (b - GUF_INT8_MIN); *result = (int_least8_t)( (a - (int_least8_t)GUF_INT8_MIN) + (b - (int_least8_t)GUF_INT8_MIN) );
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -1910,11 +1910,11 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_least_i8(int_least8_t
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
GUF_ASSERT(b < 0); GUF_ASSERT(b < 0);
*result = (a + GUF_INT8_MIN) - (b - GUF_INT8_MIN); // TODO: not sure *result = (int_least8_t)( (a + (int_least8_t)GUF_INT8_MIN) - (b - (int_least8_t)GUF_INT8_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 - GUF_INT8_MIN) - (b + GUF_INT8_MIN); // TODO: not sure *result = (int_least8_t)( (a - (int_least8_t)GUF_INT8_MIN) - (b + (int_least8_t)GUF_INT8_MIN) ); // TODO: not sure
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -1937,7 +1937,7 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_least_i8(int_least8_t
const uint_least8_t mod = (uint_least8_t)GUF_INT8_MAX + 1u; const uint_least8_t mod = (uint_least8_t)GUF_INT8_MAX + 1u;
GUF_ASSERT(mod > 0); GUF_ASSERT(mod > 0);
res = 1u * res % mod; res = 1u * res % mod;
*result = GUF_INT8_MIN + (int_least8_t)res; *result = (int_least8_t)GUF_INT8_MIN + (int_least8_t)res;
} else { } else {
*result = (int_least8_t)res; *result = (int_least8_t)res;
} }
@ -2020,10 +2020,10 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_least_i16(int_least16
*result = a + b; *result = a + b;
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
*result = (a + GUF_INT16_MIN) + (b + GUF_INT16_MIN); *result = (int_least16_t)( (a + (int_least16_t)GUF_INT16_MIN) + (b + (int_least16_t)GUF_INT16_MIN) );
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
*result = (a - GUF_INT16_MIN) + (b - GUF_INT16_MIN); *result = (int_least16_t)( (a - (int_least16_t)GUF_INT16_MIN) + (b - (int_least16_t)GUF_INT16_MIN) );
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2041,11 +2041,11 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_least_i16(int_least16
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
GUF_ASSERT(b < 0); GUF_ASSERT(b < 0);
*result = (a + GUF_INT16_MIN) - (b - GUF_INT16_MIN); // TODO: not sure *result = (int_least16_t)( (a + (int_least16_t)GUF_INT16_MIN) - (b - (int_least16_t)GUF_INT16_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 - GUF_INT16_MIN) - (b + GUF_INT16_MIN); // TODO: not sure *result = (int_least16_t)( (a - (int_least16_t)GUF_INT16_MIN) - (b + (int_least16_t)GUF_INT16_MIN) ); // TODO: not sure
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2068,7 +2068,7 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_least_i16(int_least16
const uint_least16_t mod = (uint_least16_t)GUF_INT16_MAX + 1u; const uint_least16_t mod = (uint_least16_t)GUF_INT16_MAX + 1u;
GUF_ASSERT(mod > 0); GUF_ASSERT(mod > 0);
res = 1u * res % mod; res = 1u * res % mod;
*result = GUF_INT16_MIN + (int_least16_t)res; *result = (int_least16_t)GUF_INT16_MIN + (int_least16_t)res;
} else { } else {
*result = (int_least16_t)res; *result = (int_least16_t)res;
} }
@ -2151,10 +2151,10 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_least_i32(int_least32
*result = a + b; *result = a + b;
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
*result = (a + GUF_INT32_MIN) + (b + GUF_INT32_MIN); *result = (int_least32_t)( (a + (int_least32_t)GUF_INT32_MIN) + (b + (int_least32_t)GUF_INT32_MIN) );
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
*result = (a - GUF_INT32_MIN) + (b - GUF_INT32_MIN); *result = (int_least32_t)( (a - (int_least32_t)GUF_INT32_MIN) + (b - (int_least32_t)GUF_INT32_MIN) );
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2172,11 +2172,11 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_least_i32(int_least32
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
GUF_ASSERT(b < 0); GUF_ASSERT(b < 0);
*result = (a + GUF_INT32_MIN) - (b - GUF_INT32_MIN); // TODO: not sure *result = (int_least32_t)( (a + (int_least32_t)GUF_INT32_MIN) - (b - (int_least32_t)GUF_INT32_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 - GUF_INT32_MIN) - (b + GUF_INT32_MIN); // TODO: not sure *result = (int_least32_t)( (a - (int_least32_t)GUF_INT32_MIN) - (b + (int_least32_t)GUF_INT32_MIN) ); // TODO: not sure
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2199,7 +2199,7 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_least_i32(int_least32
const uint_least32_t mod = (uint_least32_t)GUF_INT32_MAX + 1u; const uint_least32_t mod = (uint_least32_t)GUF_INT32_MAX + 1u;
GUF_ASSERT(mod > 0); GUF_ASSERT(mod > 0);
res = 1u * res % mod; res = 1u * res % mod;
*result = GUF_INT32_MIN + (int_least32_t)res; *result = (int_least32_t)GUF_INT32_MIN + (int_least32_t)res;
} else { } else {
*result = (int_least32_t)res; *result = (int_least32_t)res;
} }
@ -2282,10 +2282,10 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_least_i64(int_least64
*result = a + b; *result = a + b;
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
*result = (a + GUF_INT64_MIN) + (b + GUF_INT64_MIN); *result = (int_least64_t)( (a + (int_least64_t)GUF_INT64_MIN) + (b + (int_least64_t)GUF_INT64_MIN) );
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
*result = (a - GUF_INT64_MIN) + (b - GUF_INT64_MIN); *result = (int_least64_t)( (a - (int_least64_t)GUF_INT64_MIN) + (b - (int_least64_t)GUF_INT64_MIN) );
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2303,11 +2303,11 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_least_i64(int_least64
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
GUF_ASSERT(b < 0); GUF_ASSERT(b < 0);
*result = (a + GUF_INT64_MIN) - (b - GUF_INT64_MIN); // TODO: not sure *result = (int_least64_t)( (a + (int_least64_t)GUF_INT64_MIN) - (b - (int_least64_t)GUF_INT64_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 - GUF_INT64_MIN) - (b + GUF_INT64_MIN); // TODO: not sure *result = (int_least64_t)( (a - (int_least64_t)GUF_INT64_MIN) - (b + (int_least64_t)GUF_INT64_MIN) ); // TODO: not sure
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2330,7 +2330,7 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_least_i64(int_least64
const uint_least64_t mod = (uint_least64_t)GUF_INT64_MAX + 1u; const uint_least64_t mod = (uint_least64_t)GUF_INT64_MAX + 1u;
GUF_ASSERT(mod > 0); GUF_ASSERT(mod > 0);
res = 1u * res % mod; res = 1u * res % mod;
*result = GUF_INT64_MIN + (int_least64_t)res; *result = (int_least64_t)GUF_INT64_MIN + (int_least64_t)res;
} else { } else {
*result = (int_least64_t)res; *result = (int_least64_t)res;
} }
@ -2414,10 +2414,10 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_i8(int8_t a, int8_t b
*result = a + b; *result = a + b;
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
*result = (a + INT8_MIN) + (b + INT8_MIN); *result = (int8_t)( (a + (int8_t)INT8_MIN) + (b + (int8_t)INT8_MIN) );
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
*result = (a - INT8_MIN) + (b - INT8_MIN); *result = (int8_t)( (a - (int8_t)INT8_MIN) + (b - (int8_t)INT8_MIN) );
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2435,11 +2435,11 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_i8(int8_t a, int8_t b
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
GUF_ASSERT(b < 0); GUF_ASSERT(b < 0);
*result = (a + INT8_MIN) - (b - INT8_MIN); // TODO: not sure *result = (int8_t)( (a + (int8_t)INT8_MIN) - (b - (int8_t)INT8_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 - INT8_MIN) - (b + INT8_MIN); // TODO: not sure *result = (int8_t)( (a - (int8_t)INT8_MIN) - (b + (int8_t)INT8_MIN) ); // TODO: not sure
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2462,7 +2462,7 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_i8(int8_t a, int8_t b
const uint8_t mod = (uint8_t)INT8_MAX + 1u; const uint8_t mod = (uint8_t)INT8_MAX + 1u;
GUF_ASSERT(mod > 0); GUF_ASSERT(mod > 0);
res = 1u * res % mod; res = 1u * res % mod;
*result = INT8_MIN + (int8_t)res; *result = (int8_t)INT8_MIN + (int8_t)res;
} else { } else {
*result = (int8_t)res; *result = (int8_t)res;
} }
@ -2547,10 +2547,10 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_i16(int16_t a, int16_
*result = a + b; *result = a + b;
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
*result = (a + INT16_MIN) + (b + INT16_MIN); *result = (int16_t)( (a + (int16_t)INT16_MIN) + (b + (int16_t)INT16_MIN) );
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
*result = (a - INT16_MIN) + (b - INT16_MIN); *result = (int16_t)( (a - (int16_t)INT16_MIN) + (b - (int16_t)INT16_MIN) );
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2568,11 +2568,11 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_i16(int16_t a, int16_
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
GUF_ASSERT(b < 0); GUF_ASSERT(b < 0);
*result = (a + INT16_MIN) - (b - INT16_MIN); // TODO: not sure *result = (int16_t)( (a + (int16_t)INT16_MIN) - (b - (int16_t)INT16_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 - INT16_MIN) - (b + INT16_MIN); // TODO: not sure *result = (int16_t)( (a - (int16_t)INT16_MIN) - (b + (int16_t)INT16_MIN) ); // TODO: not sure
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2595,7 +2595,7 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_i16(int16_t a, int16_
const uint16_t mod = (uint16_t)INT16_MAX + 1u; const uint16_t mod = (uint16_t)INT16_MAX + 1u;
GUF_ASSERT(mod > 0); GUF_ASSERT(mod > 0);
res = 1u * res % mod; res = 1u * res % mod;
*result = INT16_MIN + (int16_t)res; *result = (int16_t)INT16_MIN + (int16_t)res;
} else { } else {
*result = (int16_t)res; *result = (int16_t)res;
} }
@ -2680,10 +2680,10 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_i32(int32_t a, int32_
*result = a + b; *result = a + b;
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
*result = (a + INT32_MIN) + (b + INT32_MIN); *result = (int32_t)( (a + (int32_t)INT32_MIN) + (b + (int32_t)INT32_MIN) );
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
*result = (a - INT32_MIN) + (b - INT32_MIN); *result = (int32_t)( (a - (int32_t)INT32_MIN) + (b - (int32_t)INT32_MIN) );
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2701,11 +2701,11 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_i32(int32_t a, int32_
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
GUF_ASSERT(b < 0); GUF_ASSERT(b < 0);
*result = (a + INT32_MIN) - (b - INT32_MIN); // TODO: not sure *result = (int32_t)( (a + (int32_t)INT32_MIN) - (b - (int32_t)INT32_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 - INT32_MIN) - (b + INT32_MIN); // TODO: not sure *result = (int32_t)( (a - (int32_t)INT32_MIN) - (b + (int32_t)INT32_MIN) ); // TODO: not sure
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2728,7 +2728,7 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_i32(int32_t a, int32_
const uint32_t mod = (uint32_t)INT32_MAX + 1u; const uint32_t mod = (uint32_t)INT32_MAX + 1u;
GUF_ASSERT(mod > 0); GUF_ASSERT(mod > 0);
res = 1u * res % mod; res = 1u * res % mod;
*result = INT32_MIN + (int32_t)res; *result = (int32_t)INT32_MIN + (int32_t)res;
} else { } else {
*result = (int32_t)res; *result = (int32_t)res;
} }
@ -2813,10 +2813,10 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_i64(int64_t a, int64_
*result = a + b; *result = a + b;
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
*result = (a + INT64_MIN) + (b + INT64_MIN); *result = (int64_t)( (a + (int64_t)INT64_MIN) + (b + (int64_t)INT64_MIN) );
break; break;
case GUF_MATH_CKD_OVERFLOW_NEG: case GUF_MATH_CKD_OVERFLOW_NEG:
*result = (a - INT64_MIN) + (b - INT64_MIN); *result = (int64_t)( (a - (int64_t)INT64_MIN) + (b - (int64_t)INT64_MIN) );
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2834,11 +2834,11 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_i64(int64_t a, int64_
break; break;
case GUF_MATH_CKD_OVERFLOW_POS: case GUF_MATH_CKD_OVERFLOW_POS:
GUF_ASSERT(b < 0); GUF_ASSERT(b < 0);
*result = (a + INT64_MIN) - (b - INT64_MIN); // TODO: not sure *result = (int64_t)( (a + (int64_t)INT64_MIN) - (b - (int64_t)INT64_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 - INT64_MIN) - (b + INT64_MIN); // TODO: not sure *result = (int64_t)( (a - (int64_t)INT64_MIN) - (b + (int64_t)INT64_MIN) ); // TODO: not sure
break; break;
default: default:
GUF_ASSERT(false); GUF_ASSERT(false);
@ -2861,7 +2861,7 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_i64(int64_t a, int64_
const uint64_t mod = (uint64_t)INT64_MAX + 1u; const uint64_t mod = (uint64_t)INT64_MAX + 1u;
GUF_ASSERT(mod > 0); GUF_ASSERT(mod > 0);
res = 1u * res % mod; res = 1u * res % mod;
*result = INT64_MIN + (int64_t)res; *result = (int64_t)INT64_MIN + (int64_t)res;
} else { } else {
*result = (int64_t)res; *result = (int64_t)res;
} }
@ -3957,3 +3957,4 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_u64(uint64_t a, uint6
#undef GUF_MATH_CKDINT_KWRDS #undef GUF_MATH_CKDINT_KWRDS
#undef GUF_MATH_CKDINT_IMPL #undef GUF_MATH_CKDINT_IMPL
#undef GUF_MATH_CKDINT_IMPL_STATIC #undef GUF_MATH_CKDINT_IMPL_STATIC

View File

@ -345,7 +345,7 @@ GUF_RAND_KWRDS uint_least64_t guf_rand32_u64(guf_rand32_state *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 uint_least32_t lower_bits = guf_rand32_u32(state); const uint_least32_t lower_bits = guf_rand32_u32(state);
const uint_least32_t upper_bits = guf_rand32_u32(state); const uint_least32_t upper_bits = guf_rand32_u32(state);
GUF_ASSERT( lower_bits <= GUF_UINT32_MAX && upper_bits <= GUF_UINT32_MAX ); //GUF_ASSERT( lower_bits <= GUF_UINT32_MAX && upper_bits <= GUF_UINT32_MAX );
GUF_ASSERT( ( ((uint_least64_t)upper_bits << 32u) | (uint_least64_t)lower_bits ) <= GUF_UINT32_MAX); GUF_ASSERT( ( ((uint_least64_t)upper_bits << 32u) | (uint_least64_t)lower_bits ) <= GUF_UINT32_MAX);
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...
@ -443,31 +443,31 @@ GUF_RAND_KWRDS void guf_randstate_jump(guf_randstate *state)
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)
} }
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)
} }
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)
} }
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)
@ -684,7 +684,7 @@ GUF_RAND_KWRDS int_least32_t guf_rand32_range_i32(guf_rand32_state *state, int_l
const int_least32_t rnd = min + (int_least32_t)step; const int_least32_t rnd = min + (int_least32_t)step;
GUF_ASSERT(rnd >= min && rnd <= max); GUF_ASSERT(rnd >= min && rnd <= max);
GUF_ASSERT(rnd <= GUF_INT32_MAX && rnd >= GUF_INT32_MIN); //GUF_ASSERT(rnd <= GUF_INT32_MAX && rnd >= GUF_INT32_MIN);
return rnd; return rnd;
} }

View File

@ -77,7 +77,7 @@ typedef struct guf_str_tok_state {
#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.lng.size = 0, .data.lng.capacity = 0, .data.lng.c_str = NULL} #define GUF_STR_UNINITIALISED_CPP guf_str{{0, 0, NULL}, 0}
#endif #endif
// 1.) guf_str_view: // 1.) guf_str_view:
@ -1200,7 +1200,7 @@ GUF_STR_KWRDS guf_str *guf_str_append_u64(guf_str *str, uint_least64_t n) {
int num_digits = 0; int num_digits = 0;
do { do {
GUF_ASSERT(start_idx > 0); GUF_ASSERT(start_idx > 0);
char c = (n % 10) + '0'; char c = (char)( (n % 10) + '0' );
buf[--start_idx] = c; buf[--start_idx] = c;
++num_digits; ++num_digits;
} while ((n = n / 10)); } while ((n = n / 10));

View File

@ -113,15 +113,15 @@ GUF_UTF8_KWRDS bool guf_utf8_encode(guf_utf8_char *result, uint_least32_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,7 +131,7 @@ GUF_UTF8_KWRDS bool guf_utf8_encode(guf_utf8_char *result, uint_least32_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;
@ -288,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;
} }
} }

BIN
libguf/src/.DS_Store vendored

Binary file not shown.

1941
libguf/test/data/bartleby.txt Executable file

File diff suppressed because it is too large Load Diff

67
libguf/test/data/utf8-test.txt Executable file
View File

@ -0,0 +1,67 @@
„Ich weiß nicht“, rief ich ohne Klang „ich weiß ja nicht. Wenn
niemand kommt, dann kommt eben niemand. Ich habe niemandem etwas
Böses getan, niemand hat mir etwas Böses getan, niemand aber will
mir helfen. Lauter niemand. Aber so ist es doch nicht. Nur daß mir
niemand hilft —, sonst wäre lauter niemand hübsch. Ich würde ganz
gern — warum denn nicht — einen Ausflug mit einer Gesellschaft von
lauter Niemand machen. Natürlich ins Gebirge, wohin denn sonst? Wie
sich diese Niemand aneinander drängen, diese vielen quer gestreckten
und eingehängten Arme, diese vielen Füße, durch winzige Schritte
getrennt! Versteht sich, daß alle in Frack sind. Wir gehen so lala,
der Wind fährt durch die Lücken, die wir und unsere Gliedmaßen offen
lassen. Die Hälse werden im Gebirge frei! Es ist ein Wunder, daß
wir nicht singen.“
Det var i den Tid, jeg gik omkring og sulted i Kristiania, denne forunderlige By,
som ingen forlader, før han har fået Mærker af den . . . .
Jeg ligger vågen på min Kvist og hører en Klokke nedenunder mig slå seks Slag; det var allerede ganske lyst,
og Folk begyndte at færdes op og ned i Trapperne. Nede ved Døren, hvor mit Rum var tapetseret med gamle Numre
af »Morgenbladet«, kunde jeg så tydelig se en Bekendtgørelse fra Fyrdirektøren, og lidt tilvenstre derfra et fedt,
bugnende Avertissement fra Bager Fabian Olsen om nybagt Brød.
The quick brown fox jumps over the lazy dog.
Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Wolther spillede på xylofon.
Falsches Üben von Xylophonmusik quält jeden größeren Zwerg.
Ξεσκεπάζω τὴν ψυχοφθόρα βδελυγμία.
El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y frío, añoraba a su querido cachorro.
Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en
canoë au delà des îles, près du mälström où brûlent les novæ.
D'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh.
Árvíztűrő tükörfúrógép.
Pchnąć w tę łódź jeża lub ośm skrzyń fig.
Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa.
В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!
Pijamalı hasta, yağız şoföre çabucak güvendi.
Albert osti fagotin ja töräytti puhkuvan melodian.
דג סקרן שט בים מאוכזב ולפתע מצא חברה
نص حكيم له سر قاطع وذو شأن عظيم مكتوب على ثوب أخضر ومغلف بجلد أزرق
بر اثر چنین تلقین و شستشوی مغزی جامعی، سطح و پایهٔ ذهن و فهم و نظر بعضی اشخاص واژگونه و معکوس می‌شود
키스의 고유조건은 입술끼리 만나야 하고 특별한 기술은 필요치 않다.
いろはにほへとちりぬるを
わかよたれそつねならむ
うゐのおくやまけふこえて
あさきゆめみしゑひもせす
イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム
ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン
ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ
ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ
ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬

320
libguf/test/example.c Executable file
View File

@ -0,0 +1,320 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "guf_init.h" /* Must be included once (or compiled in a separate .c file and linked) */
#define GUF_ALLOC_LIBC_IMPL_STATIC
#include "guf_alloc_libc.h"
#include "guf_cstr.h"
#include "guf_linalg.h"
#include "guf_utils.h"
#define GUF_T float
#define GUF_SORT_IMPL_STATIC
#include "guf_sort.h"
#define GUF_T int
#define GUF_SORT_IMPL_STATIC
#include "guf_sort.h"
#define GUF_DBUF_NAME dbuf_int
#define GUF_T int
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_IMPL_STATIC
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_float
#define GUF_T float
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_IMPL_STATIC
#include "guf_dbuf.h"
#define GUF_T guf_cstr_heap
#define GUF_DBUF_NAME dbuf_heap_cstr
#define GUF_T_COPY guf_cstr_heap_copy
#define GUF_T_MOVE guf_cstr_heap_move
#define GUF_T_FREE guf_cstr_heap_free
#define GUF_T_EQ guf_cstr_heap_eq
#define GUF_DBUF_IMPL_STATIC
// #define GUF_CNT_WITH_ELEM_CTX
#include "guf_dbuf.h"
#define GUF_T guf_cstr_const
#define GUF_DBUF_NAME dbuf_const_cstr
#define GUF_T_EQ guf_cstr_const_eq
#define GUF_DBUF_IMPL_STATIC
#include "guf_dbuf.h"
#define GUF_RAND_IMPL_STATIC
// #define GUF_RAND_32_BIT
#include "guf_rand.h"
#include "impls/dict_impl.h"
int main(void)
{
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
guf_alloc_tracker_init(&allocator_ctx.tracker, 1, "example_allocator", NULL, NULL);
allocator_ctx.zero_init = false;
guf_libc_allocator_init(&allocator, &allocator_ctx);
printf("libguf example: " GUF_PLATFORM_STRING "\n");
guf_platform_assert_endianness();
guf_platform_assert_native_word_bits();
guf_allocator zero_init_allocator;
guf_libc_alloc_ctx zero_init_allocator_ctx;
guf_alloc_tracker_init(&zero_init_allocator_ctx.tracker, 2, "example_zero_init_allocator", stdout, stderr);
zero_init_allocator_ctx.zero_init = true;
guf_libc_allocator_init(&zero_init_allocator, &zero_init_allocator_ctx);
dict_cstr_int ht;
dict_cstr_int_init(&ht, &zero_init_allocator);
dict_cstr_int_insert_val_arg(&ht, "Hello", 42, GUF_CPY_VALUE, GUF_CPY_VALUE);
dict_cstr_int_insert_val_arg(&ht, "World", 64, GUF_CPY_VALUE, GUF_CPY_VALUE);
int kv_iter = 0;
GUF_CNT_FOREACH(&ht, dict_cstr_int, kv_it) {
printf("%d: %s -> %d\n", kv_iter++, kv_it.ptr->key, kv_it.ptr->val);
}
guf_cstr_const key = "World";
int *res = dict_cstr_int_at_val_arg(&ht, "World");
if (res) {
printf("%s: %d\n", key, *res);
} else {
printf("key '%s' not found\n", key);
}
GUF_ASSERT(dict_cstr_int_at_val_arg(&ht, "World"));
GUF_ASSERT(dict_cstr_int_at_val_arg(&ht, "Hello"));
GUF_ASSERT(dict_cstr_int_at_val_arg(&ht, "hello") == NULL);
GUF_ASSERT(dict_cstr_int_at_val_arg(&ht, "") == NULL);
GUF_ASSERT(dict_cstr_int_contains_val_arg(&ht, "World"));
GUF_ASSERT(dict_cstr_int_contains_val_arg(&ht, "Hello"));
const int ht_needle_val = 64;
const dict_cstr_int_iter ht_it = dict_cstr_int_find_val(&ht, dict_cstr_int_begin(&ht), dict_cstr_int_end(&ht), &ht_needle_val);
if (!dict_cstr_int_iter_is_end(&ht, ht_it)) {
printf("found value %d (key %s)\n", ht_needle_val, ht_it.ptr->key);
}
dict_cstr_int_free(&ht, NULL);
GUF_CNT_LIFETIME_BLOCK(dbuf_float, floats, {
floats = dbuf_float_new(&allocator);
for (int i = 0; i <= 16; ++i) {
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 *res = float_arr_merge_sort(floats.data, tmp, floats.size, GUF_SORT_ASCENDING, NULL);
// test_allocator.free(tmp, floats.size * sizeof(float), &test_allocator_ctx);
// GUF_ASSERT_RELEASE(res == floats.data);
float_arr_qsort(floats.data, floats.size, GUF_SORT_ASCENDING, NULL);
GUF_ASSERT_RELEASE(float_arr_is_sorted(floats.data, floats.size, GUF_SORT_ASCENDING, NULL));
GUF_CNT_FOREACH(&floats, dbuf_float, it) {
printf("float: %f\n", (double)*it.ptr);
}
})
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, "Bar 2");
char *move_me = guf_cstr_dup("Baz 3");
dbuf_heap_cstr_push(&strings, &move_me, GUF_CPY_MOVE);
GUF_ASSERT_RELEASE(move_me == NULL);
dbuf_heap_cstr_push_val_cpy(&strings, "Boz 4");
char *findme = "Baz 3";
dbuf_heap_cstr_iter beg = dbuf_heap_cstr_begin(&strings);
dbuf_heap_cstr_iter end = dbuf_heap_cstr_end(&strings);
dbuf_heap_cstr_iter fnd_it = dbuf_heap_cstr_find(&strings, beg, end, &findme);
if (!dbuf_heap_cstr_iter_is_end(&strings, fnd_it)) {
printf("%s found in range [%td, %td) at idx %td\n", findme, dbuf_heap_cstr_iter_to_idx(&strings, beg), dbuf_heap_cstr_iter_to_idx(&strings, end), dbuf_heap_cstr_iter_to_idx(&strings, fnd_it));
} else {
printf("%s not found in range [%td, %td) at idx %td\n", findme, dbuf_heap_cstr_iter_to_idx(&strings, beg), dbuf_heap_cstr_iter_to_idx(&strings, end), dbuf_heap_cstr_iter_to_idx(&strings, fnd_it));
}
if (dbuf_heap_cstr_contains_val(&strings, "Baz 3")) {
printf("contains\n");
} else {
printf("does not contain\n");
}
GUF_CNT_FOREACH(&strings, dbuf_heap_cstr, it) {
printf("%s\n", *it.ptr);
}
dbuf_heap_cstr_free(&strings, NULL);
dbuf_const_cstr const_strings = dbuf_const_cstr_new(&allocator);
dbuf_const_cstr_push_val(&const_strings, "Const 1");
dbuf_const_cstr_push_val(&const_strings, "Const 2");
const char *foo = "Const 3";
dbuf_const_cstr_push(&const_strings, &foo, GUF_CPY_VALUE);
dbuf_const_cstr_iter found_it = dbuf_const_cstr_find(&const_strings, dbuf_const_cstr_begin(&const_strings), dbuf_const_cstr_end(&const_strings), &foo);
if (found_it.ptr != dbuf_const_cstr_end(&const_strings).ptr) {
*found_it.ptr = "Found!";
}
GUF_CNT_FOREACH(&const_strings, dbuf_const_cstr, it) {
printf("%s\n", *it.ptr);
}
dbuf_const_cstr_free(&const_strings, NULL);
dbuf_int integers = dbuf_int_new(&allocator);
dbuf_int_push_val(&integers, 420);
dbuf_int_push_val(&integers, 520);
dbuf_int_push_val(&integers, 620);
dbuf_int_push_val(&integers, 720);
dbuf_int_push_val(&integers, 820);
guf_err err;
dbuf_int_try_at(&integers, 16, &err);
if (err) {
printf("%s %s\n", guf_err_to_str(err), GUF_ERR_MSG_EMPTY());
}
int i = 0;
GUF_DBUF_FOREACH(integers, int, elem) {
printf("elem %d: %d\n", i, *elem);
++i;
}
GUF_CNT_FOREACH(&integers, dbuf_int, it) {
printf("it-elem: %d", *it.ptr);
if (dbuf_int_iter_next(&integers, it, 1).ptr != dbuf_int_end(&integers).ptr) {
printf(", it-next: %d", *dbuf_int_iter_next(&integers, it, 1).ptr);
}
if (dbuf_int_iter_next(&integers, it, -1).ptr != dbuf_int_end(&integers).ptr) {
printf(", it-prev: %d", *dbuf_int_iter_next(&integers, it, -1).ptr);
}
printf("\n");
}
for (dbuf_int_iter it = dbuf_int_begin(&integers); it.ptr != dbuf_int_end(&integers).ptr; it = dbuf_int_iter_next(&integers, it, 2)) {
printf("every other: %d\n", *it.ptr);
}
for (dbuf_int_iter it = dbuf_int_rbegin(&integers); it.ptr != dbuf_int_rend(&integers).ptr; it = dbuf_int_iter_next(&integers, it, 1)) {
printf("reverse: %d\n", *it.ptr);
}
for (dbuf_int_iter it = dbuf_int_rbegin(&integers); it.ptr != dbuf_int_rend(&integers).ptr; it = dbuf_int_iter_next(&integers, it, 2)) {
printf("every other reverse: %d (idx %td)\n", *it.ptr, dbuf_int_iter_to_idx(&integers, it));
}
dbuf_int_free(&integers, NULL);
printf("\n");
guf_randstate rng;
guf_randstate_init(&rng, (guf_rand_seed_t)time(NULL));
int heads = 0, tails = 0;
int throws = 10;
for (i = 0; i < throws; ++i) {
bool is_head = guf_rand_flip(&rng);
if (is_head) {
puts("head");
++heads;
} else {
puts("tail");
++tails;
}
}
printf("n: %d\nheads: %d\ntails: %d\n", throws, heads, tails);
int result[256];
memset(result, 0, sizeof result);
for (int n = 0; n < 32000; ++n) {
float r = roundf(guf_rand_normal_sample_one_f32(&rng, 100, 15));
r = guf_clamp_f32(r, 0, 255);
result[(int)r] += 1;
}
for (size_t n = 60; n <= 140; ++n) {
printf("%zu:\t", n);
for (int j = 0; j < result[n] / 8; ++j) {
putc('#', stdout);
}
puts("");
}
for (float angle = 0; angle <= 8.f * GUF_PI_F32; angle += 0.001f) {
guf_quaternion rotq = guf_quaternion_from_axis_angle(angle, guf_vec3_normalised((guf_vec3){-2324234.3f, 1.4f, -1.3f}));
guf_mat4x4 rotmat, rotmat_inv;
guf_mat4x4_init_from_quaternion(&rotmat, rotq);
GUF_ASSERT_RELEASE(guf_mat4x4_inverted(&rotmat, &rotmat_inv))
guf_mat4x4_set_trans(&rotmat, (guf_vec3){42.1234f, -512.2f, 3.1415926f});
GUF_ASSERT_RELEASE(guf_mat4x4_inverted(&rotmat, &rotmat_inv));
GUF_ASSERT_RELEASE(guf_mat4x4_inverted(&rotmat_inv, &rotmat));
GUF_ASSERT_RELEASE(guf_mat4x4_inverted(&rotmat, &rotmat_inv));
}
guf_quaternion q = guf_quaternion_from_axis_angle(GUF_PI_F32 / 8.f, guf_vec3_normalised((guf_vec3){0.3f, 10.2f, -25.f}));
guf_mat4x4 mat;
guf_mat4x4_init_from_quaternion(&mat, q);
guf_mat4x4_set_trans(&mat, (guf_vec3){42.1234f, -512.2f, 3.1415926f});
mat.data[2][0] *= 100000.4f;
const guf_mat4x4 mat_cpy = mat;
printf("Matrix:\n");
guf_mat4x4_print_with_precision(&mat, stdout, 8);
guf_mat4x4 mat_inv;
bool invertible = guf_mat4x4_inverted(&mat, &mat_inv);
if (!invertible) {
printf("Not invertible\n");
} else {
printf("Inverse:\n");
guf_mat4x4_print_with_precision(&mat_inv, stdout, 8);
GUF_ASSERT_RELEASE(guf_mat4x4_inverted(&mat_inv, &mat));
GUF_ASSERT_RELEASE(guf_mat4x4_nearly_equal(&mat, &mat_cpy, 1e-4f, 1e-5f));
}
mat = (guf_mat4x4) {.data = {
{1, 1.3f, 1, 1},
{2, 2.6f, 2, 2},
{0, 0, 2, 4},
{0, 0, 0, 1}
}};
printf("Matrix 2:\n");
guf_mat4x4_print_with_precision(&mat, stdout, 8);
invertible = guf_mat4x4_inverted(&mat, &mat_inv);
if (!invertible) {
printf("Not invertible\n");
} else {
printf("Inverse:\n");
guf_mat4x4_print_with_precision(&mat_inv, stdout, 8);
}
bool leak = false;
if (guf_alloc_tracker_found_leak(&allocator_ctx.tracker)) {
printf("Found memory leak:\n");
guf_alloc_tracker_print(&allocator_ctx.tracker, stderr);
leak = true;
}
if (guf_alloc_tracker_found_leak(&zero_init_allocator_ctx.tracker)) {
printf("Found memory leak:\n");
guf_alloc_tracker_print(&zero_init_allocator_ctx.tracker, stderr);
leak = true;
}
return leak ? EXIT_FAILURE: EXIT_SUCCESS;
}

View File

@ -0,0 +1,2 @@
#define GUF_ALLOC_LIBC_IMPL
#include "guf_alloc_libc.h"

View File

@ -0,0 +1,2 @@
#define GUF_ALLOC_TRACKER_IMPL
#include "guf_alloc_tracker.h"

View File

@ -0,0 +1,2 @@
#define GUF_MATH_CKDINT_IMPL
#include "guf_math_ckdint.h"

55
libguf/test/impls/dbuf_impl.c Executable file
View File

@ -0,0 +1,55 @@
#include "dbuf_impl.h"
#define GUF_DBUF_NAME dbuf_int
#define GUF_T int
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_i32
#define GUF_T int32_t
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_char
#define GUF_T char
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_float
#define GUF_T float
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_T guf_cstr_heap
#define GUF_DBUF_NAME dbuf_heap_cstr
#define GUF_T_COPY guf_cstr_heap_copy
#define GUF_T_MOVE guf_cstr_heap_move
#define GUF_T_FREE guf_cstr_heap_free
#define GUF_T_EQ guf_cstr_heap_eq
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_T guf_cstr_const
#define GUF_DBUF_NAME dbuf_const_cstr
#define GUF_T_EQ guf_cstr_const_eq
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_T guf_str_view
#define GUF_DBUF_NAME dbuf_str_view
#define GUF_T_EQ guf_str_view_equal
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"
#define GUF_T guf_str
#define GUF_DBUF_NAME dbuf_str
#define GUF_T_COPY guf_str_copy
#define GUF_T_MOVE guf_str_move
#define GUF_T_FREE guf_str_free
#define GUF_T_EQ guf_str_equal
#define GUF_DBUF_IMPL
#include "guf_dbuf.h"

53
libguf/test/impls/dbuf_impl.h Executable file
View File

@ -0,0 +1,53 @@
#ifndef GUF_DBUF_IMPL_H
#define GUF_DBUF_IMPL_H
#include "guf_cstr.h"
#include "guf_str.h"
#define GUF_DBUF_NAME dbuf_int
#define GUF_T int
#define GUF_T_IS_INTEGRAL_TYPE
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_i32
#define GUF_T int32_t
#define GUF_T_IS_INTEGRAL_TYPE
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_char
#define GUF_T char
#define GUF_T_IS_INTEGRAL_TYPE
#include "guf_dbuf.h"
#define GUF_DBUF_NAME dbuf_float
#define GUF_T float
#define GUF_T_IS_INTEGRAL_TYPE
#include "guf_dbuf.h"
#define GUF_T guf_cstr_heap
#define GUF_DBUF_NAME dbuf_heap_cstr
#define GUF_T_COPY guf_cstr_heap_copy
#define GUF_T_MOVE guf_cstr_heap_move
#define GUF_T_FREE guf_cstr_heap_free
#define GUF_T_EQ guf_cstr_heap_eq
#include "guf_dbuf.h"
#define GUF_T guf_cstr_const
#define GUF_DBUF_NAME dbuf_const_cstr
#define GUF_T_EQ guf_cstr_const_eq
#include "guf_dbuf.h"
#define GUF_T guf_str_view
#define GUF_DBUF_NAME dbuf_str_view
#define GUF_T_EQ guf_str_view_equal
#include "guf_dbuf.h"
#define GUF_T guf_str
#define GUF_DBUF_NAME dbuf_str
#define GUF_T_COPY guf_str_copy
#define GUF_T_MOVE guf_str_move
#define GUF_T_FREE guf_str_free
#define GUF_T_EQ guf_str_equal
#include "guf_dbuf.h"
#endif

47
libguf/test/impls/dict_impl.c Executable file
View File

@ -0,0 +1,47 @@
#include "dict_impl.h"
#define GUF_DICT_KEY_T guf_cstr_const
#define GUF_DICT_KEY_T_EQ guf_cstr_const_eq
#define GUF_DICT_KEY_HASH guf_cstr_const_hash
#define GUF_DICT_VAL_T int
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_cstr_int
#define GUF_DICT_IMPL
#include "guf_dict.h"
#define GUF_DICT_KEY_T guf_str_view
#define GUF_DICT_KEY_HASH guf_str_view_hash
#define GUF_DICT_KEY_T_EQ guf_str_view_equal
#define GUF_DICT_VAL_T int32_t
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_sv_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 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_HASH int32_hash
#define GUF_DICT_KEY_T_EQ int32_eq
#define GUF_DICT_VAL_T bool
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_i32_bool
#define GUF_DICT_IMPL
#include "guf_dict.h"

61
libguf/test/impls/dict_impl.h Executable file
View File

@ -0,0 +1,61 @@
#ifndef GUF_DICT_IMPL_H
#define GUF_DICT_IMPL_H
#include "guf_common.h"
#include "guf_cstr.h"
#include "guf_str.h"
#include "guf_hash.h"
#define GUF_DICT_KEY_T guf_cstr_const
#define GUF_DICT_KEY_HASH guf_cstr_const_hash
#define GUF_DICT_KEY_T_EQ guf_cstr_const_eq
#define GUF_DICT_VAL_T int
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_cstr_int
#include "guf_dict.h"
#define GUF_DICT_KEY_T guf_str_view
#define GUF_DICT_KEY_HASH guf_str_view_hash
#define GUF_DICT_KEY_T_EQ guf_str_view_equal
#define GUF_DICT_VAL_T int32_t
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_sv_i32
// #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 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)
{
return guf_hash(a, sizeof(int32_t), GUF_HASH_INIT); // TODO: byte order...
}
static inline bool int32_eq(const int32_t *a, const int32_t *b)
{
return *a == *b;
}
#define GUF_DICT_KEY_T int32_t
#define GUF_DICT_KEY_HASH int32_hash
#define GUF_DICT_KEY_T_EQ int32_eq
#define GUF_DICT_VAL_T bool
#define GUF_DICT_VAL_T_IS_INTEGRAL_TYPE
#define GUF_DICT_NAME dict_i32_bool
#include "guf_dict.h"
#endif

1
libguf/test/impls/init_impl.c Executable file
View File

@ -0,0 +1 @@
#include "guf_init.h"

View File

@ -0,0 +1,2 @@
#define GUF_LINALG_IMPL
#include "guf_linalg.h"

2
libguf/test/impls/rand_impl.c Executable file
View File

@ -0,0 +1,2 @@
#define GUF_RAND_IMPL
#include "guf_rand.h"

17
libguf/test/impls/sort_impl.c Executable file
View File

@ -0,0 +1,17 @@
#include "sort_impl.h"
#define GUF_T float
#define GUF_SORT_IMPL
#include "guf_sort.h"
#define GUF_T int32_t
#define GUF_SORT_IMPL
#include "guf_sort.h"
#define GUF_T int8_t
#define GUF_SORT_IMPL
#include "guf_sort.h"
#define GUF_T guf_cstr_heap
#define GUF_SORT_IMPL
#include "guf_sort.h"

18
libguf/test/impls/sort_impl.h Executable file
View File

@ -0,0 +1,18 @@
#ifndef GUF_SORT_IMPL_H
#define GUF_SORT_IMPL_H
#include "guf_cstr.h"
#define GUF_T float
#include "guf_sort.h"
#define GUF_T int32_t
#include "guf_sort.h"
#define GUF_T int8_t
#include "guf_sort.h"
#define GUF_T guf_cstr_heap
#include "guf_sort.h"
#endif

2
libguf/test/impls/str_impl.c Executable file
View File

@ -0,0 +1,2 @@
#define GUF_STR_IMPL
#include "guf_str.h"

64
libguf/test/test.cpp Executable file
View File

@ -0,0 +1,64 @@
#include <vector>
#include <string>
#include <cstdio>
#include <iostream>
#include "test_dbuf.hpp"
#include "test_dict.hpp"
#include "test_utf8.hpp"
#include "test_str.hpp"
#include "test_ckdint.hpp"
extern "C"
{
#include "guf_assert.h"
#include "guf_math.h"
}
static std::vector<std::unique_ptr<Test>> g_tests {};
static void init_tests()
{
g_tests.push_back(std::make_unique<DbufIntTest>("DbufIntTest"));
g_tests.push_back(std::make_unique<DbufCstringTest>("DbufCstringTest"));
g_tests.push_back(std::make_unique<DbufStrTest>("DbufStrTest"));
g_tests.push_back(std::make_unique<DictSvToIntTest>("DictSvToIntTest"));
g_tests.push_back(std::make_unique<UTF8Test>("UTF8Test"));
g_tests.push_back(std::make_unique<StrTest>("StrTest"));
g_tests.push_back(std::make_unique<CkdIntTest>("CkdIntTest"));
}
int main()
{
init_tests();
std::cout << "Running " << g_tests.size() << " tests...\n";
// std::cout << "max cap 1:" << dict_sv_i32_max_capacity() << "\n";
// std::cout << "max cap 2:" << dict_cstr_int_max_capacity() << "\n";
size_t num_passed = 0;
for (auto &test : g_tests) {
Test *tst = test.get();
GUF_ASSERT_RELEASE(tst);
tst->before_run();
tst->run();
tst->after_run();
std::cout << "- " << *tst << "\n";
if (tst->passed) {
++num_passed;
}
}
const bool passed_all = (num_passed == g_tests.size());
GUF_ASSERT_RELEASE(num_passed <= g_tests.size());
if (passed_all) {
std::cout << "-> SUCCESS: Passed all (" << num_passed << "/" << g_tests.size() << ") tests.\n";
} else {
std::cout << "-> FAILURE: Failed " << (g_tests.size() - num_passed) << "/" << g_tests.size() << " tests.\n";
}
return passed_all ? EXIT_SUCCESS : EXIT_FAILURE;
}

108
libguf/test/test.hpp Executable file
View File

@ -0,0 +1,108 @@
#ifndef GUF_TEST_HPP
#define GUF_TEST_HPP
#include <stack>
#include <string>
#include <cstdio>
#include <iostream>
#include <chrono>
#include <iomanip>
extern "C" {
#include "guf_common.h"
#include "guf_assert.h"
}
#define TEST_CHECK(COND) (check((COND), GUF_STRINGIFY(COND), __LINE__, __FILE__))
struct Test
{
private:
std::chrono::steady_clock::time_point time_start, time_end;
protected:
std::stack<std::string> check_name_stack;
std::string full_check_name = "";
void push_check_name(const std::string& check_name)
{
check_name_stack.push(check_name);
full_check_name = full_check_name + "::" + check_name;
}
void pop_check_name()
{
const size_t sep_idx = full_check_name.rfind("::");
GUF_ASSERT_RELEASE(sep_idx != std::string::npos);
full_check_name = full_check_name.substr(0, sep_idx);
check_name_stack.pop();
}
bool check(bool cond, std::string_view msg, size_t line, std::string_view fname)
{
if (!cond) {
std::cerr << name << full_check_name << ": ";
std::cerr << "FAILED CHECK (" << msg << ") on line " << line << " in file " << fname << "\n"; \
++num_failed_checks;
} else {
++num_passed_checks;
}
return cond;
}
public:
const std::string name {};
std::chrono::duration<float, std::milli> runtime_ms {0};
bool passed {false}, done {false};
size_t num_failed_checks {0}, num_passed_checks {0};
Test(const std::string& nm) : name{nm} {}
virtual ~Test() = default;
size_t total_checks() const
{
return num_passed_checks + num_failed_checks;
}
virtual void run() = 0;
void before_run()
{
time_start = std::chrono::steady_clock::now();
}
void after_run()
{
done = true;
passed = (num_failed_checks == 0);
time_end = std::chrono::steady_clock::now();
runtime_ms = std::chrono::duration_cast<decltype(runtime_ms)>(time_end - time_start);
}
friend std::ostream& operator<<(std::ostream &os, const Test& tst)
{
std::ios_base::fmtflags os_flags = os.flags();
std::streamsize os_precision = os.precision();
os << tst.name << ": " << (tst.passed ? "PASS" : "FAIL");
os << std::fixed << std::setprecision(2);
os << " (" << tst.num_passed_checks << "/" << tst.total_checks() << ") in " << tst.runtime_ms.count() << " ms";
os.precision(os_precision);
os.flags(os_flags);
return os;
}
};
template<>
struct std::hash<std::unique_ptr<Test>> {
std::size_t operator()(const std::unique_ptr<Test>& test) const
{
if (test.get() == nullptr) {
return 0;
}
return std::hash<std::string>()(test.get()->name);
}
};
#endif

361
libguf/test/test_ckdint.cpp Executable file
View File

@ -0,0 +1,361 @@
#include "test_ckdint.hpp"
extern "C"
{
#include "guf_math_ckdint.h"
}
/*
CkdIntTest:
*/
void CkdIntTest::run()
{
push_check_name("test_ckd");
test_ckd();
pop_check_name();
push_check_name("test_ckd_uint");
test_ckd_uint();
pop_check_name();
}
void CkdIntTest::test_ckd()
{
for (int32_t a = INT8_MIN; a <= INT8_MAX; ++a) {
for (int32_t b = INT8_MIN; b <= INT8_MAX; ++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);
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) {
TEST_CHECK(ckd_add == GUF_MATH_CKD_OVERFLOW_POS);
int8_t saturated, saturated2;
TEST_CHECK(guf_saturating_add_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == INT8_MAX);
TEST_CHECK(guf_saturating_add_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &saturated2) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == saturated2);
int8_t wrapped, wrapped2;
TEST_CHECK(guf_wrapping_add_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(static_cast<int32_t>(wrapped) == INT8_MIN + (add_res % (INT8_MAX + 1)));
TEST_CHECK(guf_wrapping_add_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &wrapped2) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(wrapped == wrapped2);
}
else if (add_res < INT8_MIN) {
TEST_CHECK(ckd_add == GUF_MATH_CKD_OVERFLOW_NEG);
int8_t saturated, saturated2;
TEST_CHECK(guf_saturating_add_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(saturated == INT8_MIN);
TEST_CHECK(guf_saturating_add_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &saturated2) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(saturated == saturated2);
int8_t wrapped, wrapped2;
TEST_CHECK(guf_wrapping_add_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(static_cast<int32_t>(wrapped) == INT8_MAX - (-add_res % (-INT8_MIN + 1)));
TEST_CHECK(guf_wrapping_add_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &wrapped2) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(wrapped == wrapped2);
}
else {
TEST_CHECK(ckd_add == GUF_MATH_CKD_SUCCESS);
int8_t saturated, saturated2;
TEST_CHECK(guf_saturating_add_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(saturated) == add_res);
TEST_CHECK(guf_saturating_add_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &saturated2) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(saturated == saturated2);
int8_t wrapped, wrapped2;
TEST_CHECK(guf_wrapping_add_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(wrapped) == add_res);
TEST_CHECK(guf_wrapping_add_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &wrapped2) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(wrapped == wrapped2);
}
const int32_t sub_res = a - 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) {
TEST_CHECK(ckd_sub == GUF_MATH_CKD_OVERFLOW_POS);
int8_t saturated;
TEST_CHECK(guf_saturating_sub_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == INT8_MAX);
int8_t wrapped;
TEST_CHECK(guf_wrapping_sub_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(static_cast<int32_t>(wrapped) == INT8_MIN + (sub_res % (INT8_MAX + 1)));
} else if (sub_res < INT8_MIN) {
TEST_CHECK(ckd_sub == GUF_MATH_CKD_OVERFLOW_NEG);
int8_t saturated;
TEST_CHECK(guf_saturating_sub_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(saturated == INT8_MIN);
int8_t wrapped;
TEST_CHECK(guf_wrapping_sub_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(static_cast<int32_t>(wrapped) == INT8_MAX - (-sub_res % (-INT8_MIN + 1)));
} else {
TEST_CHECK(ckd_sub == GUF_MATH_CKD_SUCCESS);
int8_t saturated;
TEST_CHECK(guf_saturating_sub_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(saturated) == sub_res);
int8_t wrapped;
TEST_CHECK(guf_wrapping_sub_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(wrapped) == sub_res);
}
const int32_t mul_res = a * 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));
if (mul_res > INT8_MAX) {
TEST_CHECK(ckd_mul == GUF_MATH_CKD_OVERFLOW_POS);
int8_t saturated, saturated2;
TEST_CHECK(guf_saturating_mul_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == INT8_MAX);
TEST_CHECK(guf_saturating_mul_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &saturated2) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == saturated2);
int8_t wrapped, wrapped2;
TEST_CHECK(guf_wrapping_mul_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(guf_wrapping_mul_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &wrapped2) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(wrapped == wrapped2);
// TODO: check wrapped
} else if (mul_res < INT8_MIN) {
TEST_CHECK(ckd_mul == GUF_MATH_CKD_OVERFLOW_NEG);
int8_t saturated, saturated2;
TEST_CHECK(guf_saturating_mul_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(saturated == INT8_MIN);
TEST_CHECK(guf_saturating_mul_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &saturated2) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(saturated == saturated2);
int8_t wrapped, wrapped2;
TEST_CHECK(guf_wrapping_mul_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(guf_wrapping_mul_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &wrapped2) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(wrapped == wrapped2);
// TODO: check wrapped
} else {
TEST_CHECK(ckd_mul == GUF_MATH_CKD_SUCCESS);
int8_t saturated, saturated2;
TEST_CHECK(guf_saturating_mul_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &saturated) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(saturated) == mul_res);
TEST_CHECK(guf_saturating_mul_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &saturated2) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(saturated == saturated2);
int8_t wrapped, wrapped2;
TEST_CHECK(guf_wrapping_mul_i8(static_cast<int8_t>(a), static_cast<int8_t>(b), &wrapped) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(wrapped) == mul_res);
TEST_CHECK(guf_wrapping_mul_i8(static_cast<int8_t>(b), static_cast<int8_t>(a), &wrapped2) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(wrapped == wrapped2);
}
}
}
int8_t mul_i8_res = -1;
TEST_CHECK(guf_wrapping_mul_i8(42, 5, &mul_i8_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i8_res == -46);
mul_i8_res = -1;
TEST_CHECK(guf_wrapping_mul_i8(5, 42, &mul_i8_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i8_res == -46);
int16_t mul_i16_res = -1245;
TEST_CHECK(guf_wrapping_mul_i16(32767, 2, &mul_i16_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i16_res == -2);
mul_i16_res = -1245;
TEST_CHECK(guf_wrapping_mul_i16(-32767, 2, &mul_i16_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(mul_i16_res == 2);
/*
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2024
use std::num::Wrapping;
fn main() {
let a = Wrapping(-314159265_i32);
let b = Wrapping(4096_i32);
println!("{}", a * b);
}
*/
int32_t mul_i32_res = -12345;
TEST_CHECK(guf_wrapping_mul_i32(INT32_MAX, 2, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32_res == -2);
mul_i32_res = -12345;
TEST_CHECK(guf_wrapping_mul_i32(2, INT32_MAX, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32_res == -2);
mul_i32_res = -12345;
TEST_CHECK(guf_wrapping_mul_i32(INT32_MAX, -2, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(mul_i32_res == 2);
mul_i32_res = -12345;
TEST_CHECK(guf_wrapping_mul_i32(-2, INT32_MAX, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(mul_i32_res == 2);
TEST_CHECK(guf_wrapping_mul_i32(42002718, 314159265, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32_res == -972735522);
mul_i32_res = -12345;
TEST_CHECK(guf_wrapping_mul_i32(314159265, 42002718, &mul_i32_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(mul_i32_res == -972735522);
mul_i32_res = 12345;
guf_wrapping_mul_i32(42002718, 314159265, &mul_i32_res);
TEST_CHECK(mul_i32_res == -972735522);
mul_i32_res = 12345;
guf_wrapping_mul_i32(-42002718, 314159265, &mul_i32_res);
TEST_CHECK(mul_i32_res == 972735522);
mul_i32_res = 12345;
guf_wrapping_mul_i32(-88888888, 99999999, &mul_i32_res);
TEST_CHECK(mul_i32_res == 1374494264);
mul_i32_res = 12345;
guf_wrapping_mul_i32(INT32_MIN, -1, &mul_i32_res);
TEST_CHECK(mul_i32_res == INT32_MIN);
mul_i32_res = 12345;
guf_wrapping_mul_i32(-2147483648, 2147483640, &mul_i32_res);
TEST_CHECK(mul_i32_res == 0);
mul_i32_res = 12345;
guf_wrapping_mul_i32(-2048, -314159265, &mul_i32_res);
TEST_CHECK(mul_i32_res == -846919680);
mul_i32_res = 12345;
guf_wrapping_mul_i32(4096, -314159265, &mul_i32_res);
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;
TEST_CHECK(guf_saturating_add_ptrdiff_t(PTRDIFF_MAX, 1, &ptrdiff_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(ptrdiff_res == PTRDIFF_MAX);
ptrdiff_res = -1234;
TEST_CHECK(guf_saturating_add_ptrdiff_t(PTRDIFF_MIN, -1, &ptrdiff_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(ptrdiff_res == PTRDIFF_MIN);
ptrdiff_res = -1234;
TEST_CHECK(guf_saturating_mul_ptrdiff_t(PTRDIFF_MAX, 2, &ptrdiff_res) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(ptrdiff_res == PTRDIFF_MAX);
ptrdiff_res = -1234;
TEST_CHECK(guf_saturating_mul_ptrdiff_t(PTRDIFF_MIN, 2, &ptrdiff_res) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(ptrdiff_res == PTRDIFF_MIN);
}
void CkdIntTest::test_ckd_uint()
{
for (int32_t a = 0; a <= UINT8_MAX; ++a) {
for (int32_t b = 0; b <= UINT8_MAX; ++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);
GUF_ASSERT(ckd_add == guf_ckd_add_least_u8((uint_least8_t)a, (uint_least8_t)b));
if (add_res > UINT8_MAX) {
TEST_CHECK(ckd_add == GUF_MATH_CKD_OVERFLOW_POS);
uint8_t saturated;
TEST_CHECK(guf_saturating_add_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == UINT8_MAX);
uint8_t wrapped;
TEST_CHECK(guf_wrapping_add_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(static_cast<int32_t>(wrapped) == 0 + (add_res % (UINT8_MAX + 1)));
}
else {
TEST_CHECK(ckd_add == GUF_MATH_CKD_SUCCESS);
uint8_t saturated;
TEST_CHECK(guf_saturating_add_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &saturated) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(saturated) == add_res);
uint8_t wrapped;
TEST_CHECK(guf_wrapping_add_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &wrapped) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(wrapped) == add_res);
}
const int32_t sub_res = a - 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) {
TEST_CHECK(ckd_sub == GUF_MATH_CKD_OVERFLOW_NEG);
uint8_t saturated;
TEST_CHECK(guf_saturating_sub_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(saturated == 0);
uint8_t wrapped;
TEST_CHECK(guf_wrapping_sub_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_NEG);
TEST_CHECK(wrapped == static_cast<uint8_t>(static_cast<uint8_t>(a) - static_cast<uint8_t>(b)));
} else {
TEST_CHECK(ckd_sub == GUF_MATH_CKD_SUCCESS);
uint8_t saturated;
TEST_CHECK(guf_saturating_sub_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &saturated) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(saturated) == sub_res);
uint8_t wrapped;
TEST_CHECK(guf_wrapping_sub_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &wrapped) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(wrapped) == sub_res);
}
const int32_t mul_res = a * 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) {
TEST_CHECK(ckd_mul == GUF_MATH_CKD_OVERFLOW_POS);
uint8_t saturated;
TEST_CHECK(guf_saturating_mul_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &saturated) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(saturated == UINT8_MAX);
uint8_t wrapped;
TEST_CHECK(guf_wrapping_mul_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &wrapped) == GUF_MATH_CKD_OVERFLOW_POS);
TEST_CHECK(wrapped == static_cast<uint8_t>(static_cast<uint8_t>(a) * static_cast<uint8_t>(b)));
} else {
TEST_CHECK(ckd_mul == GUF_MATH_CKD_SUCCESS);
uint8_t saturated;
TEST_CHECK(guf_saturating_mul_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &saturated) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(saturated) == mul_res);
uint8_t wrapped;
TEST_CHECK(guf_wrapping_mul_u8(static_cast<uint8_t>(a), static_cast<uint8_t>(b), &wrapped) == GUF_MATH_CKD_SUCCESS);
TEST_CHECK(static_cast<int32_t>(wrapped) == mul_res);
}
}
}
}

12
libguf/test/test_ckdint.hpp Executable file
View File

@ -0,0 +1,12 @@
#pragma once
#include "test.hpp"
struct CkdIntTest : public Test
{
CkdIntTest(const std::string& nm) : Test(nm) {};
void run() override;
private:
void test_ckd();
void test_ckd_uint();
};

685
libguf/test/test_dbuf.cpp Executable file
View File

@ -0,0 +1,685 @@
#include "test_dbuf.hpp"
extern "C"
{
#include "guf_alloc_libc.h"
#include "impls/dbuf_impl.h"
}
/*
DbufIntTest
*/
void DbufIntTest::run()
{
if (done) {
return;
}
// allocator_ctx.tracker.log = fopen("alloc_log.txt", "w");
// allocator_ctx.tracker.err_log = fopen("alloc_err_log.txt", "w");
dbuf_int dbuf {};
dbuf_int_init(&dbuf, 0, &allocator);
push_check_name("test_push");
test_push(&dbuf, 256);
test_push(&dbuf, 128);
test_push(&dbuf, 17);
TEST_CHECK(dbuf.size == (256 + 128 + 17));
dbuf_int_free(&dbuf, NULL);
TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && dbuf.data == NULL);
dbuf_int_init(&dbuf, 24, &allocator);
TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 24 && dbuf.data);
test_push(&dbuf, 365);
test_push(&dbuf, 4);
test_push(&dbuf, 25);
test_push(&dbuf, 64);
TEST_CHECK(dbuf.size == (365 + 4 + 25 + 64));
dbuf_int_free(&dbuf, NULL);
TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && dbuf.data == NULL);
pop_check_name();
push_check_name("insert_remove");
for (int n = 0; n <= 128; ++n) {
test_insert_remove(n);
}
test_insert_remove(400);
test_insert_remove(401);
test_insert_remove(512);
test_insert_remove(513);
test_insert_remove(601);
test_insert_remove(2048);
test_insert_remove(2049);
pop_check_name();
TEST_CHECK(!guf_alloc_tracker_found_leak(&allocator_ctx.tracker));
// guf_alloc_tracker_print(&allocator_ctx.tracker, stdout);
// puts("");
// fclose(allocator_ctx.tracker.log);
// fclose(allocator_ctx.tracker.err_log);
}
std::vector<int> DbufIntTest::dbuf_to_vec(dbuf_int *dbuf)
{
std::vector<int> vec;
GUF_CNT_FOREACH(dbuf, dbuf_int, it) {
vec.push_back(*it.ptr);
}
return vec;
}
void DbufIntTest::test_push(dbuf_int *dbuf, int n)
{
std::vector<int> vec = dbuf_to_vec(dbuf);
TEST_CHECK(std::ssize(vec) == dbuf->size);
for (int i = 0; i < n; ++i) {
dbuf_int_push_val(dbuf, i);
vec.push_back(i);
TEST_CHECK(*dbuf_int_back(dbuf) == vec.back());
}
ptrdiff_t i = 0;
GUF_CNT_FOREACH(dbuf, dbuf_int, it) {
TEST_CHECK(*it.ptr == vec.at(i++));
}
TEST_CHECK(i == dbuf->size);
i = dbuf->size - 1;
GUF_CNT_FOREACH_REVERSE(dbuf, dbuf_int, rit) {
TEST_CHECK(*rit.ptr == vec.at(i--));
}
TEST_CHECK(i == -1);
}
void DbufIntTest::test_insert_remove(int n)
{
dbuf_int dbuf = {};
dbuf_int_init(&dbuf, 0, &allocator);
std::vector<int> vec = dbuf_to_vec(&dbuf);
guf_err err = GUF_ERR_NONE;
dbuf_int_try_erase(&dbuf, 0, &err);
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
err = GUF_ERR_NONE;
dbuf_int_try_erase(&dbuf, 12, &err);
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
err = GUF_ERR_NONE;
dbuf_int_try_front(&dbuf, &err);
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
err = GUF_ERR_NONE;
dbuf_int_try_back(&dbuf, &err);
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
err = GUF_ERR_NONE;
dbuf_int_try_at(&dbuf, 0, &err);
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
for (int i = 0; i < n; ++i) {
dbuf_int_insert_val(&dbuf, i, i);
dbuf_int_insert_val(&dbuf, i * 2, 0);
dbuf_int_insert_val(&dbuf, i * 4, dbuf.size);
vec.insert(vec.begin() + i, i);
vec.insert(vec.begin(), i * 2);
vec.insert(vec.end(), i * 4);
}
TEST_CHECK(std::ssize(vec) == dbuf.size);
// Iterate
dbuf_int_iter it_dbuf = dbuf_int_begin(&dbuf);
std::vector<int>::const_iterator it_vec = vec.begin();
while (!dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec != vec.end()) {
TEST_CHECK(*it_dbuf.ptr == *it_vec);
it_dbuf = dbuf_int_iter_next(&dbuf, it_dbuf, 1);
std::advance(it_vec, 1);
}
TEST_CHECK(dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec == vec.end());
// Step iterate.
it_dbuf = dbuf_int_begin(&dbuf);
it_vec = vec.begin();
while (!dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec != vec.end()) {
TEST_CHECK(*it_dbuf.ptr == *it_vec);
it_dbuf = dbuf_int_iter_next(&dbuf, it_dbuf, 7);
if (dbuf_int_iter_is_end(&dbuf, it_dbuf)) {
it_vec = vec.end();
} else {
std::advance(it_vec, 7);
}
}
TEST_CHECK(dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec == vec.end());
// Reverse iterate.
dbuf_int_iter rit_dbuf = dbuf_int_rbegin(&dbuf);
std::vector<int>::const_reverse_iterator rit_vec = vec.crbegin();
while (!dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec != vec.crend()) {
TEST_CHECK(*rit_dbuf.ptr == *rit_vec);
rit_dbuf = dbuf_int_iter_next(&dbuf, rit_dbuf, 1);
std::advance(rit_vec, 1);
}
TEST_CHECK(dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec == vec.rend());
// Reverse iterate step.
rit_dbuf = dbuf_int_rbegin(&dbuf);
rit_vec = vec.crbegin();
while (!dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec != vec.crend()) {
TEST_CHECK(*rit_dbuf.ptr == *rit_vec);
rit_dbuf = dbuf_int_iter_next(&dbuf, rit_dbuf, 4);
if (dbuf_int_iter_is_end(&dbuf, rit_dbuf)) {
rit_vec = vec.rend();
} else {
std::advance(rit_vec, 4);
}
}
TEST_CHECK(dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec == vec.rend());
TEST_CHECK(dbuf.size == std::ssize(vec));
for (ptrdiff_t i = 0; i < dbuf.size; i += 8) {
dbuf_int_erase(&dbuf, i);
dbuf_int_erase(&dbuf, 0);
dbuf_int_pop(&dbuf);
vec.erase(vec.begin() + i);
vec.erase(vec.begin() + 0);
vec.pop_back();
}
TEST_CHECK(dbuf.size == std::ssize(vec));
for (ptrdiff_t i = 0; i < dbuf.size; i += 8) {
TEST_CHECK(*dbuf_int_at(&dbuf, i) == vec.at(i));
}
const ptrdiff_t size = dbuf.size;
for (ptrdiff_t i = 0; i < size; ++i) {
int a = dbuf_int_pop_move(&dbuf);
int b = vec.back();
TEST_CHECK(a == b);
vec.pop_back();
}
TEST_CHECK(dbuf.size == 0 && vec.size() == 0);
dbuf_int_free(&dbuf, NULL);
TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && !dbuf.data);
}
/*
DbufCstringTest
*/
void DbufCstringTest::run()
{
if (done) {
return;
}
// allocator_ctx.tracker.log = fopen("alloc_log.txt", "w");
// allocator_ctx.tracker.err_log = fopen("alloc_err_log.txt", "w");
push_check_name("push_insert_erase");
for (int i = 1; i <= 32; ++i) {
test_push_insert_erase(i);
test_push_insert_erase(i, i - 1);
test_push_insert_erase(i, i + 1);
test_push_insert_erase(i, i);
test_push_insert_erase(i, i / 2);
}
test_push_insert_erase(2048);
test_push_insert_erase(2048, 11);
dbuf_heap_cstr str_dbuf = {};
dbuf_heap_cstr_init(&str_dbuf, 0, &allocator);
std::vector<std::string> str_vec {};
for (int i = 0; i < 512; ++i) {
char buf[128];
memset(buf, '\0', GUF_ARR_SIZE(buf));
snprintf(buf, GUF_ARR_SIZE(buf), "This is a pretty guf string (number %d)", i);
guf_cstr_heap str = buf;
dbuf_heap_cstr_push(&str_dbuf, &str, GUF_CPY_DEEP);
str_vec.push_back(std::string{buf});
}
for (int i = 0; i < str_dbuf.size + 16; ++i) {
test_iter(str_vec, &str_dbuf, i);
}
dbuf_heap_cstr_free(&str_dbuf, NULL);
TEST_CHECK(str_dbuf.size == 0 && str_dbuf.capacity == 0 && !str_dbuf.data);
pop_check_name();
push_check_name("find");
test_find();
test_find(3);
test_find(42);
test_find(129);
pop_check_name();
TEST_CHECK(!guf_alloc_tracker_found_leak(&allocator_ctx.tracker));
// guf_alloc_tracker_print(&allocator_ctx.tracker, stdout);
// puts("");
// fclose(allocator_ctx.tracker.log);
// fclose(allocator_ctx.tracker.err_log);
}
void DbufCstringTest::test_iter(std::vector<std::string>& str_vec, dbuf_heap_cstr *str_dbuf, int step)
{
GUF_ASSERT_RELEASE(str_dbuf);
if (step <= 0) {
step = 1;
}
ptrdiff_t i = 0;
GUF_CNT_FOREACH(str_dbuf, dbuf_heap_cstr, it) {
char *str = *it.ptr;
TEST_CHECK(str_vec.at(i) == str);
++i;
}
TEST_CHECK(i == str_dbuf->size);
i = str_dbuf->size - 1;
GUF_CNT_FOREACH_REVERSE(str_dbuf, dbuf_heap_cstr, rit) {
char *str = *rit.ptr;
TEST_CHECK(str_vec.at(i) == str);
--i;
}
TEST_CHECK(i == -1);
dbuf_heap_cstr_iter it_dbuf = dbuf_heap_cstr_begin(str_dbuf);
std::vector<std::string>::iterator it_vec = str_vec.begin();
while (!dbuf_heap_cstr_iter_is_end(str_dbuf, it_dbuf)) {
TEST_CHECK(it_vec != str_vec.end());
TEST_CHECK(*it_vec == *it_dbuf.ptr);
it_dbuf = dbuf_heap_cstr_iter_next(str_dbuf, it_dbuf, step);
if (!dbuf_heap_cstr_iter_is_end(str_dbuf, it_dbuf)) {
std::advance(it_vec, step);
} else {
it_vec = str_vec.end();
}
}
TEST_CHECK(dbuf_heap_cstr_iter_is_end(str_dbuf, it_dbuf) && it_vec == str_vec.end());
dbuf_heap_cstr_iter rit_dbuf = dbuf_heap_cstr_rbegin(str_dbuf);
std::vector<std::string>::reverse_iterator rit_vec = str_vec.rbegin();
while (!dbuf_heap_cstr_iter_is_end(str_dbuf, rit_dbuf)) {
TEST_CHECK(rit_vec != str_vec.rend());
TEST_CHECK(*rit_vec == *rit_dbuf.ptr);
rit_dbuf = dbuf_heap_cstr_iter_next(str_dbuf, rit_dbuf, step);
if (!dbuf_heap_cstr_iter_is_end(str_dbuf, rit_dbuf)) {
std::advance(rit_vec, step);
} else {
rit_vec = str_vec.rend();
}
}
TEST_CHECK(dbuf_heap_cstr_iter_is_end(str_dbuf, rit_dbuf) && rit_vec == str_vec.rend());
for (i = 0; i < str_dbuf->size; ++i) {
char *str = *dbuf_heap_cstr_at(str_dbuf, i);
TEST_CHECK(str_vec.at(i) == str);
}
}
void DbufCstringTest::test_push_insert_erase(int n, ptrdiff_t start_cap)
{
std::vector<std::string> str_vec;
dbuf_heap_cstr str_dbuf {};
dbuf_heap_cstr_init(&str_dbuf, start_cap, &allocator);
for (int i = 0; i < n; ++i) {
constexpr int BUF_SZ = 128;
char buf[BUF_SZ];
memset(buf, '\0', BUF_SZ);
snprintf(buf, BUF_SZ, "This is string number %d", i);
guf_cstr_heap str = buf;
dbuf_heap_cstr_push(&str_dbuf, &str, GUF_CPY_DEEP);
dbuf_heap_cstr_push_val_cpy(&str_dbuf, str);
char *heap_buf = strdup("Move me plz");
dbuf_heap_cstr_push(&str_dbuf, &heap_buf, GUF_CPY_MOVE);
TEST_CHECK(heap_buf == NULL);
TEST_CHECK(strncmp(*dbuf_heap_cstr_back(&str_dbuf), "Move me plz", BUF_SZ) == 0);
TEST_CHECK(strncmp(*dbuf_heap_cstr_at(&str_dbuf, str_dbuf.size - 2), buf, BUF_SZ) == 0);
TEST_CHECK(strncmp(*dbuf_heap_cstr_at(&str_dbuf, str_dbuf.size - 3), buf, BUF_SZ) == 0);
str_vec.push_back(std::string{buf});
str_vec.push_back(std::string{buf});
str_vec.emplace_back("Move me plz");
}
TEST_CHECK(str_dbuf.size == std::ssize(str_vec));
TEST_CHECK(str_dbuf.size == 3 * n);
for (int i = 1; i <= 8; ++i) {
test_iter(str_vec, &str_dbuf, i);
}
test_iter(str_vec, &str_dbuf, (int)str_dbuf.size);
test_iter(str_vec, &str_dbuf, (int)str_dbuf.size - 1);
test_iter(str_vec, &str_dbuf, (int)str_dbuf.size + 1);
for (ptrdiff_t i = 0; i < str_dbuf.size; ++i) {
TEST_CHECK(str_vec.at(i) == *dbuf_heap_cstr_at(&str_dbuf, i));
}
// Insert front.
for (ptrdiff_t i = 0; i < 16; ++i) {
char str[] = "front";
dbuf_heap_cstr_insert_val_cpy(&str_dbuf, str, 0);
str_vec.insert(str_vec.begin(), std::string{str});
}
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
for (ptrdiff_t i = 0; i < str_dbuf.size; ++i) {
TEST_CHECK(str_vec.at(i) == *dbuf_heap_cstr_at(&str_dbuf, i));
}
// Insert back.
for (ptrdiff_t i = 0; i < 16; ++i) {
char str[] = "front";
dbuf_heap_cstr_insert_val_cpy(&str_dbuf, str, str_dbuf.size);
str_vec.insert(str_vec.end(), std::string{str});
}
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
for (ptrdiff_t i = 0; i < str_dbuf.size; ++i) {
TEST_CHECK(str_vec.at(i) == *dbuf_heap_cstr_at(&str_dbuf, i));
}
// Insert at i.
char str[] = "guf";
dbuf_heap_cstr_insert_val_cpy(&str_dbuf, str, str_dbuf.size / 2);
str_vec.insert(str_vec.begin() + str_vec.size() / 2, str);
dbuf_heap_cstr_insert_val_cpy(&str_dbuf, str, str_dbuf.size / 4);
str_vec.insert(str_vec.begin() + str_vec.size() / 4, str);
dbuf_heap_cstr_insert_val_cpy(&str_dbuf, str, 1);
str_vec.insert(str_vec.begin() + 1, str);
dbuf_heap_cstr_insert_val_cpy(&str_dbuf, str, str_dbuf.size - 1);
str_vec.insert(str_vec.begin() + (str_vec.size() - 1), str);
for (ptrdiff_t i = 0; i < str_dbuf.size; ++i) {
TEST_CHECK(str_vec.at(i) == *dbuf_heap_cstr_at(&str_dbuf, i));
}
guf_err err = GUF_ERR_NONE;
dbuf_heap_cstr_try_insert_val_cpy(&str_dbuf, str, str_dbuf.size + 1, &err);
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
err = GUF_ERR_NONE;
dbuf_heap_cstr_try_insert_val_cpy(&str_dbuf, str, -1, &err);
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
err = GUF_ERR_NONE;
dbuf_heap_cstr_try_insert_val_cpy(&str_dbuf, str, str_dbuf.size + 2, &err);
TEST_CHECK(err == GUF_ERR_IDX_RANGE);
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
for (ptrdiff_t i = 0; i < str_dbuf.size; ++i) {
TEST_CHECK(str_vec.at(i) == *dbuf_heap_cstr_at(&str_dbuf, i));
}
if (str_dbuf.size) {
dbuf_heap_cstr_erase(&str_dbuf, str_dbuf.size - 1);
str_vec.erase(str_vec.end() - 1);
}
ptrdiff_t to_rem = 8;
while (str_dbuf.size && to_rem--) {
dbuf_heap_cstr_erase(&str_dbuf, 0);
str_vec.erase(str_vec.begin());
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
if (str_dbuf.size) {
dbuf_heap_cstr_pop(&str_dbuf);
str_vec.pop_back();
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
}
if (str_dbuf.size) {
dbuf_heap_cstr_erase(&str_dbuf, str_dbuf.size / 2);
str_vec.erase(str_vec.begin() + (str_vec.size() / 2));
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
}
}
dbuf_heap_cstr_free(&str_dbuf, NULL);
TEST_CHECK(str_dbuf.size == 0 && str_dbuf.capacity == 0 && !str_dbuf.data);
}
void DbufCstringTest::test_find(int n)
{
if (n < 2) {
n = 2;
}
std::vector<std::string> str_vec {};
dbuf_heap_cstr str_dbuf = {};
dbuf_heap_cstr_init(&str_dbuf, 0, &allocator);
for (int i = 0; i < n; ++i) {
constexpr int BUF_SZ = 128;
char buf[BUF_SZ];
memset(buf, '\0', BUF_SZ);
snprintf(buf, BUF_SZ, "String number %d", i);
dbuf_heap_cstr_push_val_cpy(&str_dbuf, buf);
str_vec.push_back(buf);
}
char *move_me = strdup("Moved string");
dbuf_heap_cstr_push(&str_dbuf, &move_me, GUF_CPY_MOVE);
GUF_ASSERT_RELEASE(move_me == NULL);
str_vec.emplace_back("Moved string");
TEST_CHECK(std::ssize(str_vec) == str_dbuf.size);
for (ptrdiff_t i = 0; i < str_dbuf.size; ++i) {
char *needle = *dbuf_heap_cstr_at(&str_dbuf, i);
TEST_CHECK(str_vec.at(i) == needle);
TEST_CHECK(dbuf_heap_cstr_contains_val(&str_dbuf, needle));
dbuf_heap_cstr_iter fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), dbuf_heap_cstr_end(&str_dbuf), needle);
TEST_CHECK(!dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.cbegin(), str_vec.cend(), needle) != str_vec.end());
dbuf_heap_cstr_iter begin = dbuf_heap_cstr_iter_next(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), i);
dbuf_heap_cstr_iter end = dbuf_heap_cstr_end(&str_dbuf);
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, begin, end, needle);
TEST_CHECK(!dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.cbegin() + i, str_vec.cend(), needle) != str_vec.end());
begin = dbuf_heap_cstr_iter_next(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), i + 1);
end = dbuf_heap_cstr_end(&str_dbuf);
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, begin, end, needle);
TEST_CHECK(dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.cbegin() + i + 1, str_vec.cend(), needle) == str_vec.end());
// Reverse.
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_rbegin(&str_dbuf), dbuf_heap_cstr_rend(&str_dbuf), needle);
TEST_CHECK(!dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.crbegin(), str_vec.crend(), needle) != str_vec.rend());
}
char needle[] = "Definitely not inside";
dbuf_heap_cstr_iter fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), dbuf_heap_cstr_end(&str_dbuf), needle);
TEST_CHECK(dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.cbegin(), str_vec.cend(), needle) == str_vec.end());
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_rbegin(&str_dbuf), dbuf_heap_cstr_rend(&str_dbuf), needle);
TEST_CHECK(dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.crbegin(), str_vec.crend(), needle) == str_vec.rend());
char *needle2 = *dbuf_heap_cstr_at(&str_dbuf, 0);
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_iter_next(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), 1), dbuf_heap_cstr_end(&str_dbuf), needle2);
TEST_CHECK(dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.cbegin() + 1, str_vec.cend(), needle2) == str_vec.end());
needle2 = *dbuf_heap_cstr_back(&str_dbuf);
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_iter_next(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), 1), dbuf_heap_cstr_iter_next(&str_dbuf, dbuf_heap_cstr_end(&str_dbuf), -1), needle2);
TEST_CHECK(dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.begin(), str_vec.end() - 1, needle2) == (str_vec.end() - 1));
needle2 = *dbuf_heap_cstr_at(&str_dbuf, 0);
fnd_it = dbuf_heap_cstr_find_val(&str_dbuf, dbuf_heap_cstr_begin(&str_dbuf), dbuf_heap_cstr_begin(&str_dbuf), needle2);
TEST_CHECK(dbuf_heap_cstr_iter_is_end(&str_dbuf, fnd_it));
TEST_CHECK(std::find(str_vec.cbegin(), str_vec.cbegin(), needle2) == str_vec.cbegin());
dbuf_heap_cstr_free(&str_dbuf, NULL);
}
/*
DbufStrTest:
*/
void DbufStrTest::run()
{
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);
}
void DbufStrTest::test_push_insert_erase(size_t n, ptrdiff_t start_cap)
{
dbuf_str strings = start_cap < 0 ? dbuf_str_new(&allocator) : dbuf_str_new_with_capacity(start_cap, &allocator);
std::vector<std::string> strings_cpp {};
guf_libc_alloc_ctx str_allocator_ctx = {
.tracker = guf_alloc_tracker_new(42, "test_push_insert_erase: local str allocator", NULL, NULL),
.zero_init = false
};
guf_allocator str_allocator = guf_libc_allocator_new(&str_allocator_ctx);
for (size_t i = 0; i < n; ++i) {
std::string str;
for (size_t c = 0; c < 512 + GUF_STR_SSO_BUF_CAP; ++c) {
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_short = guf_str_view{.str = str.data(), .len = (ptrdiff_t)GUF_STR_SSO_BUF_CAP - 1};
guf_str long_str = GUF_STR_UNINITIALISED_CPP, short_str = GUF_STR_UNINITIALISED_CPP;
guf_str_init(&long_str, sv_long, &str_allocator);
guf_str_init(&short_str, sv_short, &str_allocator);
TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 1 + (i*3));
// Move
guf_err err;
dbuf_str_try_push(&strings, &long_str, GUF_CPY_MOVE, &err);
TEST_CHECK(err == GUF_ERR_NONE);
dbuf_str_try_push(&strings, &short_str, GUF_CPY_MOVE, &err);
TEST_CHECK(err == GUF_ERR_NONE);
strings_cpp.push_back(str.substr(0, str.size()));
strings_cpp.push_back(str.substr(0, GUF_STR_SSO_BUF_CAP - 1));
TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 1 + (i*3));
TEST_CHECK(guf_str_is_uninit(&long_str));
TEST_CHECK(guf_str_is_uninit(&short_str));
// Deep-copy
guf_str_init(&long_str, sv_long, &str_allocator);
guf_str_init(&short_str, sv_short, &str_allocator);
TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 2 + (i*3));
dbuf_str_try_push(&strings, &long_str, GUF_CPY_DEEP, &err);
TEST_CHECK(err == GUF_ERR_NONE);
dbuf_str_try_push(&strings, &short_str, GUF_CPY_DEEP, &err);
TEST_CHECK(err == GUF_ERR_NONE);
strings_cpp.push_back(str.substr(0, str.size()));
strings_cpp.push_back(str.substr(0, GUF_STR_SSO_BUF_CAP - 1));
TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 3 + (i*3));
TEST_CHECK(guf_str_is_valid(&long_str) && guf_str_is_valid(&short_str));
TEST_CHECK(guf_str_view_equal_val_arg(guf_str_view_from_str(&long_str), sv_long));
TEST_CHECK(guf_str_view_equal_val_arg(guf_str_view_from_str(&short_str), sv_short));
guf_str_free(&long_str, NULL);
guf_str_free(&short_str, NULL);
TEST_CHECK(str_allocator_ctx.tracker.free_count == 1 + (i*1));
TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 3 + (i*3));
}
TEST_CHECK(str_allocator_ctx.tracker.free_count == n);
TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 3 * n);
TEST_CHECK(strings.size == 4 * (ptrdiff_t)n);
TEST_CHECK(strings.size == std::ssize(strings_cpp));
std::string str;
for (size_t c = 0; c < 512 + GUF_STR_SSO_BUF_CAP; ++c) {
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_short = guf_str_view{.str = str.data(), .len = (ptrdiff_t)GUF_STR_SSO_BUF_CAP - 1};
size_t i = 0;
GUF_CNT_FOREACH(&strings, dbuf_str, it) {
if (TEST_CHECK(it.ptr)) {
const guf_str *s = it.ptr;
TEST_CHECK(guf_str_view_equal_val_arg(guf_str_view_from_str(s), i % 2 == 0 ? sv_long : sv_short));
const guf_str_view sv_cpp = guf_str_view {.str = strings_cpp.at(i).data(), .len = (ptrdiff_t)strings_cpp.at(i).size()};
TEST_CHECK(guf_str_view_equal_val_arg(guf_str_view_from_str(s), sv_cpp));
}
++i;
}
GUF_CNT_FOREACH(&strings, dbuf_str, it) {
if (TEST_CHECK(it.ptr)) {
guf_str *s = it.ptr;
TEST_CHECK(guf_str_append(s, GUF_CSTR_LIT_TO_VIEW_CPP("<END>")));
}
}
std::vector<guf_str_view> delims = {guf_str_view{.str = "<END>", .len = 5}};
i = 0;
GUF_CNT_FOREACH(&strings, dbuf_str, it) {
if (TEST_CHECK(it.ptr)) {
const guf_str *s = it.ptr;
guf_str_tok_state tk_state = guf_str_tok_state_new(guf_str_view_from_str(s), delims.data(), std::ssize(delims), GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
size_t tok_n = 0;
while (guf_str_tok_next(&tk_state, true)) {
TEST_CHECK(guf_str_view_equal_val_arg(tk_state.cur_tok , i % 2 == 0 ? sv_long : sv_short));
TEST_CHECK(guf_str_view_equal_val_arg(tk_state.cur_delim, guf_str_view{.str = "<END>", .len = 5}));
++tok_n;
}
TEST_CHECK(tok_n == 1);
}
++i;
}
dbuf_str_free(&strings, NULL);
TEST_CHECK(!guf_alloc_tracker_found_leak(&str_allocator_ctx.tracker));
}

69
libguf/test/test_dbuf.hpp Executable file
View File

@ -0,0 +1,69 @@
#pragma once
#include <vector>
#include "test.hpp"
extern "C"
{
#include "guf_alloc_libc.h"
#include "impls/dbuf_impl.h"
}
struct DbufIntTest : public Test
{
DbufIntTest(const std::string& nm) : Test(nm)
{
allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 1, "DbufIntTest_allocator", NULL, NULL);
guf_libc_allocator_init(&allocator, &allocator_ctx);
}
void run() override;
private:
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
std::vector<int> dbuf_to_vec(dbuf_int *dbuf);
void test_push(dbuf_int *dbuf, int n);
void test_insert_remove(int n);
};
struct DbufCstringTest : public Test
{
DbufCstringTest(std::string nm) : Test(nm)
{
allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 2, "DbufCstringTest_allocator", NULL, NULL);
guf_libc_allocator_init(&allocator, &allocator_ctx);
}
void run() override;
private:
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
void test_iter(std::vector<std::string>& str_vec, dbuf_heap_cstr *str_dbuf, int step = 1);
void test_push_insert_erase(int n, ptrdiff_t start_cap = 0);
void test_find(int n = 32);
};
struct DbufStrTest : public Test
{
DbufStrTest(std::string nm) : Test(nm)
{
allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 3, "DbufStrTest_allocator", NULL, NULL);
guf_libc_allocator_init(&allocator, &allocator_ctx);
}
void run() override;
private:
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
void test_push_insert_erase(size_t n, ptrdiff_t start_cap = 0);
};

431
libguf/test/test_dict.cpp Executable file
View File

@ -0,0 +1,431 @@
#include "test_dict.hpp"
#include <unordered_map>
#include <cstring>
extern "C"
{
#include "guf_alloc_libc.h"
#include "guf_str.h"
#include "impls/dict_impl.h"
#include "impls/dbuf_impl.h"
}
/*
DictSvToIntTest:
*/
void DictSvToIntTest::run()
{
if (done) {
return;
}
push_check_name("insert_lookup(\"utf8-test.txt\")");
if (TEST_CHECK(load_file(TEST_DATA_DIR "/utf8-test.txt"))) {
insert_lookup();
for (ptrdiff_t i = 0; i <= 64; ++i) {
insert_lookup(i);
}
insert_lookup(512);
insert_lookup(1997);
insert_lookup(1999);
}
free_file();
pop_check_name();
push_check_name("insert_lookup(\"bartleby.txt\")");
if (TEST_CHECK(load_file(TEST_DATA_DIR "/bartleby.txt"))) {
insert_lookup();
insert_lookup(201);
}
free_file();
pop_check_name();
//guf_alloc_tracker_print(&allocator_ctx.tracker, NULL);
TEST_CHECK(!guf_alloc_tracker_found_leak(&allocator_ctx.tracker));
}
void DictSvToIntTest::insert_lookup(std::optional<ptrdiff_t> inital_dict_cap)
{
std::unordered_map<std::string_view, int32_t> word_cnt_map {};
dict_sv_i32 word_cnt_dict {};
dict_str_i32 word_cnt_dict_str {};
if (inital_dict_cap) {
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 {
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);
for (size_t i = 0; i < GUF_ARR_SIZE(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);
}
for (size_t i = 0; i < GUF_ARR_SIZE(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);
}
guf_str_tok_state tok_state = guf_str_tok_state_new(guf_str_view{.str = text_buf.data, .len = text_buf.size}, delims.data, delims.size, GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
while (guf_str_tok_next(&tok_state, true)) {
guf_str_view tok = tok_state.cur_tok;
// if (tok.len <= 0) {
// continue;
// }
std::string_view sv(tok.str , tok.len);
//std::cout << sv << std::string_view(tok_state.cur_delim.str, tok_state.cur_delim.len);
TEST_CHECK(dict_sv_i32_contains(&word_cnt_dict, &tok) == word_cnt_map.contains(sv));
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);
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 {
int32_t *cnt = dict_sv_i32_at_val_arg(&word_cnt_dict, tok);
if (TEST_CHECK(cnt)) {
*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;
}
// printf("tok_len: %td ", tok.len);
// printf("'%.*s'\n", (int)tok.len, tok.str);
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);
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_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 ) {
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 *res2 = dict_str_i32_at_val_arg(&word_cnt_dict_str, guf_str_new_readonly(sv));
TEST_CHECK(res && *res == cnt);
TEST_CHECK(res2 && *res2 == cnt);
}
ptrdiff_t i = 0;
GUF_CNT_FOREACH(&word_cnt_dict, dict_sv_i32, kv_it) {
const dict_sv_i32_kv *kv = kv_it.ptr;
if (TEST_CHECK(kv)) {
const int32_t cnt = kv->val;
const std::string_view sv(kv->key.str, kv->key.len);
if (TEST_CHECK(word_cnt_map.contains(sv))) {
TEST_CHECK(word_cnt_map.at(sv) == cnt);
}
}
++i;
}
TEST_CHECK(i == dict_sv_i32_size(&word_cnt_dict));
TEST_CHECK(i == std::ssize(word_cnt_map));
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 << "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";
// Erase tests:
const double load_fac_before_erase = dict_sv_i32_load_factor(&word_cnt_dict);
const ptrdiff_t size_before_erase = dict_sv_i32_size(&word_cnt_dict);
ptrdiff_t num_del = 0;
while (dict_sv_i32_size(&word_cnt_dict) > size_before_erase / 2) {
dict_sv_i32_kv *kv = NULL;
if (num_del % 2) {
dict_sv_i32_iter it = dict_sv_i32_begin(&word_cnt_dict);
GUF_ASSERT_RELEASE(!dict_sv_i32_iter_is_end(&word_cnt_dict, it));
kv = it.ptr;
} else {
dict_sv_i32_iter rit = dict_sv_i32_rbegin(&word_cnt_dict);
GUF_ASSERT_RELEASE(!dict_sv_i32_iter_is_end(&word_cnt_dict, rit));
kv = rit.ptr;
}
GUF_ASSERT_RELEASE(kv);
const guf_str_view key = kv->key;
const bool del_success = dict_sv_i32_erase(&word_cnt_dict, &key);
TEST_CHECK(del_success);
TEST_CHECK(!dict_sv_i32_contains(&word_cnt_dict, &key));
std::string_view sv(key.str, (size_t)key.len);
if (TEST_CHECK(word_cnt_map.contains(sv))) {
word_cnt_map.erase(sv);
}
TEST_CHECK(!word_cnt_map.contains(sv));
if (del_success) {
++num_del;
}
}
TEST_CHECK(dict_sv_i32_size(&word_cnt_dict) >= 0);
TEST_CHECK(size_before_erase - num_del == dict_sv_i32_size(&word_cnt_dict));
TEST_CHECK(std::ssize(word_cnt_map) == dict_sv_i32_size(&word_cnt_dict));
if (dict_sv_i32_size(&word_cnt_dict) != 0) {
TEST_CHECK(load_fac_before_erase == dict_sv_i32_load_factor(&word_cnt_dict));
} else {
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == 0);
}
if (dict_sv_i32_size(&word_cnt_dict) >= 4) {
dict_sv_i32_kv_dbuf_iter it = dict_sv_i32_begin(&word_cnt_dict);
it = dict_sv_i32_iter_next(&word_cnt_dict, it, 1);
GUF_ASSERT_RELEASE(!dict_sv_i32_iter_is_end(&word_cnt_dict, it));
guf_str_view key = it.ptr->key;
bool del_success = dict_sv_i32_erase(&word_cnt_dict, &key);
TEST_CHECK(del_success);
TEST_CHECK(!dict_sv_i32_contains(&word_cnt_dict, &key));
std::string_view sv(key.str, (size_t)key.len);
if (TEST_CHECK(word_cnt_map.contains(sv))) {
word_cnt_map.erase(sv);
}
it = dict_sv_i32_rbegin(&word_cnt_dict);
it = dict_sv_i32_iter_next(&word_cnt_dict, it, 1);
GUF_ASSERT_RELEASE(!dict_sv_i32_iter_is_end(&word_cnt_dict, it));
key = it.ptr->key;
del_success = dict_sv_i32_erase(&word_cnt_dict, &key);
TEST_CHECK(del_success);
TEST_CHECK(!dict_sv_i32_contains(&word_cnt_dict, &key));
sv = std::string_view(key.str, (size_t)key.len);
if (TEST_CHECK(word_cnt_map.contains(sv))) {
word_cnt_map.erase(sv);
}
}
TEST_CHECK(std::ssize(word_cnt_map) == dict_sv_i32_size(&word_cnt_dict));
i = 0;
GUF_CNT_FOREACH(&word_cnt_dict, dict_sv_i32, kv_it) {
const dict_sv_i32_kv *kv = kv_it.ptr;
if (TEST_CHECK(kv)) {
const int32_t cnt = kv->val;
const std::string_view sv(kv->key.str, (size_t)kv->key.len);
if (TEST_CHECK(word_cnt_map.contains(sv))) {
TEST_CHECK(word_cnt_map.at(sv) == cnt);
}
++i;
}
}
TEST_CHECK(i == word_cnt_dict.kv_elems.size);
TEST_CHECK(i == std::ssize(word_cnt_map));
while (dict_sv_i32_size(&word_cnt_dict) > 0) {
const dict_sv_i32_iter beg = dict_sv_i32_begin(&word_cnt_dict);
if (TEST_CHECK(!dict_sv_i32_iter_is_end(&word_cnt_dict, beg))) {
const guf_str_view key = beg.ptr->key;
if (TEST_CHECK(dict_sv_i32_contains(&word_cnt_dict, &key))) {
const bool del_success = dict_sv_i32_erase(&word_cnt_dict, &key);
TEST_CHECK(del_success);
TEST_CHECK(!dict_sv_i32_contains(&word_cnt_dict, &key));
}
const std::string_view sv(key.str, (size_t)key.len);
if (TEST_CHECK(word_cnt_map.contains(sv))) {
word_cnt_map.erase(sv);
}
}
}
TEST_CHECK(dict_sv_i32_size(&word_cnt_dict) == 0 && word_cnt_map.size() == 0);
TEST_CHECK(word_cnt_dict.num_tombstones == 0);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == 0);
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Hej"), (size_t)64, GUF_CPY_VALUE, GUF_CPY_VALUE);
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("verden!"), (size_t)128, GUF_CPY_VALUE, GUF_CPY_VALUE);
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Flødeskum"), (size_t)256, GUF_CPY_VALUE, GUF_CPY_VALUE);
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("med"), (size_t)512, GUF_CPY_VALUE, GUF_CPY_VALUE);
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Faxe Kondi."), (size_t)1024, GUF_CPY_VALUE, GUF_CPY_VALUE);
TEST_CHECK(dict_sv_i32_size(&word_cnt_dict) == 5);
int32_t *val = dict_sv_i32_at_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Hej"));
TEST_CHECK(val && *val == 64);
val = dict_sv_i32_at_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Flødeskum"));
TEST_CHECK(val && *val == 256);
val = dict_sv_i32_at_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Faxe Kondi."));
TEST_CHECK(val && *val == 1024);
val = dict_sv_i32_at_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("verden!"));
TEST_CHECK(val && *val == 128);
val = dict_sv_i32_at_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("med"));
TEST_CHECK(val && *val == 512);
TEST_CHECK(word_cnt_dict.kv_elems.size == 5);
TEST_CHECK(word_cnt_dict.kv_elems.data[0].val == 64 && std::strcmp(word_cnt_dict.kv_elems.data[0].key.str, "Hej") == 0);
TEST_CHECK(word_cnt_dict.kv_elems.data[1].val == 128 && std::strcmp(word_cnt_dict.kv_elems.data[1].key.str, "verden!") == 0);
TEST_CHECK(word_cnt_dict.kv_elems.data[2].val == 256 && std::strcmp(word_cnt_dict.kv_elems.data[2].key.str, "Flødeskum") == 0);
TEST_CHECK(word_cnt_dict.kv_elems.data[3].val == 512 && std::strcmp(word_cnt_dict.kv_elems.data[3].key.str, "med") == 0);
TEST_CHECK(word_cnt_dict.kv_elems.data[4].val == 1024 && std::strcmp(word_cnt_dict.kv_elems.data[4].key.str, "Faxe Kondi.") == 0);
const double load_fac_beg = dict_sv_i32_load_factor(&word_cnt_dict);
const ptrdiff_t cap_begin = word_cnt_dict.kv_indices_cap;
ptrdiff_t del = 0;
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Hej")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
for (ptrdiff_t n = 0; n < cap_begin + 128; ++n) {
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Hej"), 64, GUF_CPY_VALUE, GUF_CPY_VALUE);
TEST_CHECK(word_cnt_dict.num_tombstones == --del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Hej")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
}
TEST_CHECK(word_cnt_dict.kv_indices_cap == cap_begin);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Faxe Kondi.")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
for (ptrdiff_t n = 0; n < 256; ++n) {
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Faxe Kondi."), 128, GUF_CPY_VALUE, GUF_CPY_VALUE);
TEST_CHECK(word_cnt_dict.num_tombstones == --del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Faxe Kondi.")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
}
TEST_CHECK(word_cnt_dict.kv_indices_cap == cap_begin);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("med")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
for (ptrdiff_t n = 0; n < 512 + cap_begin; ++n) {
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("med"), 256, GUF_CPY_VALUE, GUF_CPY_VALUE);
TEST_CHECK(word_cnt_dict.num_tombstones == --del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("med")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
}
TEST_CHECK(word_cnt_dict.kv_indices_cap == cap_begin);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Flødeskum")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
for (ptrdiff_t n = 0; n < 71; ++n) {
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Flødeskum"), 512, GUF_CPY_VALUE, GUF_CPY_VALUE);
TEST_CHECK(word_cnt_dict.num_tombstones == --del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("Flødeskum")));
TEST_CHECK(word_cnt_dict.num_tombstones == ++del);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == load_fac_beg);
}
TEST_CHECK(word_cnt_dict.kv_indices_cap == cap_begin);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("verden!")));
TEST_CHECK(word_cnt_dict.num_tombstones == 0);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == 0);
for (ptrdiff_t n = 0; n < 201; ++n) {
dict_sv_i32_insert_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("verden!"), 128, GUF_CPY_VALUE, GUF_CPY_VALUE);
TEST_CHECK(word_cnt_dict.num_tombstones == 0);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) > 0);
TEST_CHECK(dict_sv_i32_erase_val_arg(&word_cnt_dict, GUF_CSTR_TO_VIEW_CPP("verden!")));
TEST_CHECK(word_cnt_dict.num_tombstones == 0);
TEST_CHECK(dict_sv_i32_load_factor(&word_cnt_dict) == 0);
}
TEST_CHECK(word_cnt_dict.kv_indices_cap == cap_begin);
TEST_CHECK(word_cnt_dict.kv_elems.size == 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_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;
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);
}
bool DictSvToIntTest::load_file(const char *fname)
{
FILE *in_file {nullptr};
if (!in_file) {
in_file = fopen(fname, "r");
}
GUF_ASSERT_RELEASE(in_file);
dbuf_char_init(&text_buf, 128, &allocator);
int c = EOF;
while ((c = fgetc(in_file)) != EOF) {
dbuf_char_push_val(&text_buf, (char)c);
text_vec.push_back((char)c);
}
fclose(in_file);
// dbuf_char_insert_val(&text_buf, '\xC0', 1);
// text_vec.insert(text_vec.cbegin() + 1, '\xC0');
return TEST_CHECK(std::ssize(text_vec) == text_buf.size);
}
void DictSvToIntTest::free_file()
{
dbuf_char_free(&text_buf, NULL);
text_buf = {};
text_vec.clear();
}

35
libguf/test/test_dict.hpp Executable file
View File

@ -0,0 +1,35 @@
#pragma once
#include <vector>
#include <optional>
#include <string>
#include "test.hpp"
extern "C"
{
#include "impls/dbuf_impl.h"
#include "guf_alloc_libc.h"
}
struct DictSvToIntTest : public Test
{
DictSvToIntTest(const std::string& nm) : Test(nm)
{
allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 3, "DictSvToIntTest_allocator", NULL, NULL);
guf_libc_allocator_init(&allocator, &allocator_ctx);
};
void run() override;
private:
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
dbuf_char text_buf {};
std::vector<char> text_vec {};
bool load_file(const char *fname);
void free_file();
void insert_lookup(std::optional<ptrdiff_t> inital_dict_cap = {});
};

379
libguf/test/test_str.cpp Executable file
View File

@ -0,0 +1,379 @@
#include "test_str.hpp"
extern "C"
{
#include "guf_alloc_libc.h"
}
/*
StrTest:
*/
void StrTest::run()
{
if (done) {
return;
}
const std::vector<std::string> words = {
"",
"\0",
"Hello",
"Othell\0o",
"f\0\0",
"\0",
"0",
"a",
"ab",
"🌈 waow a rainboge!",
"orange cat(1) :3",
"xes yag",
"Hello, world! This is a pretty darn long string I'd say...",
"I want to eat crayons. I crave crayons because they are tasty, and everybody telling me crayons are not edible must be either lying or dumb. I like trains. 42 is a number. 3.14159265... is not a rational number, and it is called pi. I ate some pie (it was a crayon pie).",
std::string(32, 'a'),
std::string(64, 'b'),
std::string(1024, 'a'),
std::string(2048, 'a'),
std::string(4096, 'a'),
std::string(5001, 'a'),
std::string(7121, 'a'),
std::string(2000, 'a'),
std::string(GUF_STR_SSO_BUF_CAP, 'a'),
std::string(GUF_STR_SSO_BUF_CAP - 1, 'a'),
std::string(GUF_STR_SSO_BUF_CAP + 1, 'a'),
std::string(GUF_STR_SSO_BUF_CAP - 2, 'a'),
std::string(GUF_STR_SSO_BUF_CAP + 2, 'a'),
std::string(GUF_STR_SSO_BUF_CAP - 3, 'a'),
std::string(GUF_STR_SSO_BUF_CAP + 3, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 2, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 3, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 4, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 5, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 6, 'a'),
std::string(GUF_STR_SSO_BUF_CAP * 7, 'a'),
};
push_check_name("init_empy");
test_init_empty();
pop_check_name();
push_check_name("append_char");
for (const auto& word : words) {
test_init_free(word);
test_append_char(word);
test_append_char(word, true);
}
pop_check_name();
push_check_name("append_str");
for (size_t i = 0; i < words.size(); ++i) {
const auto& w1 = words.at(i);
append_str(w1, w1);
append_str(w1, w1);
for (size_t j = i + 1; j < words.size(); ++j) {
const auto& w2 = words.at(j);
append_str(w1, w2);
append_str(w2, w1);
}
}
pop_check_name();
push_check_name("test_popsplit");
std::vector<std::string_view> split = test_popsplit("1997-04-01", "-");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "1997" && split.at(1) == "04" && split.at(2) == "01");
}
split = test_popsplit("1997-04-01-", "-");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "1997" && split.at(1) == "04" && split.at(2) == "01");
}
split = test_popsplit("2025/05/08", "/");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "2025" && split.at(1) == "05" && split.at(2) == "08");
}
split = test_popsplit("2025/05/08/", "/");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "2025" && split.at(1) == "05" && split.at(2) == "08");
}
split = test_popsplit("2025/05/08//", "/");
if (TEST_CHECK(split.size() == 4)) {
TEST_CHECK(split.at(0) == "2025" && split.at(1) == "05" && split.at(2) == "08" && split.at(3) == "");
}
split = test_popsplit("/2025/05/08", "/");
if (TEST_CHECK(split.size() == 4)) {
TEST_CHECK(split.at(0) == "" && split.at(1) == "2025" && split.at(2) == "05" && split.at(3) == "08");
}
split = test_popsplit("//2025/05/08", "/");
if (TEST_CHECK(split.size() == 5)) {
TEST_CHECK(split.at(0) == "" && split.at(1) == "" && split.at(2) == "2025" && split.at(3) == "05" && split.at(4) == "08");
}
split = test_popsplit("I eat formidable crayons, oof, for real", "foo");
if (TEST_CHECK(split.size() == 1)) {
TEST_CHECK(split.at(0) == "I eat formidable crayons, oof, for real");
}
split = test_popsplit("Hej <<", "<<");
if (TEST_CHECK(split.size() == 1)) {
TEST_CHECK(split.at(0) == "Hej ");
}
split = test_popsplit("Hej << verden", "<<");
if (TEST_CHECK(split.size() == 2)) {
TEST_CHECK(split.at(0) == "Hej " && split.at(1) == " verden");
}
split = test_popsplit("<< Hej << verden", "<<");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "" && split.at(1) == " Hej " && split.at(2) == " verden");
}
split = test_popsplit("<< Hej << verden <<< foo<>", "<<");
if (TEST_CHECK(split.size() == 4)) {
TEST_CHECK(split.at(0) == "" && split.at(1) == " Hej " && split.at(2) == " verden " && split.at(3) == "< foo<>");
}
split = test_popsplit("I eat tofu", "");
if (TEST_CHECK(split.size() == 1)) {
TEST_CHECK(split.at(0) == "I eat tofu");
}
split = test_popsplit("At 3 a.m. during FULL-moon FULL-STOP Next to the public-library's -STOP sign FULL-STOP", "FULL-STOP");
if (TEST_CHECK(split.size() == 2)) {
TEST_CHECK(split.at(0) == "At 3 a.m. during FULL-moon " && split.at(1) == " Next to the public-library's -STOP sign ");
}
split = test_popsplit("At 3 a.m. during FULL-moon FULL-STOP Next to the public-library's -STOP sign FULL-STOPI like trains, FULL-STO", "FULL-STOP");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "At 3 a.m. during FULL-moon " && split.at(1) == " Next to the public-library's -STOP sign " && split.at(2) == "I like trains, FULL-STO");
}
split = test_popsplit("At 3 a.m. during FULL-moon FULL-STOP Next to the public-library's -STOP sign FULL-STOPI like trains, FULL-STO Poo", "FULL-STOP");
if (TEST_CHECK(split.size() == 3)) {
TEST_CHECK(split.at(0) == "At 3 a.m. during FULL-moon " && split.at(1) == " Next to the public-library's -STOP sign " && split.at(2) == "I like trains, FULL-STO Poo");
}
pop_check_name();
push_check_name("get_toks");
std::vector<std::string_view> tok_words = {"hello", "world", "cat", "vertex", "normal", "pizza", "running", "mouse", "playing", "adjacent"};
std::vector<std::string_view> delims = {",", " ", "\n", "\t", "\r"};
for (int is_trailing = 0; is_trailing < 2; ++is_trailing) {
for (ptrdiff_t num_words = 1; num_words < std::ssize(tok_words); ++num_words) {
std::string str = "";
for (ptrdiff_t j = 0; j < num_words; ++j) {
str += tok_words.at(j);
if (j < num_words - 1 || is_trailing) {
str += ", ";
}
}
std::vector<std::string_view> toks = get_toks(std::string_view{str}, delims, false, GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
if (TEST_CHECK(std::ssize(toks) == num_words)) {
for (ptrdiff_t i = 0; i < num_words; ++i) {
TEST_CHECK(toks.at(i) == tok_words.at(i));
}
}
}
}
std::string_view tok_str = "<stats>age: 28, occupation: NULL, crayons_eaten: 256 </stats>";
delims = {"<stats>", "</stats>", ":", ",", " ", "\t", "<stats", "<", ">", "</"};
auto tok_result = get_toks(tok_str, delims, true, GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
TEST_CHECK(tok_result.size() == 19);
TEST_CHECK(tok_result.at(18) == "</stats>" && tok_result.at(0) == "<stats>" && tok_result.at(1) == "age" && tok_result.at(2) == ":" && tok_result.at(3) == " " && tok_result.at(4) == "28");
tok_result = get_toks(tok_str, delims, false, GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
TEST_CHECK(tok_result.size() == 6);
TEST_CHECK(tok_result.at(0) == "age" && tok_result.at(1) == "28" && tok_result.at(2) == "occupation" && tok_result.at(3) == "NULL" &&
tok_result.at(4) == "crayons_eaten" && tok_result.at(5) == "256");
pop_check_name();
// guf_alloc_tracker_print(&allocator_ctx.tracker, NULL);
TEST_CHECK(!guf_alloc_tracker_found_leak(&allocator_ctx.tracker));
}
void StrTest::test_init_free(std::string str)
{
guf_str s0;
guf_str_init(&s0, GUF_CSTR_TO_VIEW_CPP(str.c_str()), &allocator);
guf_str s1 = guf_str_new(GUF_CSTR_TO_VIEW_CPP(str.c_str()), &allocator);
guf_str s2;
guf_str_init_from_cstr(&s2, str.c_str(), &allocator);
TEST_CHECK(guf_str_equal(&s0, &s1));
TEST_CHECK(guf_str_equal(&s0, &s2));
TEST_CHECK(guf_str_equal(&s1, &s2));
TEST_CHECK((ptrdiff_t)str.size() == guf_str_len(&s0));
TEST_CHECK(str == guf_str_const_cstr(&s0));
TEST_CHECK(str == guf_str_cstr(&s0));
TEST_CHECK((ptrdiff_t)str.size() == guf_str_len(&s1));
TEST_CHECK(str == guf_str_const_cstr(&s1));
TEST_CHECK(str == guf_str_cstr(&s1));
TEST_CHECK((ptrdiff_t)str.size() == guf_str_len(&s2));
TEST_CHECK(str == guf_str_const_cstr(&s2));
TEST_CHECK(str == guf_str_cstr(&s2));
guf_str_free(&s0, NULL);
guf_str_free(&s1, NULL);
guf_str_free(&s2, NULL);
TEST_CHECK(guf_str_is_uninit(&s0));
TEST_CHECK(guf_str_is_uninit(&s1));
TEST_CHECK(guf_str_is_uninit(&s2));
}
void StrTest::test_init_empty()
{
std::string str = "";
guf_str s = GUF_STR_UNINITIALISED_CPP;
guf_str_init_empty(&s, &allocator);
TEST_CHECK(guf_str_len(&s) == 0);
TEST_CHECK(str == guf_str_const_cstr(&s));
guf_str_append_char(&s, 'a', 1024);
str.append(1024, 'a');
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
guf_str_append_char(&s, 'b', 24);
str.append(24, 'b');
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
guf_str_append_char(&s, 'c', 255);
str.append(255, 'c');
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
*guf_str_at(&s, 0) = '<';
str.at(0) = '<';
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
*guf_str_at(&s, guf_str_len(&s) - 1) = '>';
str.at(str.size() - 1) = '>';
TEST_CHECK(guf_str_len(&s) == (ptrdiff_t)str.size());
TEST_CHECK(guf_str_const_cstr(&s) == str);
guf_err err = GUF_ERR_NONE;
TEST_CHECK(NULL == guf_str_try_at(&s, guf_str_len(&s), &err));
TEST_CHECK(err != GUF_ERR_NONE && err == GUF_ERR_IDX_RANGE);
err = GUF_ERR_NONE;
TEST_CHECK(NULL == guf_str_try_at(&s, -1, &err));
TEST_CHECK(err != GUF_ERR_NONE && err == GUF_ERR_IDX_RANGE);
guf_str_free(&s, NULL);
TEST_CHECK(guf_str_is_uninit(&s));
}
void StrTest::test_append_char(std::string str, bool include_null)
{
guf_str s0 = guf_str_new(guf_str_view{.str = str.c_str(), .len = (ptrdiff_t)str.size()}, &allocator);
TEST_CHECK((ptrdiff_t)str.size() == guf_str_len(&s0));
TEST_CHECK((str == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
for (int i = include_null ? 0 : 1; i < 128; ++i) {
char ch = (char)i;
guf_str_append_one_char(&s0, ch);
str.append(1, ch);
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str.size());
TEST_CHECK((str == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
}
for (int i = include_null ? 0 : 1; i < 128; ++i) {
char ch = (char)i;
guf_str_append_char(&s0, ch, i);
str.append(i, ch);
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str.size());
TEST_CHECK((str == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
guf_str_append_char(&s0, ch, i * 16);
str.append(i * 16, ch);
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str.size());
TEST_CHECK((str == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
}
guf_str_free(&s0, NULL);
TEST_CHECK(guf_str_is_uninit(&s0));
}
void StrTest::append_str(const std::string& a, const std::string& b)
{
std::string str0 = a;
guf_str s0 = guf_str_new(guf_str_view{.str = str0.c_str(), .len = (ptrdiff_t)str0.size()}, &allocator);
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str0.size());
TEST_CHECK((str0 == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
TEST_CHECK((str0 == std::string_view{guf_str_cstr(&s0), (size_t)guf_str_len(&s0)}));
for (int i = 0; i <= 64; ++i) {
str0.append(b);
guf_str_append(&s0, guf_str_view{.str = b.c_str(), .len = (ptrdiff_t)b.size()});
TEST_CHECK(guf_str_len(&s0) == (ptrdiff_t)str0.size());
TEST_CHECK((str0 == std::string_view{guf_str_const_cstr(&s0), (size_t)guf_str_len(&s0)}));
TEST_CHECK((str0 == std::string_view{guf_str_cstr(&s0), (size_t)guf_str_len(&s0)}));
}
guf_str_free(&s0, NULL);
TEST_CHECK(guf_str_is_uninit(&s0));
}
std::vector<std::string_view> StrTest::test_popsplit(std::string_view str, std::string_view delim)
{
std::vector<std::string_view> result = {};
if (delim.size() > 0) { // NOTE: str.find with an empty delimiter returns 0, not std::string::npos
std::string_view src_cpp = str;
for (size_t idx = src_cpp.find(delim, 0); src_cpp.size() > 0; idx = src_cpp.find(delim, 0)) {
result.push_back(src_cpp.substr(0, idx));
if (idx == std::string::npos) {
break;
}
src_cpp = src_cpp.substr(idx + delim.size());
}
} else {
result.push_back(str);
}
const guf_str_view delim_sv = guf_str_view{.str = delim.data(), .len = (ptrdiff_t)delim.size()};
guf_str_view src = guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()};
size_t n = 0;
do {
const guf_str_view popped = guf_str_view_pop_split(&src, delim_sv);
TEST_CHECK(n < result.size());
TEST_CHECK(std::string_view(popped.str, (size_t)popped.len) == result.at(n));
const guf_str_view res = {.str = result.at(n).data(), .len = (ptrdiff_t)result.at(n).size()};
TEST_CHECK(guf_str_view_equal(&popped, &res));
TEST_CHECK(guf_str_view_equal_val_arg(popped, res));
// std::cout << "guf: " << std::string_view{popped.str, (size_t)popped.len} << "\n";
// std::cout << "cpp: " << std::string_view{res.str, (size_t)res.len} << "\n";
++n;
} while (src.len > 0);
TEST_CHECK(n == result.size());
return result;
}
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{.str = sv_in.data(), .len = (ptrdiff_t)sv_in.size()};
std::vector<guf_str_view> delims;
for (const auto delim : delims_in) {
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);
std::vector<std::string_view> toks_out;
while (guf_str_tok_next(&tok_state, preserve_delims)) {
if (tok_state.cur_tok.len > 0) {
toks_out.push_back( std::string_view{tok_state.cur_tok.str, (size_t)tok_state.cur_tok.len});
}
if (preserve_delims && tok_state.cur_delim.len > 0) {
toks_out.push_back( std::string_view{tok_state.cur_delim.str, (size_t)tok_state.cur_delim.len});
}
}
TEST_CHECK(tok_state.done);
const ptrdiff_t num_toks = preserve_delims ? tok_state.num_delims_read + tok_state.num_toks_read : tok_state.num_toks_read;
TEST_CHECK(num_toks == std::ssize(toks_out));
return toks_out;
}

33
libguf/test/test_str.hpp Executable file
View File

@ -0,0 +1,33 @@
#pragma once
#include <vector>
#include <string>
#include "test.hpp"
extern "C"
{
#include "guf_str.h"
#include "guf_alloc_libc.h"
}
struct StrTest : public Test
{
StrTest(const std::string& nm) : Test(nm)
{
allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 4, "StrTest_allocator", NULL, NULL);
guf_libc_allocator_init(&allocator, &allocator_ctx);
};
void run() override;
private:
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
void test_init_free(std::string str);
void test_init_empty();
void test_append_char(std::string str, bool include_null = false);
void append_str(const std::string& a, const std::string& b);
std::vector<std::string_view> test_popsplit(std::string_view str, std::string_view delim);
std::vector<std::string_view> get_toks(std::string_view sv_in, const std::vector<std::string_view>& delims_in, bool preserve_delims = false, guf_str_tok_delim_opt opt = GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
};

387
libguf/test/test_utf8.cpp Executable file
View File

@ -0,0 +1,387 @@
#include "test_utf8.hpp"
extern "C"
{
#include "guf_alloc_libc.h"
#include "guf_str.h"
#include "impls/dict_impl.h"
}
/*
UTF8Test:
*/
void UTF8Test::run()
{
if (done) {
return;
}
push_check_name("read_utf8_chars");
ptrdiff_t valid = 0, invalid = 0;
read_utf8_chars(TEST_DATA_DIR "/" "utf8-test.txt", &valid, &invalid);
TEST_CHECK(valid == 2635 && invalid == 0);
read_utf8_chars(TEST_DATA_DIR "/" "bartleby.txt", &valid, &invalid);
TEST_CHECK(valid > 16000 && invalid == 0);
pop_check_name();
push_check_name("count_words");
dbuf_str_view delims = dbuf_str_view_new(&allocator);
for (size_t i = 0; i < GUF_ARR_SIZE(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);
}
for (size_t i = 0; i < GUF_ARR_SIZE(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);
}
int words = count_words(TEST_DATA_DIR "/" "utf8-test.txt", &delims);
TEST_CHECK(words == 422);
int words_with_delims = count_words_with_delims(TEST_DATA_DIR "/" "utf8-test.txt", &delims);
TEST_CHECK(words_with_delims == 950);
int words2 = count_words(TEST_DATA_DIR "/" "bartleby.txt", &delims);
TEST_CHECK(words2 > 2048);
dbuf_str_view_free(&delims, NULL);
pop_check_name();
push_check_name("encode_decode");
encode_decode();
encode_decode_file(TEST_DATA_DIR "/" "utf8-test.txt");
encode_decode_file(TEST_DATA_DIR "/" "bartleby.txt");
pop_check_name();
//guf_alloc_tracker_print(&allocator_ctx.tracker, NULL);
TEST_CHECK(!guf_alloc_tracker_found_leak(&allocator_ctx.tracker));
}
bool UTF8Test::load_text(const char *fname)
{
FILE *in_file {nullptr};
if (!in_file) {
in_file = fopen(fname, "r");
}
if (!in_file) {
return false;
}
dbuf_char_init(&text_buf, 128, &allocator);
int c = EOF;
while ((c = fgetc(in_file)) != EOF) {
dbuf_char_push_val(&text_buf, (char)c);
text_vec.push_back((char)c);
}
fclose(in_file);
return TEST_CHECK(std::ssize(text_vec) == text_buf.size);
}
void UTF8Test::free_text()
{
dbuf_char_free(&text_buf, NULL);
text_vec.clear();
}
void UTF8Test::read_utf8_chars(const char *fname, ptrdiff_t *n_valid, ptrdiff_t *n_invalid)
{
GUF_ASSERT_RELEASE(load_text(fname));
ptrdiff_t valid_chars = 0, invalid_chars = 0, bytes = 0;
guf_str_view input_str = {.str = text_buf.data, .len = text_buf.size};
guf_utf8_char ch = {};
for (guf_utf8_stat stat = guf_utf8_char_next(&ch, &input_str); stat != GUF_UTF8_READ_DONE; stat = guf_utf8_char_next(&ch, &input_str)) {
if (stat == GUF_UTF8_READ_VALID) {
++valid_chars;
// printf("%s", ch.bytes);
} else {
++invalid_chars;
// printf("::INVALID_UTF8_CHAR::");
}
bytes += guf_utf8_char_num_bytes(&ch);
}
TEST_CHECK(input_str.len == 0 && input_str.str == NULL);
TEST_CHECK(bytes == text_buf.size);
// printf("\nread %td bytes\n", bytes);
// printf("read %td valid and %td invalid utf-8 characters\n", valid_chars, invalid_chars);
free_text();
if (n_valid)
*n_valid = valid_chars;
if (n_invalid)
*n_invalid = invalid_chars;
}
int UTF8Test::count_words(const char *fname, const dbuf_str_view *delims)
{
GUF_ASSERT_RELEASE(load_text(fname));
int num_words = 0;
guf_str_tok_state tok_state = guf_str_tok_state_new(guf_str_view{.str = text_buf.data, .len = text_buf.size}, delims->data, delims->size, GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
while (guf_str_tok_next(&tok_state, false)) {
TEST_CHECK(tok_state.cur_tok.len > 0);
++num_words;
}
free_text();
return num_words;
}
int UTF8Test::count_words_with_delims(const char *fname, const dbuf_str_view *delims)
{
GUF_ASSERT_RELEASE(load_text(fname));
int num_words = 0, num_delims = 0;
guf_str_tok_state tok_state = guf_str_tok_state_new(guf_str_view{.str = text_buf.data, .len = text_buf.size}, delims->data, delims->size, GUF_STR_TOK_DELIM_OPT_MATCH_LONGEST);
while (guf_str_tok_next(&tok_state, true)) {
if (tok_state.cur_tok.len) {
++num_words;
// printf("'%.*s'\n", (int)tok_state.cur_tok.len, tok_state.cur_tok.str);
}
if (tok_state.cur_delim.len) {
++num_delims;
// if (tok_state.cur_delim.str[0] == '\n')
// printf("'\\n'\n");
// else
// printf("'%.*s'\n", (int)tok_state.cur_delim.len, tok_state.cur_delim.str);
}
}
free_text();
return num_words + num_delims;
}
void UTF8Test::encode_decode_file(const char *fname)
{
GUF_ASSERT_RELEASE(load_text(fname));
dbuf_i32 cp_buf = dbuf_i32_new(&allocator);
ptrdiff_t valid_chars = 0, invalid_chars = 0;
guf_str_view input_str = {.str = text_buf.data, .len = text_buf.size};
guf_utf8_char ch = {};
for (guf_utf8_stat stat = guf_utf8_char_next(&ch, &input_str); stat != GUF_UTF8_READ_DONE; stat = guf_utf8_char_next(&ch, &input_str)) {
if (stat == GUF_UTF8_READ_VALID) {
++valid_chars;
const int32_t codepoint = guf_utf8_decode(&ch);
TEST_CHECK(codepoint >= 0);
dbuf_i32_push_val(&cp_buf, codepoint);
} else {
++invalid_chars;
const int32_t codepoint = guf_utf8_decode(&ch);
TEST_CHECK(codepoint < 0);
dbuf_i32_push_val(&cp_buf, -1);
}
}
TEST_CHECK(cp_buf.size == valid_chars + invalid_chars);
guf_str_view in_str = {.str = text_buf.data, .len = text_buf.size};
GUF_CNT_FOREACH(&cp_buf, dbuf_i32, it) {
GUF_ASSERT_RELEASE(it.ptr);
const int32_t codepoint = *it.ptr;
guf_utf8_char utf8_ch = {};
const guf_utf8_stat stat = guf_utf8_char_next(&utf8_ch, &in_str);
if (codepoint >= 0) {
TEST_CHECK(stat == GUF_UTF8_READ_VALID);
guf_utf8_char encoded_ch = {};
TEST_CHECK(guf_utf8_encode(&encoded_ch, codepoint));
TEST_CHECK(guf_utf8_equal(&encoded_ch, &utf8_ch));
}
}
guf_utf8_char utf8_ch = {};
const guf_utf8_stat stat = guf_utf8_char_next(&utf8_ch, &in_str);
TEST_CHECK(stat == GUF_UTF8_READ_DONE);
dbuf_i32_free(&cp_buf, NULL);
free_text();
}
void UTF8Test::encode_decode()
{
guf_utf8_char utf8 = {0};
// 1 byte characters.
for (uint8_t ascii = 0; ascii <= 0x7F; ++ascii) {
TEST_CHECK(guf_utf8_encode(&utf8, ascii));
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 1);
TEST_CHECK(utf8.bytes[0] == ascii);
TEST_CHECK(utf8.bytes[1] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == ascii);
}
// 2 byte characters:
TEST_CHECK(guf_utf8_encode(&utf8, 0x00E6)); // "æ" (Latin Small Letter Ae)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\xA6');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00E6);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00E5)); // "å" (Latin Small Letter A with Ring Above)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\xA5');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00E5);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00F8)); // "ø" (Latin Small Letter O with Stroke)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\xB8');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00F8);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00E4)); // "ä" (Latin Small Letter A with Diaeresis)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\xA4');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00E4);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00F6)); // "ö" (Latin Small Letter O with Diaeresis)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\xB6');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00F6);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00D6)); // "Ö" (Latin Capital Letter O with Diaeresis)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\x96');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00D6);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00FC)); // "ü" (Latin Small Letter U with Diaeresis)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC3' && utf8.bytes[1] == '\xBC');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00FC);
TEST_CHECK(guf_utf8_encode(&utf8, 0x00B5)); // "µ" (Micro Sign)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xC2' && utf8.bytes[1] == '\xB5');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x00B5);
TEST_CHECK(guf_utf8_encode(&utf8, 0x030A)); // "◌̊" (Combining Ring Above)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 2);
TEST_CHECK(utf8.bytes[0] == '\xCC' && utf8.bytes[1] == '\x8A');
TEST_CHECK(utf8.bytes[2] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x030A);
// 3 byte characters:
TEST_CHECK(guf_utf8_encode(&utf8, 0x7121)); // "無" (Nothingness; CJK Unified Ideograph-7121)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 3);
TEST_CHECK(!guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(utf8.bytes[0] == '\xE7' && utf8.bytes[1] == '\x84' && utf8.bytes[2] == '\xA1');
TEST_CHECK(utf8.bytes[3] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x7121);
TEST_CHECK(guf_utf8_encode(&utf8, 0x201E)); // "„" (Double Low-9 Quotation Mark)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 3);
TEST_CHECK(!guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(utf8.bytes[0] == '\xE2' && utf8.bytes[1] == '\x80' && utf8.bytes[2] == '\x9E');
TEST_CHECK(utf8.bytes[3] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x201E);
TEST_CHECK(guf_utf8_encode(&utf8, 0x20AC)); // "€" (Euro Sign)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 3);
TEST_CHECK(!guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(utf8.bytes[0] == '\xE2' && utf8.bytes[1] == '\x82' && utf8.bytes[2] == '\xAC');
TEST_CHECK(utf8.bytes[3] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x20AC);
TEST_CHECK(guf_utf8_encode(&utf8, 0xFC51)); // "ﱑ" (Arabic Ligature Heh with Jeem Isolated Form)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 3);
TEST_CHECK(!guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(utf8.bytes[0] == '\xEF' && utf8.bytes[1] == '\xB1' && utf8.bytes[2] == '\x91');
TEST_CHECK(utf8.bytes[3] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0xFC51);
TEST_CHECK(guf_utf8_encode(&utf8, 0x1AA3)); // "᪣" (Tai Tham Sign Keow)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 3);
TEST_CHECK(!guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(utf8.bytes[0] == '\xE1' && utf8.bytes[1] == '\xAA' && utf8.bytes[2] == '\xA3');
TEST_CHECK(utf8.bytes[3] == '\0');
TEST_CHECK(guf_utf8_decode(&utf8) == 0x1AA3);
TEST_CHECK(guf_utf8_encode(&utf8, GUF_UTF8_REPLACEMENT_CHAR_CODEPOINT)); // "<22>" (Replacement Character)
TEST_CHECK(guf_utf8_char_num_bytes(&utf8) == 3);
TEST_CHECK(utf8.bytes[0] == '\xEF' && utf8.bytes[1] == '\xBF' && utf8.bytes[2] == '\xBD');
TEST_CHECK(utf8.bytes[3] == '\0');
TEST_CHECK(guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(guf_utf8_decode(&utf8) == GUF_UTF8_REPLACEMENT_CHAR_CODEPOINT);
// 4 byte characters:
TEST_CHECK(guf_utf8_encode(&utf8, 0x1F308)); // "🌈" (Rainbow)
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(guf_utf8_decode(&utf8) == 0x1F308);
TEST_CHECK(guf_utf8_encode(&utf8, 0x130B8)); // "𓂸" (Egyptian Hieroglyph D052)
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(guf_utf8_decode(&utf8) == 0x130B8);
TEST_CHECK(guf_utf8_encode(&utf8, 0x1F97A)); // "🥺" (Face with Pleading Eyes)
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(guf_utf8_decode(&utf8) == 0x1F97A);
TEST_CHECK(guf_utf8_encode(&utf8, 0x1F980)); // "🦀" (Crab)
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(guf_utf8_decode(&utf8) == 0x1F980);
// Invalid characters:
utf8 = {.bytes = {'\xC0', '\x80', 0, 0}};
TEST_CHECK(guf_utf8_decode(&utf8) < 0);
utf8 = {.bytes = {'\xC0', 0, 0, 0}};
TEST_CHECK(guf_utf8_decode(&utf8) < 0);
utf8 = {.bytes = {'\x80', 0, 0, 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).
TEST_CHECK(!guf_utf8_encode(&utf8, 0xD800));
TEST_CHECK(guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(guf_utf8_decode(&utf8) == GUF_UTF8_REPLACEMENT_CHAR_CODEPOINT);
TEST_CHECK(!guf_utf8_encode(&utf8, 0xDFFF));
TEST_CHECK(guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(guf_utf8_decode(&utf8) == GUF_UTF8_REPLACEMENT_CHAR_CODEPOINT);
TEST_CHECK(!guf_utf8_encode(&utf8, 0xDA00));
TEST_CHECK(guf_utf8_equal(&utf8, &GUF_UTF8_REPLACEMENT_CHAR));
TEST_CHECK(guf_utf8_decode(&utf8) == GUF_UTF8_REPLACEMENT_CHAR_CODEPOINT);
char buf[] = {'\x2F', '\xC0', '\xAE', '\x2E', '\x2F'};
guf_str_view input_str = {.str = buf, .len = GUF_ARR_SIZE(buf)};
guf_utf8_char ch = {};
int valid_chars = 0, invalid_chars = 0;
for (guf_utf8_stat stat = guf_utf8_char_next(&ch, &input_str); stat != GUF_UTF8_READ_DONE; stat = guf_utf8_char_next(&ch, &input_str)) {
if (stat == GUF_UTF8_READ_VALID) {
++valid_chars;
} else {
++invalid_chars;
}
}
TEST_CHECK(invalid_chars == 2 && valid_chars == 3);
char buf2[] = {'\xE0', '\x80', 'a', 'b', 'c'}; // 1 invalid 3-byte-character, 2 valid 1-byte-characters
input_str = {.str = buf2, .len = GUF_ARR_SIZE(buf2)};
ch = {};
valid_chars = invalid_chars = 0;
for (guf_utf8_stat stat = guf_utf8_char_next(&ch, &input_str); stat != GUF_UTF8_READ_DONE; stat = guf_utf8_char_next(&ch, &input_str)) {
if (stat == GUF_UTF8_READ_VALID) {
// printf("%s", ch.bytes);
++valid_chars;
} else {
// printf("%s", GUF_UTF8_REPLACEMENT_CHAR.bytes);
++invalid_chars;
}
}
TEST_CHECK(invalid_chars == 1 && valid_chars == 2);
}

36
libguf/test/test_utf8.hpp Executable file
View File

@ -0,0 +1,36 @@
#pragma once
#include <vector>
#include "test.hpp"
extern "C"
{
#include "impls/dbuf_impl.h"
#include "guf_alloc_libc.h"
}
struct UTF8Test : public Test
{
UTF8Test(const std::string& nm) : Test(nm)
{
allocator_ctx.zero_init = false;
guf_alloc_tracker_init(&allocator_ctx.tracker, 5, "UTF8Test_allocator", NULL, NULL);
guf_libc_allocator_init(&allocator, &allocator_ctx);
};
void run() override;
private:
guf_allocator allocator;
guf_libc_alloc_ctx allocator_ctx;
dbuf_char text_buf {};
std::vector<char> text_vec;
bool load_text(const char *fname);
void free_text();
void read_utf8_chars(const char *fname, ptrdiff_t *n_valid, ptrdiff_t *n_invalid);
int count_words(const char *fname, const dbuf_str_view *delims);
int count_words_with_delims(const char *fname, const dbuf_str_view *delims);
void encode_decode_file(const char *fname);
void encode_decode();
};