Add checked arithmetic
This commit is contained in:
parent
c4b68d5ad2
commit
9c417d2aa1
@ -15,6 +15,12 @@ typedef struct guf_allocator {
|
||||
void *ctx;
|
||||
} guf_allocator;
|
||||
|
||||
typedef struct guf_alloc_meta {
|
||||
size_t alloc_count, realloc_count, free_count;
|
||||
ptrdiff_t allocated_bytes;
|
||||
uint32_t alloc_id;
|
||||
} guf_alloc_meta;
|
||||
|
||||
typedef enum guf_alloc_fn_type {
|
||||
GUF_ALLOC_FN_TYPE_ALLOC,
|
||||
GUF_ALLOC_FN_TYPE_REALLOC,
|
||||
|
||||
@ -8,7 +8,8 @@
|
||||
#include "guf_alloc.h"
|
||||
|
||||
typedef struct guf_libc_alloc_ctx {
|
||||
int alloc_type_id, thread_id;
|
||||
guf_alloc_meta meta;
|
||||
bool track_allocs;
|
||||
bool zero_init;
|
||||
} guf_libc_alloc_ctx;
|
||||
|
||||
|
||||
@ -1,14 +1,116 @@
|
||||
|
||||
#include "guf_common.h"
|
||||
#include "guf_alloc.h"
|
||||
#include "math.h"
|
||||
|
||||
typedef struct guf_alloc_info {
|
||||
const char *name;
|
||||
ptrdiff_t allocated_bytes, freed_bytes;
|
||||
typedef struct guf_alloc_tracker {
|
||||
FILE *log, *err_log;
|
||||
size_t alloc_count, realloc_count, free_count;
|
||||
} guf_alloc_info;
|
||||
ptrdiff_t allocated_bytes;
|
||||
uint32_t id;
|
||||
} guf_alloc_tracker;
|
||||
|
||||
static bool guf_track_alloc(guf_alloc_tracker *t, ptrdiff_t size)
|
||||
{
|
||||
GUF_ASSERT(t);
|
||||
GUF_ASSERT(size >= 0);
|
||||
bool success = true;
|
||||
if (t->err_log && t->alloc_count == SIZE_MAX) {
|
||||
fprintf(t->err_log, "guf_alloc_track (id %" PRIu32 ") WARNING: alloc_count overflow\n", t->id);
|
||||
//success = false;
|
||||
}
|
||||
t->alloc_count = guf_add_saturated_size_t(t->alloc_count, 1);
|
||||
|
||||
if (guf_add_is_overflow_ptrdiff(t->allocated_bytes, size)) {
|
||||
if (t->err_log) {
|
||||
fprintf(t->err_log, "guf_alloc_track (id %" PRIu32 ") ERROR: allocated_byte overflow\n", t->id);
|
||||
}
|
||||
success = false;
|
||||
}
|
||||
t->allocated_bytes = guf_add_saturated_ptrdiff(t->allocated_bytes, size);
|
||||
if (t->allocated_bytes < 0) {
|
||||
if (t->err_log) {
|
||||
fprintf(t->err_log, "guf_alloc_track (id %" PRIu32 ") ERROR: allocated_bytes < 0\n", t->id);
|
||||
}
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (t->log) {
|
||||
fprintf(t->log, "guf_alloc_track (id %" PRIu32 "): alloc (%td bytes) %s\n", t->id, size, success ? "SUCCESS" : "FAILURE");
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
static bool guf_track_realloc(guf_alloc_tracker *t, ptrdiff_t old_size, ptrdiff_t new_size)
|
||||
{
|
||||
GUF_ASSERT(t);
|
||||
GUF_ASSERT(old_size >= 0 && new_size >= 0);
|
||||
bool success = true;
|
||||
if (t->err_log && t->realloc_count == SIZE_MAX) {
|
||||
fprintf(t->err_log, "guf_realloc_track (id %" PRIu32 ") WARNING: realloc_count overflow\n");
|
||||
//success = false;
|
||||
}
|
||||
t->realloc_count = guf_add_saturated_size_t(t->realloc_count, 1);
|
||||
|
||||
if (old_size < 0 || new_size < 0) {
|
||||
if (t->err_log) {
|
||||
fprintf(t->err_log, "guf_realloc_track (id %" PRIu32 ") ERROR: old_size < 0 or new_size < 0\n");
|
||||
}
|
||||
success = false;
|
||||
}
|
||||
|
||||
t->allocated_bytes = guf_sub_saturated_ptrdiff(t->allocated_bytes, old_size);
|
||||
if (t->allocated_bytes < 0) {
|
||||
success = false;
|
||||
fprintf(t->err_log, "guf_realloc_track (id %" PRIu32 ") ERROR: allocated_bytes < 0 after subtracting old_size\n", t->id);
|
||||
}
|
||||
if (guf_add_is_overflow_ptrdiff(t->allocated_bytes, new_size)) {
|
||||
success = false;
|
||||
if (t->err_log) {
|
||||
fprintf(t->err_log, "guf_realloc_track (id %" PRIu32 ") ERROR: allocated_bytes overflow \n");
|
||||
}
|
||||
}
|
||||
t->allocated_bytes = guf_add_saturated_ptrdiff(t->allocated_bytes, new_size);
|
||||
|
||||
if (t->allocated_bytes < 0) {
|
||||
success = false;
|
||||
fprintf(t->err_log, "guf_realloc_track (id %" PRIu32 ") ERROR: allocated_bytes < 0 after adding new_size\n", t->id);
|
||||
}
|
||||
|
||||
if (t->log) {
|
||||
fprintf(t->log, "guf_realloc_track (id %" PRIu32 "): realloc (from %td to %td bytes) %s", t->id, old_size, new_size, (success ? "SUCCESS" : "FAILURE"));
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static bool guf_track_free(guf_alloc_tracker *t, ptrdiff_t size)
|
||||
{
|
||||
GUF_ASSERT(t);
|
||||
GUF_ASSERT(size >= 0);
|
||||
|
||||
if (t->err_log && t->free_count == SIZE_MAX) {
|
||||
fprintf(t->err_log, "guf_track_free (id %" PRIu32 ") WARNING: free_count overflow\n");
|
||||
}
|
||||
bool success = true;
|
||||
|
||||
if (size < 0) {
|
||||
success = false;
|
||||
if (t->err_log) {
|
||||
fprintf(t->err_log, "guf_track_free (id %" PRIu32 ") ERROR: size < 0\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (t->allocated_bytes < size) {
|
||||
success = false;
|
||||
if (t->err_log) {
|
||||
fprintf(t->err_log, "guf_track_free (id %" PRIu32 ") ERROR: freed more bytes than allocated\n");
|
||||
}
|
||||
}
|
||||
t->allocated_bytes = guf_sub_saturated_ptrdiff(t->allocated_bytes, size);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
// void guf_alloc_track(int type_id, int thread_id, ptrdiff_t size)
|
||||
// {
|
||||
// static guf_alloc_info guf_alloc_info[GUF_ALLOC_THREAD_ID_MAX][GUF_ALLOC_TYPE_ID_MAX];
|
||||
|
||||
// }
|
||||
|
||||
186
src/guf_math.h
186
src/guf_math.h
@ -22,56 +22,117 @@
|
||||
static inline uint64_t guf_rotl_u64(uint64_t x, int k) {return (x << k) | (x >> (64 - k));}
|
||||
static inline uint32_t guf_rotl_u32(uint32_t x, int k) {return (x << k) | (x >> (32 - k));}
|
||||
|
||||
#define GUF_DEFINE_MIN_MAX_CLAMP(int_type, int_type_name)\
|
||||
static inline int_type GUF_CAT(guf_min_, int_type_name)(int_type a, int_type b) {return a < b ? a : b;}\
|
||||
static inline int_type GUF_CAT(guf_max_, int_type_name)(int_type a, int_type b) {return a > b ? a : b;}\
|
||||
static inline int_type GUF_CAT(guf_clamp_, int_type_name)(int_type x, int_type min, int_type max) {if (x < min) {return min;} if (x > max) {return max;} return x;}
|
||||
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(char, char)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(int, int)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(long, long)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(long long, long_long)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(int8_t, i8)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(int16_t, i16)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(int32_t, i32)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(int64_t, i64)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(ptrdiff_t, ptrdiff_t)
|
||||
// Signed min, max, clamp functions (generated with libguf/tools/min_max_clamp-gen.py)
|
||||
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(unsigned char, uchar)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(unsigned, unsigned)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(unsigned long, ulong)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(unsigned long long, ulong_long)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(uint8_t, u8)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(uint16_t, u16)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(uint32_t, u32)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(uint64_t, u64)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(size_t, size_t)
|
||||
static inline char guf_min_char(char a, char b) { return a < b ? a : b; }
|
||||
static inline char guf_max_char(char a, char b) { return a > b ? a : b; }
|
||||
static inline char guf_clamp_char(char x, char min, char max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(float, f32)
|
||||
GUF_DEFINE_MIN_MAX_CLAMP(double, f64)
|
||||
static inline int guf_min_int(int a, int b) { return a < b ? a : b; }
|
||||
static inline int guf_max_int(int a, int b) { return a > b ? a : b; }
|
||||
static inline int guf_clamp_int(int x, int min, int max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
#undef GUF_DEFINE_MIN_MAX_CLAMP
|
||||
static inline long guf_min_long(long a, long b) { return a < b ? a : b; }
|
||||
static inline long guf_max_long(long a, long b) { return a > b ? a : b; }
|
||||
static inline long guf_clamp_long(long x, long min, long max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline int guf_abs_int(int x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT_MIN); return -x;} // I would not drink that...
|
||||
static inline int8_t guf_abs_i8 (int8_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT8_MIN); return -x;}
|
||||
static inline int16_t guf_abs_i16(int16_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT16_MIN); return -x;}
|
||||
static inline int32_t guf_abs_i32(int32_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT32_MIN); return -x;}
|
||||
static inline int64_t guf_abs_i64(int64_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > INT64_MIN); return -x;}
|
||||
static inline ptrdiff_t guf_abs_ptrdiff(ptrdiff_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(x > PTRDIFF_MIN); return -x;}
|
||||
static inline long long guf_min_long_long(long long a, long long b) { return a < b ? a : b; }
|
||||
static inline long long guf_max_long_long(long long a, long long b) { return a > b ? a : b; }
|
||||
static inline long long guf_clamp_long_long(long long x, long long min, long long max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline unsigned char guf_uabs_char(char x) {if (x >= 0) {return x;} else if (x == CHAR_MIN) {return (unsigned char)CHAR_MAX + 1;} else {return -x;}}
|
||||
static inline unsigned guf_uabs_int(int x) {if (x >= 0) {return x;} else if (x == INT_MIN) {return (unsigned)INT_MAX + 1;} 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 + 1;} 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 + 1;} 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 + 1;} 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 + 1;} else {return -x;}}
|
||||
static inline int8_t guf_min_i8(int8_t a, int8_t b) { return a < b ? a : b; }
|
||||
static inline int8_t guf_max_i8(int8_t a, int8_t b) { return a > b ? a : b; }
|
||||
static inline int8_t guf_clamp_i8(int8_t x, int8_t min, int8_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline int16_t guf_min_i16(int16_t a, int16_t b) { return a < b ? a : b; }
|
||||
static inline int16_t guf_max_i16(int16_t a, int16_t b) { return a > b ? a : b; }
|
||||
static inline int16_t guf_clamp_i16(int16_t x, int16_t min, int16_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline int32_t guf_min_i32(int32_t a, int32_t b) { return a < b ? a : b; }
|
||||
static inline int32_t guf_max_i32(int32_t a, int32_t b) { return a > b ? a : b; }
|
||||
static inline int32_t guf_clamp_i32(int32_t x, int32_t min, int32_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline int64_t guf_min_i64(int64_t a, int64_t b) { return a < b ? a : b; }
|
||||
static inline int64_t guf_max_i64(int64_t a, int64_t b) { return a > b ? a : b; }
|
||||
static inline int64_t guf_clamp_i64(int64_t x, int64_t min, int64_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline ptrdiff_t guf_min_ptrdiff_t(ptrdiff_t a, ptrdiff_t b) { return a < b ? a : b; }
|
||||
static inline ptrdiff_t guf_max_ptrdiff_t(ptrdiff_t a, ptrdiff_t b) { return a > b ? a : b; }
|
||||
static inline ptrdiff_t guf_clamp_ptrdiff_t(ptrdiff_t x, ptrdiff_t min, ptrdiff_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline float guf_min_f32(float a, float b) { return a < b ? a : b; }
|
||||
static inline float guf_max_f32(float a, float b) { return a > b ? a : b; }
|
||||
static inline float guf_clamp_f32(float x, float min, float max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline double guf_min_f64(double a, double b) { return a < b ? a : b; }
|
||||
static inline double guf_max_f64(double a, double b) { return a > b ? a : b; }
|
||||
static inline double guf_clamp_f64(double x, double min, double max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
// Unsigned min, max, clamp functions (generated with libguf/tools/min_max_clamp-gen.py)
|
||||
|
||||
static inline unsigned char guf_min_uchar(unsigned char a, unsigned char b) { return a < b ? a : b; }
|
||||
static inline unsigned char guf_max_uchar(unsigned char a, unsigned char b) { return a > b ? a : b; }
|
||||
static inline unsigned char guf_clamp_uchar(unsigned char x, unsigned char min, unsigned char max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline unsigned guf_min_unsigned(unsigned a, unsigned b) { return a < b ? a : b; }
|
||||
static inline unsigned guf_max_unsigned(unsigned a, unsigned b) { return a > b ? a : b; }
|
||||
static inline unsigned guf_clamp_unsigned(unsigned x, unsigned min, unsigned max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline unsigned long guf_min_ulong(unsigned long a, unsigned long b) { return a < b ? a : b; }
|
||||
static inline unsigned long guf_max_ulong(unsigned long a, unsigned long b) { return a > b ? a : b; }
|
||||
static inline unsigned long guf_clamp_ulong(unsigned long x, unsigned long min, unsigned long max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline unsigned long long guf_min_ulong_long(unsigned long long a, unsigned long long b) { return a < b ? a : b; }
|
||||
static inline unsigned long long guf_max_ulong_long(unsigned long long a, unsigned long long b) { return a > b ? a : b; }
|
||||
static inline unsigned long long guf_clamp_ulong_long(unsigned long long x, unsigned long long min, unsigned long long max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline uint8_t guf_min_u8(uint8_t a, uint8_t b) { return a < b ? a : b; }
|
||||
static inline uint8_t guf_max_u8(uint8_t a, uint8_t b) { return a > b ? a : b; }
|
||||
static inline uint8_t guf_clamp_u8(uint8_t x, uint8_t min, uint8_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline uint16_t guf_min_u16(uint16_t a, uint16_t b) { return a < b ? a : b; }
|
||||
static inline uint16_t guf_max_u16(uint16_t a, uint16_t b) { return a > b ? a : b; }
|
||||
static inline uint16_t guf_clamp_u16(uint16_t x, uint16_t min, uint16_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline uint32_t guf_min_u32(uint32_t a, uint32_t b) { return a < b ? a : b; }
|
||||
static inline uint32_t guf_max_u32(uint32_t a, uint32_t b) { return a > b ? a : b; }
|
||||
static inline uint32_t guf_clamp_u32(uint32_t x, uint32_t min, uint32_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline uint64_t guf_min_u64(uint64_t a, uint64_t b) { return a < b ? a : b; }
|
||||
static inline uint64_t guf_max_u64(uint64_t a, uint64_t b) { return a > b ? a : b; }
|
||||
static inline uint64_t guf_clamp_u64(uint64_t x, uint64_t min, uint64_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
static inline size_t guf_min_size_t(size_t a, size_t b) { return a < b ? a : b; }
|
||||
static inline size_t guf_max_size_t(size_t a, size_t b) { return a > b ? a : b; }
|
||||
static inline size_t guf_clamp_size_t(size_t x, size_t min, size_t max) { if (x < min) {return min;} if (x > max) {return max;} return x; }
|
||||
|
||||
|
||||
// abs functions with signed result (can fail/panic for abs(INT_TYPE_MIN) for platforms with two's complement signed ints; C2X for example guarantees two's complement)
|
||||
static inline int guf_abs_int(int x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT_MAX == INT_MIN || x > INT_MIN); return -x;} // I would not drink that...
|
||||
static inline int8_t guf_abs_i8 (int8_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT8_MAX == INT8_MIN || x > INT8_MIN); return -x;}
|
||||
static inline int16_t guf_abs_i16(int16_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT16_MAX == INT16_MIN || x > INT16_MIN); return -x;}
|
||||
static inline int32_t guf_abs_i32(int32_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT32_MAX == INT32_MIN || x > INT32_MIN); return -x;}
|
||||
static inline int64_t guf_abs_i64(int64_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-INT64_MAX == INT64_MIN || x > INT64_MIN); return -x;}
|
||||
static inline ptrdiff_t guf_abs_ptrdiff(ptrdiff_t x) {if (x >= 0) {return x;} GUF_ASSERT_RELEASE(-PTRDIFF_MAX == PTRDIFF_MIN || x > PTRDIFF_MIN); return -x;}
|
||||
|
||||
// abs functions with unsigned result functions (cannot fail)
|
||||
static inline unsigned guf_uabs_int(int x) {if (x >= 0) {return x;} else if (x == INT_MIN && -INT_MAX != INT_MIN) {return (unsigned)INT_MAX + 1;} else {return -x;}}
|
||||
static inline uint8_t guf_uabs_i8(int8_t x) {if (x >= 0) {return x;} else if (x == INT8_MIN && -INT8_MAX != INT8_MIN) {return (uint8_t)INT8_MAX + 1;} else {return -x;}}
|
||||
static inline uint16_t guf_uabs_i16(int16_t x) {if (x >= 0) {return x;} else if (x == INT16_MIN && -INT16_MAX != INT16_MIN) {return (uint16_t)INT16_MAX + 1;} else {return -x;}}
|
||||
static inline uint32_t guf_uabs_i32(int32_t x) {if (x >= 0) {return x;} else if (x == INT32_MIN && -INT32_MAX != INT32_MIN) {return (uint32_t)INT32_MAX + 1;} else {return -x;}}
|
||||
static inline uint64_t guf_uabs_i64(int64_t x) {if (x >= 0) {return x;} else if (x == INT64_MIN && -INT64_MAX != INT64_MIN) {return (uint64_t)INT64_MAX + 1;} 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 + 1;} else {return -x;}}
|
||||
|
||||
// absdiff functions with unsigned result (cannot fail)
|
||||
static inline unsigned char guf_absdiff_char(char a, char b) {return a > b ? (unsigned char)a - (unsigned char)b : (unsigned char)b - (unsigned char)a;}
|
||||
static inline unsigned guf_absdiff_int(int a, int b) {return a > b ? (unsigned)a - (unsigned)b : (unsigned)b - (unsigned)a;}
|
||||
static inline uint8_t guf_absdiff_i8(int8_t a, int8_t b) {return a > b ? (uint8_t)a - (uint8_t)b : (uint8_t)b - (uint8_t)a;}
|
||||
static inline uint16_t guf_absdiff_i16(int16_t a, int16_t b) {return a > b ? (uint16_t)a - (uint16_t)b : (uint16_t)b - (uint16_t)a;}
|
||||
static inline uint32_t guf_absdiff_i32(int32_t a, int32_t b) {return a > b ? (uint32_t)a - (uint32_t)b : (uint32_t)b - (uint32_t)a;}
|
||||
static inline uint64_t guf_absdiff_i64(int64_t a, int64_t b) {return a > b ? (uint64_t)a - (uint64_t)b : (uint64_t)b - (uint64_t)a;}
|
||||
static inline size_t guf_absdiff_ptrdiff_t(ptrdiff_t a, ptrdiff_t b) {return a > b ? (size_t)a - (size_t)b : (size_t)b - (size_t)a;}
|
||||
|
||||
|
||||
static inline bool guf_add_is_overflow_size_t(size_t a, size_t b)
|
||||
{
|
||||
@ -87,54 +148,6 @@ static inline bool guf_mul_is_overflow_size_t(size_t a, size_t b)
|
||||
return a != 0 && ((c / a) != b);
|
||||
}
|
||||
|
||||
static inline bool guf_add_is_overflow_u32(uint32_t a, uint32_t b)
|
||||
{
|
||||
return (a + b) < a;
|
||||
}
|
||||
static inline bool guf_sub_is_overflow_u32(uint32_t a, uint32_t b)
|
||||
{
|
||||
return (a - b) > a;
|
||||
}
|
||||
static inline bool guf_mul_is_overflow_u32(uint32_t a, uint32_t b)
|
||||
{
|
||||
const uint32_t c = a * b;
|
||||
return a != 0 && ((c / a) != b);
|
||||
}
|
||||
|
||||
// cf. https://stackoverflow.com/questions/199333/how-do-i-detect-unsigned-integer-overflow (last-retrieved 2025-03-17)
|
||||
static inline bool guf_add_is_overflow_ptrdiff(ptrdiff_t a, ptrdiff_t b)
|
||||
{
|
||||
return (b > 0 && a > PTRDIFF_MAX - b) || // a + b overflow
|
||||
(b < 0 && a < PTRDIFF_MIN - b); // a + b underflow
|
||||
}
|
||||
static inline bool guf_sub_is_overflow_ptrdiff(ptrdiff_t a, ptrdiff_t b)
|
||||
{
|
||||
return (b < 0 && a > PTRDIFF_MAX + b) || // a - b overflow
|
||||
(b > 0 && a < PTRDIFF_MIN + b); // a - b underflow
|
||||
}
|
||||
|
||||
static inline bool guf_add_is_overflow_i32(int32_t a, int32_t b)
|
||||
{
|
||||
return (b > 0 && a > INT32_MAX - b) || // a + b overflow
|
||||
(b < 0 && a < INT32_MIN - b); // a + b underflow
|
||||
}
|
||||
static inline bool guf_sub_is_overflow_i32(int32_t a, int32_t b)
|
||||
{
|
||||
return (b < 0 && a > INT32_MAX + b) || // a - b overflow
|
||||
(b > 0 && a < INT32_MIN + b); // a - b underflow
|
||||
}
|
||||
|
||||
static inline bool guf_add_is_overflow_i64(int64_t a, int64_t b)
|
||||
{
|
||||
return (b > 0 && a > INT64_MAX - b) || // a + b overflow
|
||||
(b < 0 && a < INT64_MIN - b); // a + b underflow
|
||||
}
|
||||
static inline bool guf_sub_is_overflow_i64(int64_t a, int64_t b)
|
||||
{
|
||||
return (b < 0 && a > INT64_MAX + b) || // a - b overflow
|
||||
(b > 0 && a < INT64_MIN + b); // a - b underflow
|
||||
}
|
||||
|
||||
static inline bool guf_size_calc_safe(ptrdiff_t count, ptrdiff_t sizeof_elem, ptrdiff_t *result)
|
||||
{
|
||||
if (count < 0 || sizeof_elem <= 0) {
|
||||
@ -152,6 +165,7 @@ static inline bool guf_size_calc_safe(ptrdiff_t count, ptrdiff_t sizeof_elem, pt
|
||||
return is_safe;
|
||||
}
|
||||
|
||||
|
||||
// cf. https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 (last-retrieved 2025-03-19)
|
||||
static inline bool guf_is_pow2_u8(uint8_t x) { return x && !(x & (x - 1)); }
|
||||
static inline bool guf_is_pow2_u16(uint16_t x) { return x && !(x & (x - 1)); }
|
||||
@ -159,9 +173,9 @@ static inline bool guf_is_pow2_u32(uint32_t x) { return x && !(x & (x - 1)); }
|
||||
static inline bool guf_is_pow2_u64(uint64_t x) { return x && !(x & (x - 1)); }
|
||||
static inline bool guf_is_pow2_size_t(size_t x) { return x && !(x & (x - 1)); }
|
||||
|
||||
|
||||
static bool guf_nearly_zero_f32(float x, float eps) {return fabsf(x) <= eps;}
|
||||
static bool guf_nearly_one_f32(float x, float eps) {return fabsf(x - 1) <= eps;}
|
||||
|
||||
static bool guf_nearly_zero_f64(double x, double eps) {return fabs(x) <= eps;}
|
||||
static bool guf_nearly_one_f64(double x, double eps) {return fabs(x - 1) <= eps;}
|
||||
|
||||
|
||||
1203
src/guf_math_ckdint.h
Normal file
1203
src/guf_math_ckdint.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -228,6 +228,7 @@ GUF_RAND_KWRDS double guf_rand32_normal_sample_one_f64(guf_rand32_state *state,
|
||||
#include "guf_common.h"
|
||||
#include "guf_assert.h"
|
||||
#include "guf_math.h"
|
||||
#include "guf_math_ckdint.h"
|
||||
|
||||
#ifdef UINT64_MAX
|
||||
/*
|
||||
@ -711,7 +712,8 @@ GUF_RAND_KWRDS int32_t guf_rand32_range_i32(guf_rand32_state *state, int32_t min
|
||||
} while (step >= limit);
|
||||
step = step / bin_size;
|
||||
|
||||
GUF_ASSERT(!guf_add_is_overflow_i32(min, step));
|
||||
|
||||
GUF_ASSERT(guf_ckd_add_i32(min, step) == GUF_MATH_CKD_SUCCESS);
|
||||
const int32_t rnd = min + (int32_t)step;
|
||||
GUF_ASSERT(rnd >= min && rnd <= max);
|
||||
return rnd;
|
||||
@ -805,7 +807,7 @@ GUF_RAND_KWRDS uint32_t guf_randrange_u32(guf_randstate *state, uint32_t min, ui
|
||||
} while (step >= limit);
|
||||
step = step / bin_size;
|
||||
|
||||
GUF_ASSERT(!guf_add_is_overflow_i64(min, step));
|
||||
GUF_ASSERT(guf_ckd_add_i64(min, step) == GUF_MATH_CKD_SUCCESS);
|
||||
const int64_t rnd = min + (int64_t)step;
|
||||
GUF_ASSERT(rnd >= min && rnd <= max);
|
||||
return rnd;
|
||||
|
||||
@ -238,6 +238,7 @@ GUF_STR_KWRDS bool guf_str_is_valid(const guf_str *str);
|
||||
|
||||
#include "guf_common.h"
|
||||
#include "guf_math.h"
|
||||
#include "guf_math_ckdint.h"
|
||||
#include <string.h>
|
||||
|
||||
#ifdef GUF_STR_IMPL
|
||||
@ -1131,7 +1132,8 @@ GUF_STR_KWRDS guf_str *guf_str_try_substr(guf_str *str, ptrdiff_t pos, ptrdiff_t
|
||||
}
|
||||
GUF_ASSERT(c_str);
|
||||
|
||||
const ptrdiff_t pos_plus_count = guf_add_is_overflow_ptrdiff(pos, count) ? PTRDIFF_MAX : pos + count;
|
||||
ptrdiff_t pos_plus_count = 0;
|
||||
guf_saturating_add_ptrdiff_t(pos, count, &pos_plus_count);
|
||||
const ptrdiff_t substr_len = pos_plus_count > len ? len - pos : count;
|
||||
GUF_ASSERT(substr_len >= 0 && substr_len <= len && substr_len <= guf_str_capacity(str));
|
||||
GUF_ASSERT((size_t)pos + (size_t)(substr_len) <= (size_t)len); // [*]
|
||||
@ -1419,7 +1421,8 @@ GUF_STR_KWRDS guf_str_view guf_str_view_substr(guf_str_view str, ptrdiff_t pos,
|
||||
return (guf_str_view){.str = str.str, .len = 0};
|
||||
}
|
||||
|
||||
const ptrdiff_t pos_plus_count = guf_add_is_overflow_ptrdiff(pos, count) ? PTRDIFF_MAX : pos + count;
|
||||
ptrdiff_t pos_plus_count = 0;
|
||||
guf_saturating_add_ptrdiff_t(pos, count, &pos_plus_count);
|
||||
const ptrdiff_t substr_len = pos_plus_count > str.len ? str.len - pos : count;
|
||||
GUF_ASSERT(substr_len >= 0);
|
||||
GUF_ASSERT(substr_len <= str.len);
|
||||
|
||||
@ -59,7 +59,7 @@ int main(void)
|
||||
guf_platform_assert_native_word_bits();
|
||||
|
||||
guf_allocator test_allocator = guf_allocator_libc;
|
||||
guf_libc_alloc_ctx test_allocator_ctx = {.alloc_type_id = 0, .thread_id = 0, .zero_init = true};
|
||||
guf_libc_alloc_ctx test_allocator_ctx = {.zero_init = true, .track_allocs = false};
|
||||
test_allocator.ctx = &test_allocator_ctx;
|
||||
|
||||
dict_cstr_int ht;
|
||||
|
||||
26
todo.txt
26
todo.txt
@ -1,3 +1,8 @@
|
||||
- abs functions with signed result assume two's complement (x)
|
||||
- abs functions with unsigned result assume two's complement (x)
|
||||
|
||||
- header guards for optional int64_t types maybe...
|
||||
|
||||
- sort: add cpp #ifdef to remove restrict from declaration
|
||||
|
||||
- separate impl and headers from tests (for compile perf)
|
||||
@ -15,27 +20,6 @@
|
||||
- each thread needs its own alloc and alloc_ctx; don't track granular, give each allocator it's unique id maybe?
|
||||
|
||||
- potential idea for alloc: instead of using a pointer (8 or 4 bytes), use a 2 byte id to save space (and avoid dangling pointers):
|
||||
typedef guf_allocator_id uint16_t; // allows for up to (UINT16_MAX - 1) allocators (and 0 for NULL)
|
||||
guf_allocator_dbuf guf_global_allocators;
|
||||
// cf. https://github.com/erincatto/box2d/blob/main/src/id_pool.c
|
||||
typedef struct guf_allocator_id_pool { // Implement generically with macro "templates"
|
||||
guf_allocator_id_dbuf free_ids;
|
||||
guf_allocator_id next_free_id;
|
||||
} guf_allocator_id_pool;
|
||||
|
||||
guf_allocator_id_pool guf_global_allocator_id_pool;
|
||||
|
||||
guf_allocator_id guf_register_allocator(guf_allocator allocator) {
|
||||
guf_allocator_id id = get_free_id(&guf_global_allocator_id_pool);
|
||||
if (!id) {
|
||||
return 0;
|
||||
}
|
||||
guf_global_allocators[id] = allocator;
|
||||
}
|
||||
|
||||
guf_allocator *guf_get_allocator(guf_allocator_id id) {
|
||||
return (id == 0 || id >= UINT16_MAX) ? NULL : (guf_global_allocators + id);
|
||||
}
|
||||
|
||||
- no guf_init.h
|
||||
|
||||
|
||||
253
tools/ckdint-gen.py
Normal file
253
tools/ckdint-gen.py
Normal file
@ -0,0 +1,253 @@
|
||||
"""
|
||||
Generate typesafe checked arithmetic functions for libguf/src/guf_math_ckdint.h
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
import textwrap
|
||||
|
||||
@dataclass
|
||||
class IntType:
|
||||
INT_TYPE: str
|
||||
INT_TYPE_ABBR: str
|
||||
INT_MIN: str
|
||||
INT_MAX: str
|
||||
|
||||
@dataclass
|
||||
class UintType:
|
||||
INT_TYPE: str
|
||||
INT_TYPE_ABBR: str
|
||||
INT_MIN: str
|
||||
INT_MAX: str
|
||||
|
||||
def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
|
||||
|
||||
ckd_add_sub_uint = textwrap.dedent("""
|
||||
static inline guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b)
|
||||
{{
|
||||
if (b > 0 && a > {int_max} - b) {{
|
||||
return GUF_MATH_CKD_OVERFLOW;
|
||||
}} else {{
|
||||
return GUF_MATH_CKD_SUCCESS;
|
||||
}}
|
||||
}}
|
||||
static inline guf_math_ckd_result guf_ckd_sub_{type_abbr}({type} a, {type} b)
|
||||
{{
|
||||
if (b > a) {{
|
||||
return GUF_MATH_CKD_UNDERFLOW;
|
||||
}} else {{
|
||||
return GUF_MATH_CKD_SUCCESS;
|
||||
}}
|
||||
}}
|
||||
""")
|
||||
|
||||
ckd_add_sub_int = textwrap.dedent("""
|
||||
static inline guf_math_ckd_result guf_ckd_add_{type_abbr}({type} a, {type} b)
|
||||
{{
|
||||
if (b > 0 && a > {int_max} - b) {{
|
||||
return GUF_MATH_CKD_OVERFLOW;
|
||||
}} else if (b < 0 && a < {int_min} - b) {{
|
||||
return GUF_MATH_CKD_UNDERFLOW;
|
||||
}} else {{
|
||||
return GUF_MATH_CKD_SUCCESS;
|
||||
}}
|
||||
}}
|
||||
static inline guf_math_ckd_result guf_ckd_sub_{type_abbr}({type} a, {type} b)
|
||||
{{
|
||||
if (b < 0 && a > {int_max} + b) {{
|
||||
return GUF_MATH_CKD_OVERFLOW;
|
||||
}} else if (b > 0 && a < {int_min} + b) {{
|
||||
return GUF_MATH_CKD_UNDERFLOW;
|
||||
}} else {{
|
||||
return GUF_MATH_CKD_SUCCESS;
|
||||
}}
|
||||
}}
|
||||
""")
|
||||
|
||||
saturating_wrapping_int = textwrap.dedent("""
|
||||
static inline guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result)
|
||||
{{
|
||||
const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b);
|
||||
if (result) {{
|
||||
switch (check) {{
|
||||
case GUF_MATH_CKD_SUCCESS:
|
||||
*result = a + b;
|
||||
break;
|
||||
case GUF_MATH_CKD_OVERFLOW:
|
||||
*result = {int_max};
|
||||
break;
|
||||
case GUF_MATH_CKD_UNDERFLOW:
|
||||
*result = {int_min};
|
||||
break;
|
||||
default:
|
||||
GUF_ASSERT(false);
|
||||
}}
|
||||
}}
|
||||
return check;
|
||||
}}
|
||||
static inline guf_math_ckd_result guf_saturating_sub_{type_abbr}({type} a, {type} b, {type} *result)
|
||||
{{
|
||||
const guf_math_ckd_result check = guf_ckd_sub_{type_abbr}(a, b);
|
||||
if (result) {{
|
||||
switch (check) {{
|
||||
case GUF_MATH_CKD_SUCCESS:
|
||||
*result = a - b;
|
||||
break;
|
||||
case GUF_MATH_CKD_OVERFLOW:
|
||||
*result = {int_max};
|
||||
break;
|
||||
case GUF_MATH_CKD_UNDERFLOW:
|
||||
*result = {int_min};
|
||||
break;
|
||||
default:
|
||||
GUF_ASSERT(false);
|
||||
}}
|
||||
}}
|
||||
return check;
|
||||
}}
|
||||
|
||||
static inline guf_math_ckd_result guf_wrapping_add_{type_abbr}({type} a, {type} b, {type} *result)
|
||||
{{
|
||||
const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b);
|
||||
if (result) {{
|
||||
switch (check) {{
|
||||
case GUF_MATH_CKD_SUCCESS:
|
||||
*result = a + b;
|
||||
break;
|
||||
case GUF_MATH_CKD_OVERFLOW:
|
||||
*result = (a + {int_min}) + (b + {int_min});
|
||||
break;
|
||||
case GUF_MATH_CKD_UNDERFLOW:
|
||||
*result = (a - {int_min}) + (b - {int_min});
|
||||
break;
|
||||
default:
|
||||
GUF_ASSERT(false);
|
||||
}}
|
||||
}}
|
||||
return check;
|
||||
}}
|
||||
static inline guf_math_ckd_result guf_wrapping_sub_{type_abbr}({type} a, {type} b, {type} *result)
|
||||
{{
|
||||
const guf_math_ckd_result check = guf_ckd_sub_{type_abbr}(a, b);
|
||||
if (result) {{
|
||||
switch (check) {{
|
||||
case GUF_MATH_CKD_SUCCESS:
|
||||
*result = a - b;
|
||||
break;
|
||||
case GUF_MATH_CKD_OVERFLOW:
|
||||
GUF_ASSERT(b < 0);
|
||||
*result = (a + {int_min}) - (b - {int_min}); // TODO: not sure
|
||||
break;
|
||||
case GUF_MATH_CKD_UNDERFLOW:
|
||||
GUF_ASSERT(b > 0);
|
||||
*result = (a - {int_min}) - (b + {int_min}); // TODO: not sure
|
||||
break;
|
||||
default:
|
||||
GUF_ASSERT(false);
|
||||
}}
|
||||
}}
|
||||
return check;
|
||||
}}
|
||||
""")
|
||||
|
||||
saturating_wrapping_uint = textwrap.dedent("""
|
||||
static inline guf_math_ckd_result guf_saturating_add_{type_abbr}({type} a, {type} b, {type} *result)
|
||||
{{
|
||||
const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b);
|
||||
GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_OVERFLOW);
|
||||
if (result) {{
|
||||
switch (check) {{
|
||||
case GUF_MATH_CKD_SUCCESS:
|
||||
*result = a + b;
|
||||
break;
|
||||
case GUF_MATH_CKD_OVERFLOW:
|
||||
*result = {int_max};
|
||||
break;
|
||||
default:
|
||||
GUF_ASSERT(false);
|
||||
}}
|
||||
}}
|
||||
return check;
|
||||
}}
|
||||
static inline guf_math_ckd_result guf_saturating_sub_{type_abbr}({type} a, {type} b, {type} *result)
|
||||
{{
|
||||
const guf_math_ckd_result check = guf_ckd_sub_{type_abbr}(a, b);
|
||||
GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_UNDERFLOW);
|
||||
if (result) {{
|
||||
switch (check) {{
|
||||
case GUF_MATH_CKD_SUCCESS:
|
||||
*result = a - b;
|
||||
break;
|
||||
case GUF_MATH_CKD_UNDERFLOW:
|
||||
*result = 0;
|
||||
break;
|
||||
default:
|
||||
GUF_ASSERT(false);
|
||||
}}
|
||||
}}
|
||||
return check;
|
||||
}}
|
||||
|
||||
static inline guf_math_ckd_result guf_wrapping_add_{type_abbr}({type} a, {type} b, {type} *result)
|
||||
{{
|
||||
const guf_math_ckd_result check = guf_ckd_add_{type_abbr}(a, b);
|
||||
if (result) {{
|
||||
*result = a + b;
|
||||
}}
|
||||
return check;
|
||||
}}
|
||||
static inline guf_math_ckd_result guf_wrapping_sub_{type_abbr}({type} a, {type} b, {type} *result)
|
||||
{{
|
||||
const guf_math_ckd_result check = guf_ckd_sub_{type_abbr}(a, b);
|
||||
if (result) {{
|
||||
*result = a - b;
|
||||
}}
|
||||
return check;
|
||||
}}
|
||||
""")
|
||||
|
||||
text_result = "// Signed integer add/sub checks (generated with libguf/tools/ckdint-gen.py)\n"
|
||||
for type in int_types:
|
||||
text_result += ckd_add_sub_int.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX)
|
||||
|
||||
text_result += "\n// Unsigned integer add/sub checks (generated with libguf/tools/ckdint-gen.py) \n"
|
||||
for type in uint_types:
|
||||
text_result += ckd_add_sub_uint.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX)
|
||||
|
||||
|
||||
text_result += "\n\n// Signed saturating/wrapping add/sub (generated with libguf/tools/ckdint-gen.py)\n"
|
||||
for type in int_types:
|
||||
text_result += saturating_wrapping_int.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) + "\n"
|
||||
|
||||
text_result += "\n// Unsigned saturating/wrapping add/sub (generated with libguf/tools/ckdint-gen.py)\n"
|
||||
for type in uint_types:
|
||||
text_result += saturating_wrapping_uint.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) + "\n"
|
||||
|
||||
return text_result
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
int_types = [
|
||||
IntType(INT_TYPE = "int", INT_TYPE_ABBR = "int", INT_MIN = "INT_MIN", INT_MAX = "INT_MAX"),
|
||||
IntType(INT_TYPE = "int8_t", INT_TYPE_ABBR = "i8", INT_MIN = "INT8_MIN", INT_MAX = "INT8_MAX"),
|
||||
IntType(INT_TYPE = "int16_t", INT_TYPE_ABBR = "i16", INT_MIN = "INT16_MIN", INT_MAX = "INT16_MAX"),
|
||||
IntType(INT_TYPE = "int32_t", INT_TYPE_ABBR = "i32", INT_MIN = "INT32_MIN", INT_MAX = "INT32_MAX"),
|
||||
IntType(INT_TYPE = "int64_t", INT_TYPE_ABBR = "i64", INT_MIN = "INT64_MIN", INT_MAX = "INT64_MAX"),
|
||||
IntType(INT_TYPE = "ptrdiff_t", INT_TYPE_ABBR = "ptrdiff_t", INT_MIN = "PTRDIFF_MIN", INT_MAX = "PTRDIFF_MAX"),
|
||||
]
|
||||
|
||||
uint_types = [
|
||||
UintType(INT_TYPE = "unsigned char", INT_TYPE_ABBR = "uchar", INT_MIN = "0", INT_MAX = "UCHAR_MAX"),
|
||||
UintType(INT_TYPE = "unsigned", INT_TYPE_ABBR = "unsigned", INT_MIN = "0", INT_MAX = "UINT_MAX"),
|
||||
UintType(INT_TYPE = "uint8_t", INT_TYPE_ABBR = "u8", INT_MIN = "0", INT_MAX = "UINT8_MAX"),
|
||||
UintType(INT_TYPE = "uint16_t", INT_TYPE_ABBR = "u16", INT_MIN = "0", INT_MAX = "UINT16_MAX"),
|
||||
UintType(INT_TYPE = "uint32_t", INT_TYPE_ABBR = "u32", INT_MIN = "0", INT_MAX = "UINT32_MAX"),
|
||||
UintType(INT_TYPE = "uint64_t", INT_TYPE_ABBR = "u64", INT_MIN = "0", INT_MAX = "UINT64_MAX"),
|
||||
UintType(INT_TYPE = "size_t", INT_TYPE_ABBR = "size_t", INT_MIN = "0", INT_MAX = "SIZE_MAX"),
|
||||
]
|
||||
|
||||
ckd_fun_code = generate_ckdint_functions(int_types = int_types, uint_types= uint_types)
|
||||
|
||||
print(ckd_fun_code)
|
||||
|
||||
77
tools/min_max_clamp-gen.py
Normal file
77
tools/min_max_clamp-gen.py
Normal file
@ -0,0 +1,77 @@
|
||||
"""
|
||||
Generate typesafe min, max and clamp functions for libguf/src/guf_math.h
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
import textwrap
|
||||
|
||||
@dataclass
|
||||
class IntType:
|
||||
INT_TYPE: str
|
||||
INT_TYPE_ABBR: str
|
||||
INT_MIN: str
|
||||
INT_MAX: str
|
||||
|
||||
@dataclass
|
||||
class UintType:
|
||||
INT_TYPE: str
|
||||
INT_TYPE_ABBR: str
|
||||
INT_MIN: str
|
||||
INT_MAX: str
|
||||
|
||||
|
||||
def gen_min_max_clamp(int_types: list, uint_types: list) -> str:
|
||||
template = textwrap.dedent("""
|
||||
static inline {type} guf_min_{type_abbr}({type} a, {type} b) {{ return a < b ? a : b; }}
|
||||
static inline {type} guf_max_{type_abbr}({type} a, {type} b) {{ return a > b ? a : b; }}
|
||||
static inline {type} guf_clamp_{type_abbr}({type} x, {type} min, {type} max) {{ if (x < min) {{return min;}} if (x > max) {{return max;}} return x; }}
|
||||
""")
|
||||
|
||||
result = "\n// Signed min, max, clamp functions (generated with libguf/tools/min_max_clamp-gen.py)\n"
|
||||
for t in int_types:
|
||||
result += template.format(type = t.INT_TYPE, type_abbr = t.INT_TYPE_ABBR)
|
||||
|
||||
result += "\n// Unsigned min, max, clamp functions (generated with libguf/tools/min_max_clamp-gen.py)\n"
|
||||
for t in uint_types:
|
||||
result += template.format(type = t.INT_TYPE, type_abbr = t.INT_TYPE_ABBR)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
int_types = [
|
||||
IntType(INT_TYPE = "char", INT_TYPE_ABBR = "char", INT_MIN = "CHAR_MIN", INT_MAX = "CHAR_MAX"),
|
||||
IntType(INT_TYPE = "int", INT_TYPE_ABBR = "int", INT_MIN = "INT_MIN", INT_MAX = "INT_MAX"),
|
||||
IntType(INT_TYPE = "long", INT_TYPE_ABBR = "long", INT_MIN = "LONG_MIN", INT_MAX = "LONG_MAX"),
|
||||
IntType(INT_TYPE = "long long", INT_TYPE_ABBR = "long_long", INT_MIN = "LLONG_MIN", INT_MAX = "LLONG_MAX"),
|
||||
|
||||
IntType(INT_TYPE = "int8_t", INT_TYPE_ABBR = "i8", INT_MIN = "INT8_MIN", INT_MAX = "INT8_MAX"),
|
||||
IntType(INT_TYPE = "int16_t", INT_TYPE_ABBR = "i16", INT_MIN = "INT16_MIN", INT_MAX = "INT16_MAX"),
|
||||
IntType(INT_TYPE = "int32_t", INT_TYPE_ABBR = "i32", INT_MIN = "INT32_MIN", INT_MAX = "INT32_MAX"),
|
||||
IntType(INT_TYPE = "int64_t", INT_TYPE_ABBR = "i64", INT_MIN = "INT64_MIN", INT_MAX = "INT64_MAX"),
|
||||
IntType(INT_TYPE = "ptrdiff_t", INT_TYPE_ABBR = "ptrdiff_t", INT_MIN = "PTRDIFF_MIN", INT_MAX = "PTRDIFF_MAX"),
|
||||
|
||||
IntType(INT_TYPE = "float", INT_TYPE_ABBR = "f32", INT_MIN = "-FLT_MAX", INT_MAX = "FLT_MAX"),
|
||||
IntType(INT_TYPE = "double", INT_TYPE_ABBR = "f64", INT_MIN = "-DBL_MAX", INT_MAX = "DBL_MAX"),
|
||||
]
|
||||
|
||||
uint_types = [
|
||||
UintType(INT_TYPE = "unsigned char", INT_TYPE_ABBR = "uchar", INT_MIN = "0", INT_MAX = "UCHAR_MAX"),
|
||||
UintType(INT_TYPE = "unsigned", INT_TYPE_ABBR = "unsigned", INT_MIN = "0", INT_MAX = "UINT_MAX"),
|
||||
UintType(INT_TYPE = "unsigned long", INT_TYPE_ABBR = "ulong", INT_MIN = "0", INT_MAX = "ULONG_MAX"),
|
||||
UintType(INT_TYPE = "unsigned long long", INT_TYPE_ABBR = "ulong_long", INT_MIN = "0", INT_MAX = "ULLONG_MAX"),
|
||||
|
||||
UintType(INT_TYPE = "uint8_t", INT_TYPE_ABBR = "u8", INT_MIN = "0", INT_MAX = "UINT8_MAX"),
|
||||
UintType(INT_TYPE = "uint16_t", INT_TYPE_ABBR = "u16", INT_MIN = "0", INT_MAX = "UINT16_MAX"),
|
||||
UintType(INT_TYPE = "uint32_t", INT_TYPE_ABBR = "u32", INT_MIN = "0", INT_MAX = "UINT32_MAX"),
|
||||
UintType(INT_TYPE = "uint64_t", INT_TYPE_ABBR = "u64", INT_MIN = "0", INT_MAX = "UINT64_MAX"),
|
||||
UintType(INT_TYPE = "size_t", INT_TYPE_ABBR = "size_t", INT_MIN = "0", INT_MAX = "SIZE_MAX"),
|
||||
]
|
||||
|
||||
|
||||
code = gen_min_max_clamp(int_types=int_types, uint_types=uint_types)
|
||||
print(code)
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user