Add checked mul arithmetic

This commit is contained in:
jun 2025-05-13 12:42:59 +02:00
parent 9c417d2aa1
commit 873cdf20b1
3 changed files with 711 additions and 10 deletions

View File

@ -18,6 +18,11 @@ typedef enum guf_math_ckd_result {GUF_MATH_CKD_SUCCESS = 0, GUF_MATH_CKD_OVERFLO
- if a - b overflows TYPE: return GUF_MATH_CKD_OVERFLOW (truthy) - if a - b overflows TYPE: return GUF_MATH_CKD_OVERFLOW (truthy)
- if a - b underflows TYPE: return GUF_MATH_CKD_UNDERFLOW (truthy) - if a - b underflows TYPE: return GUF_MATH_CKD_UNDERFLOW (truthy)
guf_math_ckd_result guf_ckd_mul_TYPE(TYPE a, TYPE b);
- if a * b doesn't over/underflow TYPE: return GUF_MATH_CKD_SUCCESS (falsy)
- if a * b overflows TYPE: return GUF_MATH_CKD_OVERFLOW (truthy)
- if a * b underflows TYPE: return GUF_MATH_CKD_UNDERFLOW (truthy)
// Functions for safely computing arithmetic operations with saturating over- and underflow semantics // Functions for safely computing arithmetic operations with saturating over- and underflow semantics
// (cf. https://doc.rust-lang.org/std/intrinsics/fn.saturating_add.html (last-retrieved 2025-05-10)) // (cf. https://doc.rust-lang.org/std/intrinsics/fn.saturating_add.html (last-retrieved 2025-05-10))
@ -32,6 +37,11 @@ typedef enum guf_math_ckd_result {GUF_MATH_CKD_SUCCESS = 0, GUF_MATH_CKD_OVERFLO
- if a - b overflows TYPE: return GUF_MATH_CKD_OVERFLOW (if result is not NULL, set result to TYPE_MAX, i.e. use saturating overflow semantics). - if a - b overflows TYPE: return GUF_MATH_CKD_OVERFLOW (if result is not NULL, set result to TYPE_MAX, i.e. use saturating overflow semantics).
- if a - b underflows TYPE: return GUF_MATH_CKD_UNDERFLOW (if result is not NULL, set result to TYPE_MIN, i.e. use saturating underflow semantics). - if a - b underflows TYPE: return GUF_MATH_CKD_UNDERFLOW (if result is not NULL, set result to TYPE_MIN, i.e. use saturating underflow semantics).
guf_math_ckd_result guf_saturating_mul_TYPE(TYPE a, TYPE b, TYPE *result);
- if a * b does not over-/underflow TYPE: return GUF_MATH_CKD_SUCCESS (if result is not NULL, set result to a * b)
- if a * b overflows TYPE: return GUF_MATH_CKD_OVERFLOW (if result is not NULL, set result to TYPE_MAX, i.e. use saturating overflow semantics).
- if a * b underflows TYPE: return GUF_MATH_CKD_UNDERFLOW (if result is not NULL, set result to TYPE_MIN, i.e. use saturating underflow semantics).
// Functions for safely computing arithmetic operations with wrapping over- and underflow semantics // Functions for safely computing arithmetic operations with wrapping over- and underflow semantics
// (cf. https://doc.rust-lang.org/std/intrinsics/fn.wrapping_add.html (last-retrieved 2025-05-10)) // (cf. https://doc.rust-lang.org/std/intrinsics/fn.wrapping_add.html (last-retrieved 2025-05-10))
@ -46,9 +56,18 @@ typedef enum guf_math_ckd_result {GUF_MATH_CKD_SUCCESS = 0, GUF_MATH_CKD_OVERFLO
- if a - b overflows TYPE: return GUF_MATH_CKD_OVERFLOW (if result is not NULL, set result using two's complement wrap-around overflow semantics). - if a - b overflows TYPE: return GUF_MATH_CKD_OVERFLOW (if result is not NULL, set result using two's complement wrap-around overflow semantics).
- if a - b underflows TYPE: return GUF_MATH_CKD_UNDERFLOW (if result is not NULL, set result using two's complement wrap-around underflow semantics). - if a - b underflows TYPE: return GUF_MATH_CKD_UNDERFLOW (if result is not NULL, set result using two's complement wrap-around underflow semantics).
guf_math_ckd_result guf_wrapping_mul_TYPE(TYPE a, TYPE b, TYPE *result);
// NOTE/TODO: guf_wrapping_mul_TYPE relies on implementation-defined unsigned-to-signed two's complement conversion on over/underflow
// cf. https://stackoverflow.com/questions/76900522/can-you-ensure-overflow-wrapping-behavior-for-signed-integer-arithmetic-in-c (last-retrieved 2025-05-13)
- if a * b doesn't over-/underflow TYPE: return GUF_MATH_CKD_SUCCESS (if result is not NULL, set result to a * b)
- if a * b overflows TYPE: return GUF_MATH_CKD_OVERFLOW (if result is not NULL, set result using two's complement wrap-around overflow semantics).
- if a * b underflows TYPE: return GUF_MATH_CKD_UNDERFLOW (if result is not NULL, set result using two's complement wrap-around underflow semantics).
cf. https://stackoverflow.com/questions/199333/how-do-i-detect-unsigned-integer-overflow (last-retrieved 2025-03-17) cf. https://stackoverflow.com/questions/199333/how-do-i-detect-unsigned-integer-overflow (last-retrieved 2025-03-17)
cf. https://stackoverflow.com/questions/59307930/how-to-implement-wrapping-signed-int-addition-in-c (last-retrieved 2025-05-10) cf. https://stackoverflow.com/questions/59307930/how-to-implement-wrapping-signed-int-addition-in-c (last-retrieved 2025-05-10)
cf. https://stackoverflow.com/questions/54318815/integer-overflow-w-multiplication-in-c (last-retrieved 2025-05-11)
cf. https://stackoverflow.com/questions/29808397/how-to-portably-find-out-minint-max-absint-min (last-retrieved 2025-05-11)
*/ */
@ -74,6 +93,30 @@ static inline guf_math_ckd_result guf_ckd_sub_int(int a, int b)
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
} }
} }
static inline guf_math_ckd_result guf_ckd_mul_int(int a, int b)
{
if (b > 0) {
if (a > INT_MAX / b) {
return GUF_MATH_CKD_OVERFLOW;
} else if (a < INT_MIN / b) {
return GUF_MATH_CKD_UNDERFLOW;
} else {
return GUF_MATH_CKD_SUCCESS;
}
} else if (b < 0) {
if (INT_MIN != -INT_MAX && b == -1) { // Prevent potential (INT_MIN / b) overflow for b == -1
return a == INT_MIN ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
} else if (a < INT_MAX / b) {
return GUF_MATH_CKD_OVERFLOW;
} else if (a > INT_MIN / b) {
return GUF_MATH_CKD_UNDERFLOW;
} else {
return GUF_MATH_CKD_SUCCESS;
}
} else {
return GUF_MATH_CKD_SUCCESS;
}
}
static inline guf_math_ckd_result guf_ckd_add_i8(int8_t a, int8_t b) static inline guf_math_ckd_result guf_ckd_add_i8(int8_t a, int8_t b)
{ {
@ -95,6 +138,30 @@ static inline guf_math_ckd_result guf_ckd_sub_i8(int8_t a, int8_t b)
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
} }
} }
static inline guf_math_ckd_result guf_ckd_mul_i8(int8_t a, int8_t b)
{
if (b > 0) {
if (a > INT8_MAX / b) {
return GUF_MATH_CKD_OVERFLOW;
} else if (a < INT8_MIN / b) {
return GUF_MATH_CKD_UNDERFLOW;
} else {
return GUF_MATH_CKD_SUCCESS;
}
} else if (b < 0) {
if (INT8_MIN != -INT8_MAX && b == -1) { // Prevent potential (INT8_MIN / b) overflow for b == -1
return a == INT8_MIN ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
} else if (a < INT8_MAX / b) {
return GUF_MATH_CKD_OVERFLOW;
} else if (a > INT8_MIN / b) {
return GUF_MATH_CKD_UNDERFLOW;
} else {
return GUF_MATH_CKD_SUCCESS;
}
} else {
return GUF_MATH_CKD_SUCCESS;
}
}
static inline guf_math_ckd_result guf_ckd_add_i16(int16_t a, int16_t b) static inline guf_math_ckd_result guf_ckd_add_i16(int16_t a, int16_t b)
{ {
@ -116,6 +183,30 @@ static inline guf_math_ckd_result guf_ckd_sub_i16(int16_t a, int16_t b)
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
} }
} }
static inline guf_math_ckd_result guf_ckd_mul_i16(int16_t a, int16_t b)
{
if (b > 0) {
if (a > INT16_MAX / b) {
return GUF_MATH_CKD_OVERFLOW;
} else if (a < INT16_MIN / b) {
return GUF_MATH_CKD_UNDERFLOW;
} else {
return GUF_MATH_CKD_SUCCESS;
}
} else if (b < 0) {
if (INT16_MIN != -INT16_MAX && b == -1) { // Prevent potential (INT16_MIN / b) overflow for b == -1
return a == INT16_MIN ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
} else if (a < INT16_MAX / b) {
return GUF_MATH_CKD_OVERFLOW;
} else if (a > INT16_MIN / b) {
return GUF_MATH_CKD_UNDERFLOW;
} else {
return GUF_MATH_CKD_SUCCESS;
}
} else {
return GUF_MATH_CKD_SUCCESS;
}
}
static inline guf_math_ckd_result guf_ckd_add_i32(int32_t a, int32_t b) static inline guf_math_ckd_result guf_ckd_add_i32(int32_t a, int32_t b)
{ {
@ -137,6 +228,30 @@ static inline guf_math_ckd_result guf_ckd_sub_i32(int32_t a, int32_t b)
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
} }
} }
static inline guf_math_ckd_result guf_ckd_mul_i32(int32_t a, int32_t b)
{
if (b > 0) {
if (a > INT32_MAX / b) {
return GUF_MATH_CKD_OVERFLOW;
} else if (a < INT32_MIN / b) {
return GUF_MATH_CKD_UNDERFLOW;
} else {
return GUF_MATH_CKD_SUCCESS;
}
} else if (b < 0) {
if (INT32_MIN != -INT32_MAX && b == -1) { // Prevent potential (INT32_MIN / b) overflow for b == -1
return a == INT32_MIN ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
} else if (a < INT32_MAX / b) {
return GUF_MATH_CKD_OVERFLOW;
} else if (a > INT32_MIN / b) {
return GUF_MATH_CKD_UNDERFLOW;
} else {
return GUF_MATH_CKD_SUCCESS;
}
} else {
return GUF_MATH_CKD_SUCCESS;
}
}
static inline guf_math_ckd_result guf_ckd_add_i64(int64_t a, int64_t b) static inline guf_math_ckd_result guf_ckd_add_i64(int64_t a, int64_t b)
{ {
@ -158,6 +273,30 @@ static inline guf_math_ckd_result guf_ckd_sub_i64(int64_t a, int64_t b)
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
} }
} }
static inline guf_math_ckd_result guf_ckd_mul_i64(int64_t a, int64_t b)
{
if (b > 0) {
if (a > INT64_MAX / b) {
return GUF_MATH_CKD_OVERFLOW;
} else if (a < INT64_MIN / b) {
return GUF_MATH_CKD_UNDERFLOW;
} else {
return GUF_MATH_CKD_SUCCESS;
}
} else if (b < 0) {
if (INT64_MIN != -INT64_MAX && b == -1) { // Prevent potential (INT64_MIN / b) overflow for b == -1
return a == INT64_MIN ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
} else if (a < INT64_MAX / b) {
return GUF_MATH_CKD_OVERFLOW;
} else if (a > INT64_MIN / b) {
return GUF_MATH_CKD_UNDERFLOW;
} else {
return GUF_MATH_CKD_SUCCESS;
}
} else {
return GUF_MATH_CKD_SUCCESS;
}
}
static inline guf_math_ckd_result guf_ckd_add_ptrdiff_t(ptrdiff_t a, ptrdiff_t b) static inline guf_math_ckd_result guf_ckd_add_ptrdiff_t(ptrdiff_t a, ptrdiff_t b)
{ {
@ -179,6 +318,30 @@ static inline guf_math_ckd_result guf_ckd_sub_ptrdiff_t(ptrdiff_t a, ptrdiff_t b
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
} }
} }
static inline guf_math_ckd_result guf_ckd_mul_ptrdiff_t(ptrdiff_t a, ptrdiff_t b)
{
if (b > 0) {
if (a > PTRDIFF_MAX / b) {
return GUF_MATH_CKD_OVERFLOW;
} else if (a < PTRDIFF_MIN / b) {
return GUF_MATH_CKD_UNDERFLOW;
} else {
return GUF_MATH_CKD_SUCCESS;
}
} else if (b < 0) {
if (PTRDIFF_MIN != -PTRDIFF_MAX && b == -1) { // Prevent potential (PTRDIFF_MIN / b) overflow for b == -1
return a == PTRDIFF_MIN ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
} else if (a < PTRDIFF_MAX / b) {
return GUF_MATH_CKD_OVERFLOW;
} else if (a > PTRDIFF_MIN / b) {
return GUF_MATH_CKD_UNDERFLOW;
} else {
return GUF_MATH_CKD_SUCCESS;
}
} else {
return GUF_MATH_CKD_SUCCESS;
}
}
// Unsigned integer add/sub checks (generated with libguf/tools/ckdint-gen.py) // Unsigned integer add/sub checks (generated with libguf/tools/ckdint-gen.py)
@ -198,6 +361,11 @@ static inline guf_math_ckd_result guf_ckd_sub_uchar(unsigned char a, unsigned ch
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
} }
} }
static inline guf_math_ckd_result guf_ckd_mul_uchar(unsigned char a, unsigned char b)
{
const unsigned char c = a * b;
return a != 0 && ((c / a) != b) ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
}
static inline guf_math_ckd_result guf_ckd_add_unsigned(unsigned a, unsigned b) static inline guf_math_ckd_result guf_ckd_add_unsigned(unsigned a, unsigned b)
{ {
@ -215,6 +383,11 @@ static inline guf_math_ckd_result guf_ckd_sub_unsigned(unsigned a, unsigned b)
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
} }
} }
static inline guf_math_ckd_result guf_ckd_mul_unsigned(unsigned a, unsigned b)
{
const unsigned c = a * b;
return a != 0 && ((c / a) != b) ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
}
static inline guf_math_ckd_result guf_ckd_add_u8(uint8_t a, uint8_t b) static inline guf_math_ckd_result guf_ckd_add_u8(uint8_t a, uint8_t b)
{ {
@ -232,6 +405,11 @@ static inline guf_math_ckd_result guf_ckd_sub_u8(uint8_t a, uint8_t b)
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
} }
} }
static inline guf_math_ckd_result guf_ckd_mul_u8(uint8_t a, uint8_t b)
{
const uint8_t c = a * b;
return a != 0 && ((c / a) != b) ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
}
static inline guf_math_ckd_result guf_ckd_add_u16(uint16_t a, uint16_t b) static inline guf_math_ckd_result guf_ckd_add_u16(uint16_t a, uint16_t b)
{ {
@ -249,6 +427,11 @@ static inline guf_math_ckd_result guf_ckd_sub_u16(uint16_t a, uint16_t b)
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
} }
} }
static inline guf_math_ckd_result guf_ckd_mul_u16(uint16_t a, uint16_t b)
{
const uint16_t c = a * b;
return a != 0 && ((c / a) != b) ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
}
static inline guf_math_ckd_result guf_ckd_add_u32(uint32_t a, uint32_t b) static inline guf_math_ckd_result guf_ckd_add_u32(uint32_t a, uint32_t b)
{ {
@ -266,6 +449,11 @@ static inline guf_math_ckd_result guf_ckd_sub_u32(uint32_t a, uint32_t b)
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
} }
} }
static inline guf_math_ckd_result guf_ckd_mul_u32(uint32_t a, uint32_t b)
{
const uint32_t c = a * b;
return a != 0 && ((c / a) != b) ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
}
static inline guf_math_ckd_result guf_ckd_add_u64(uint64_t a, uint64_t b) static inline guf_math_ckd_result guf_ckd_add_u64(uint64_t a, uint64_t b)
{ {
@ -283,6 +471,11 @@ static inline guf_math_ckd_result guf_ckd_sub_u64(uint64_t a, uint64_t b)
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
} }
} }
static inline guf_math_ckd_result guf_ckd_mul_u64(uint64_t a, uint64_t b)
{
const uint64_t c = a * b;
return a != 0 && ((c / a) != b) ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
}
static inline guf_math_ckd_result guf_ckd_add_size_t(size_t a, size_t b) static inline guf_math_ckd_result guf_ckd_add_size_t(size_t a, size_t b)
{ {
@ -300,6 +493,11 @@ static inline guf_math_ckd_result guf_ckd_sub_size_t(size_t a, size_t b)
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
} }
} }
static inline guf_math_ckd_result guf_ckd_mul_size_t(size_t a, size_t b)
{
const size_t c = a * b;
return a != 0 && ((c / a) != b) ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
}
// Signed saturating/wrapping add/sub (generated with libguf/tools/ckdint-gen.py) // Signed saturating/wrapping add/sub (generated with libguf/tools/ckdint-gen.py)
@ -344,6 +542,26 @@ static inline guf_math_ckd_result guf_saturating_sub_int(int a, int b, int *resu
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_saturating_mul_int(int a, int b, int *result)
{
const guf_math_ckd_result check = guf_ckd_mul_int(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_int(int a, int b, int *result) static inline guf_math_ckd_result guf_wrapping_add_int(int a, int b, int *result)
{ {
@ -387,6 +605,24 @@ static inline guf_math_ckd_result guf_wrapping_sub_int(int a, int b, int *result
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_wrapping_mul_int(int a, int b, int *result)
{
const guf_math_ckd_result check = guf_ckd_mul_int(a, b);
if (result) {
switch (check) {
case GUF_MATH_CKD_SUCCESS:
*result = a * b;
break;
case GUF_MATH_CKD_OVERFLOW:
case GUF_MATH_CKD_UNDERFLOW:
*result = (int)((unsigned)a * (unsigned)b); // TODO: Implementation defined...
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_saturating_add_i8(int8_t a, int8_t b, int8_t *result) static inline guf_math_ckd_result guf_saturating_add_i8(int8_t a, int8_t b, int8_t *result)
@ -429,6 +665,26 @@ static inline guf_math_ckd_result guf_saturating_sub_i8(int8_t a, int8_t b, int8
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_saturating_mul_i8(int8_t a, int8_t b, int8_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_i8(a, b);
if (result) {
switch (check) {
case GUF_MATH_CKD_SUCCESS:
*result = a * b;
break;
case GUF_MATH_CKD_OVERFLOW:
*result = INT8_MAX;
break;
case GUF_MATH_CKD_UNDERFLOW:
*result = INT8_MIN;
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_wrapping_add_i8(int8_t a, int8_t b, int8_t *result) static inline guf_math_ckd_result guf_wrapping_add_i8(int8_t a, int8_t b, int8_t *result)
{ {
@ -472,6 +728,24 @@ static inline guf_math_ckd_result guf_wrapping_sub_i8(int8_t a, int8_t b, int8_t
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_wrapping_mul_i8(int8_t a, int8_t b, int8_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_i8(a, b);
if (result) {
switch (check) {
case GUF_MATH_CKD_SUCCESS:
*result = a * b;
break;
case GUF_MATH_CKD_OVERFLOW:
case GUF_MATH_CKD_UNDERFLOW:
*result = (int8_t)((uint8_t)a * (uint8_t)b); // TODO: Implementation defined...
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_saturating_add_i16(int16_t a, int16_t b, int16_t *result) static inline guf_math_ckd_result guf_saturating_add_i16(int16_t a, int16_t b, int16_t *result)
@ -514,6 +788,26 @@ static inline guf_math_ckd_result guf_saturating_sub_i16(int16_t a, int16_t b, i
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_saturating_mul_i16(int16_t a, int16_t b, int16_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_i16(a, b);
if (result) {
switch (check) {
case GUF_MATH_CKD_SUCCESS:
*result = a * b;
break;
case GUF_MATH_CKD_OVERFLOW:
*result = INT16_MAX;
break;
case GUF_MATH_CKD_UNDERFLOW:
*result = INT16_MIN;
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_wrapping_add_i16(int16_t a, int16_t b, int16_t *result) static inline guf_math_ckd_result guf_wrapping_add_i16(int16_t a, int16_t b, int16_t *result)
{ {
@ -557,6 +851,24 @@ static inline guf_math_ckd_result guf_wrapping_sub_i16(int16_t a, int16_t b, int
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_wrapping_mul_i16(int16_t a, int16_t b, int16_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_i16(a, b);
if (result) {
switch (check) {
case GUF_MATH_CKD_SUCCESS:
*result = a * b;
break;
case GUF_MATH_CKD_OVERFLOW:
case GUF_MATH_CKD_UNDERFLOW:
*result = (int16_t)((uint16_t)a * (uint16_t)b); // TODO: Implementation defined...
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_saturating_add_i32(int32_t a, int32_t b, int32_t *result) static inline guf_math_ckd_result guf_saturating_add_i32(int32_t a, int32_t b, int32_t *result)
@ -599,6 +911,26 @@ static inline guf_math_ckd_result guf_saturating_sub_i32(int32_t a, int32_t b, i
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_saturating_mul_i32(int32_t a, int32_t b, int32_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_i32(a, b);
if (result) {
switch (check) {
case GUF_MATH_CKD_SUCCESS:
*result = a * b;
break;
case GUF_MATH_CKD_OVERFLOW:
*result = INT32_MAX;
break;
case GUF_MATH_CKD_UNDERFLOW:
*result = INT32_MIN;
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_wrapping_add_i32(int32_t a, int32_t b, int32_t *result) static inline guf_math_ckd_result guf_wrapping_add_i32(int32_t a, int32_t b, int32_t *result)
{ {
@ -642,6 +974,24 @@ static inline guf_math_ckd_result guf_wrapping_sub_i32(int32_t a, int32_t b, int
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_wrapping_mul_i32(int32_t a, int32_t b, int32_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_i32(a, b);
if (result) {
switch (check) {
case GUF_MATH_CKD_SUCCESS:
*result = a * b;
break;
case GUF_MATH_CKD_OVERFLOW:
case GUF_MATH_CKD_UNDERFLOW:
*result = (int32_t)((uint32_t)a * (uint32_t)b); // TODO: Implementation defined...
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_saturating_add_i64(int64_t a, int64_t b, int64_t *result) static inline guf_math_ckd_result guf_saturating_add_i64(int64_t a, int64_t b, int64_t *result)
@ -684,6 +1034,26 @@ static inline guf_math_ckd_result guf_saturating_sub_i64(int64_t a, int64_t b, i
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_saturating_mul_i64(int64_t a, int64_t b, int64_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_i64(a, b);
if (result) {
switch (check) {
case GUF_MATH_CKD_SUCCESS:
*result = a * b;
break;
case GUF_MATH_CKD_OVERFLOW:
*result = INT64_MAX;
break;
case GUF_MATH_CKD_UNDERFLOW:
*result = INT64_MIN;
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_wrapping_add_i64(int64_t a, int64_t b, int64_t *result) static inline guf_math_ckd_result guf_wrapping_add_i64(int64_t a, int64_t b, int64_t *result)
{ {
@ -727,6 +1097,24 @@ static inline guf_math_ckd_result guf_wrapping_sub_i64(int64_t a, int64_t b, int
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_wrapping_mul_i64(int64_t a, int64_t b, int64_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_i64(a, b);
if (result) {
switch (check) {
case GUF_MATH_CKD_SUCCESS:
*result = a * b;
break;
case GUF_MATH_CKD_OVERFLOW:
case GUF_MATH_CKD_UNDERFLOW:
*result = (int64_t)((uint64_t)a * (uint64_t)b); // TODO: Implementation defined...
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_saturating_add_ptrdiff_t(ptrdiff_t a, ptrdiff_t b, ptrdiff_t *result) static inline guf_math_ckd_result guf_saturating_add_ptrdiff_t(ptrdiff_t a, ptrdiff_t b, ptrdiff_t *result)
@ -769,6 +1157,26 @@ static inline guf_math_ckd_result guf_saturating_sub_ptrdiff_t(ptrdiff_t a, ptrd
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_saturating_mul_ptrdiff_t(ptrdiff_t a, ptrdiff_t b, ptrdiff_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_ptrdiff_t(a, b);
if (result) {
switch (check) {
case GUF_MATH_CKD_SUCCESS:
*result = a * b;
break;
case GUF_MATH_CKD_OVERFLOW:
*result = PTRDIFF_MAX;
break;
case GUF_MATH_CKD_UNDERFLOW:
*result = PTRDIFF_MIN;
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_wrapping_add_ptrdiff_t(ptrdiff_t a, ptrdiff_t b, ptrdiff_t *result) static inline guf_math_ckd_result guf_wrapping_add_ptrdiff_t(ptrdiff_t a, ptrdiff_t b, ptrdiff_t *result)
{ {
@ -812,6 +1220,24 @@ static inline guf_math_ckd_result guf_wrapping_sub_ptrdiff_t(ptrdiff_t a, ptrdif
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_wrapping_mul_ptrdiff_t(ptrdiff_t a, ptrdiff_t b, ptrdiff_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_ptrdiff_t(a, b);
if (result) {
switch (check) {
case GUF_MATH_CKD_SUCCESS:
*result = a * b;
break;
case GUF_MATH_CKD_OVERFLOW:
case GUF_MATH_CKD_UNDERFLOW:
*result = (ptrdiff_t)((size_t)a * (size_t)b); // TODO: Implementation defined...
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
// Unsigned saturating/wrapping add/sub (generated with libguf/tools/ckdint-gen.py) // Unsigned saturating/wrapping add/sub (generated with libguf/tools/ckdint-gen.py)
@ -852,6 +1278,24 @@ static inline guf_math_ckd_result guf_saturating_sub_uchar(unsigned char a, unsi
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_saturating_mul_uchar(unsigned char a, unsigned char b, unsigned char *result)
{
const guf_math_ckd_result check = guf_ckd_mul_uchar(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 = UCHAR_MAX;
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_wrapping_add_uchar(unsigned char a, unsigned char b, unsigned char *result) static inline guf_math_ckd_result guf_wrapping_add_uchar(unsigned char a, unsigned char b, unsigned char *result)
{ {
@ -869,6 +1313,14 @@ static inline guf_math_ckd_result guf_wrapping_sub_uchar(unsigned char a, unsign
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_wrapping_mul_uchar(unsigned char a, unsigned char b, unsigned char *result)
{
const guf_math_ckd_result check = guf_ckd_mul_uchar(a, b);
if (result) {
*result = a * b;
}
return check;
}
static inline guf_math_ckd_result guf_saturating_add_unsigned(unsigned a, unsigned b, unsigned *result) static inline guf_math_ckd_result guf_saturating_add_unsigned(unsigned a, unsigned b, unsigned *result)
@ -907,6 +1359,24 @@ static inline guf_math_ckd_result guf_saturating_sub_unsigned(unsigned a, unsign
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_saturating_mul_unsigned(unsigned a, unsigned b, unsigned *result)
{
const guf_math_ckd_result check = guf_ckd_mul_unsigned(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 = UINT_MAX;
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_wrapping_add_unsigned(unsigned a, unsigned b, unsigned *result) static inline guf_math_ckd_result guf_wrapping_add_unsigned(unsigned a, unsigned b, unsigned *result)
{ {
@ -924,6 +1394,14 @@ static inline guf_math_ckd_result guf_wrapping_sub_unsigned(unsigned a, unsigned
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_wrapping_mul_unsigned(unsigned a, unsigned b, unsigned *result)
{
const guf_math_ckd_result check = guf_ckd_mul_unsigned(a, b);
if (result) {
*result = a * b;
}
return check;
}
static inline guf_math_ckd_result guf_saturating_add_u8(uint8_t a, uint8_t b, uint8_t *result) static inline guf_math_ckd_result guf_saturating_add_u8(uint8_t a, uint8_t b, uint8_t *result)
@ -962,6 +1440,24 @@ static inline guf_math_ckd_result guf_saturating_sub_u8(uint8_t a, uint8_t b, ui
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_saturating_mul_u8(uint8_t a, uint8_t b, uint8_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_u8(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 = UINT8_MAX;
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_wrapping_add_u8(uint8_t a, uint8_t b, uint8_t *result) static inline guf_math_ckd_result guf_wrapping_add_u8(uint8_t a, uint8_t b, uint8_t *result)
{ {
@ -979,6 +1475,14 @@ static inline guf_math_ckd_result guf_wrapping_sub_u8(uint8_t a, uint8_t b, uint
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_wrapping_mul_u8(uint8_t a, uint8_t b, uint8_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_u8(a, b);
if (result) {
*result = a * b;
}
return check;
}
static inline guf_math_ckd_result guf_saturating_add_u16(uint16_t a, uint16_t b, uint16_t *result) static inline guf_math_ckd_result guf_saturating_add_u16(uint16_t a, uint16_t b, uint16_t *result)
@ -1017,6 +1521,24 @@ static inline guf_math_ckd_result guf_saturating_sub_u16(uint16_t a, uint16_t b,
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_saturating_mul_u16(uint16_t a, uint16_t b, uint16_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_u16(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 = UINT16_MAX;
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_wrapping_add_u16(uint16_t a, uint16_t b, uint16_t *result) static inline guf_math_ckd_result guf_wrapping_add_u16(uint16_t a, uint16_t b, uint16_t *result)
{ {
@ -1034,6 +1556,14 @@ static inline guf_math_ckd_result guf_wrapping_sub_u16(uint16_t a, uint16_t b, u
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_wrapping_mul_u16(uint16_t a, uint16_t b, uint16_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_u16(a, b);
if (result) {
*result = a * b;
}
return check;
}
static inline guf_math_ckd_result guf_saturating_add_u32(uint32_t a, uint32_t b, uint32_t *result) static inline guf_math_ckd_result guf_saturating_add_u32(uint32_t a, uint32_t b, uint32_t *result)
@ -1072,6 +1602,24 @@ static inline guf_math_ckd_result guf_saturating_sub_u32(uint32_t a, uint32_t b,
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_saturating_mul_u32(uint32_t a, uint32_t b, uint32_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_u32(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 = UINT32_MAX;
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_wrapping_add_u32(uint32_t a, uint32_t b, uint32_t *result) static inline guf_math_ckd_result guf_wrapping_add_u32(uint32_t a, uint32_t b, uint32_t *result)
{ {
@ -1089,6 +1637,14 @@ static inline guf_math_ckd_result guf_wrapping_sub_u32(uint32_t a, uint32_t b, u
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_wrapping_mul_u32(uint32_t a, uint32_t b, uint32_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_u32(a, b);
if (result) {
*result = a * b;
}
return check;
}
static inline guf_math_ckd_result guf_saturating_add_u64(uint64_t a, uint64_t b, uint64_t *result) static inline guf_math_ckd_result guf_saturating_add_u64(uint64_t a, uint64_t b, uint64_t *result)
@ -1127,6 +1683,24 @@ static inline guf_math_ckd_result guf_saturating_sub_u64(uint64_t a, uint64_t b,
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_saturating_mul_u64(uint64_t a, uint64_t b, uint64_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_u64(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 = UINT64_MAX;
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_wrapping_add_u64(uint64_t a, uint64_t b, uint64_t *result) static inline guf_math_ckd_result guf_wrapping_add_u64(uint64_t a, uint64_t b, uint64_t *result)
{ {
@ -1144,6 +1718,14 @@ static inline guf_math_ckd_result guf_wrapping_sub_u64(uint64_t a, uint64_t b, u
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_wrapping_mul_u64(uint64_t a, uint64_t b, uint64_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_u64(a, b);
if (result) {
*result = a * b;
}
return check;
}
static inline guf_math_ckd_result guf_saturating_add_size_t(size_t a, size_t b, size_t *result) static inline guf_math_ckd_result guf_saturating_add_size_t(size_t a, size_t b, size_t *result)
@ -1182,6 +1764,24 @@ static inline guf_math_ckd_result guf_saturating_sub_size_t(size_t a, size_t b,
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_saturating_mul_size_t(size_t a, size_t b, size_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_size_t(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 = SIZE_MAX;
break;
default:
GUF_ASSERT(false);
}
}
return check;
}
static inline guf_math_ckd_result guf_wrapping_add_size_t(size_t a, size_t b, size_t *result) static inline guf_math_ckd_result guf_wrapping_add_size_t(size_t a, size_t b, size_t *result)
{ {
@ -1199,5 +1799,13 @@ static inline guf_math_ckd_result guf_wrapping_sub_size_t(size_t a, size_t b, si
} }
return check; return check;
} }
static inline guf_math_ckd_result guf_wrapping_mul_size_t(size_t a, size_t b, size_t *result)
{
const guf_math_ckd_result check = guf_ckd_mul_size_t(a, b);
if (result) {
*result = a * b;
}
return check;
}
#endif #endif

View File

@ -1,5 +1,4 @@
- abs functions with signed result assume two's complement (x) - guf_wrapping_mul_TYPE: Don't rely on implementation defined behaviour
- abs functions with unsigned result assume two's complement (x)
- header guards for optional int64_t types maybe... - header guards for optional int64_t types maybe...

View File

@ -11,6 +11,7 @@ class IntType:
INT_TYPE_ABBR: str INT_TYPE_ABBR: str
INT_MIN: str INT_MIN: str
INT_MAX: str INT_MAX: str
UINT_TYPE: str = "size_t"
@dataclass @dataclass
class UintType: class UintType:
@ -38,6 +39,11 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
}} }}
}} }}
static inline guf_math_ckd_result guf_ckd_mul_{type_abbr}({type} a, {type} b)
{{
const {type} c = a * b;
return a != 0 && ((c / a) != b) ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
}}
""") """)
ckd_add_sub_int = textwrap.dedent(""" ckd_add_sub_int = textwrap.dedent("""
@ -61,6 +67,30 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
return GUF_MATH_CKD_SUCCESS; return GUF_MATH_CKD_SUCCESS;
}} }}
}} }}
static inline guf_math_ckd_result guf_ckd_mul_{type_abbr}({type} a, {type} b)
{{
if (b > 0) {{
if (a > {int_max} / b) {{
return GUF_MATH_CKD_OVERFLOW;
}} else if (a < {int_min} / b) {{
return GUF_MATH_CKD_UNDERFLOW;
}} else {{
return GUF_MATH_CKD_SUCCESS;
}}
}} else if (b < 0) {{
if ({int_min} != -{int_max} && b == -1) {{ // Prevent potential ({int_min} / b) overflow for b == -1
return a == {int_min} ? GUF_MATH_CKD_OVERFLOW : GUF_MATH_CKD_SUCCESS;
}} else if (a < {int_max} / b) {{
return GUF_MATH_CKD_OVERFLOW;
}} else if (a > {int_min} / b) {{
return GUF_MATH_CKD_UNDERFLOW;
}} else {{
return GUF_MATH_CKD_SUCCESS;
}}
}} else {{
return GUF_MATH_CKD_SUCCESS;
}}
}}
""") """)
saturating_wrapping_int = textwrap.dedent(""" saturating_wrapping_int = textwrap.dedent("""
@ -104,6 +134,26 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
}} }}
return check; return check;
}} }}
static inline guf_math_ckd_result guf_saturating_mul_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_mul_{type_abbr}(a, b);
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) static inline guf_math_ckd_result guf_wrapping_add_{type_abbr}({type} a, {type} b, {type} *result)
{{ {{
@ -147,6 +197,24 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
}} }}
return check; return check;
}} }}
static inline guf_math_ckd_result guf_wrapping_mul_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_mul_{type_abbr}(a, b);
if (result) {{
switch (check) {{
case GUF_MATH_CKD_SUCCESS:
*result = a * b;
break;
case GUF_MATH_CKD_OVERFLOW:
case GUF_MATH_CKD_UNDERFLOW:
*result = ({type})(({uint_type})a * ({uint_type})b); // TODO: Implementation defined...
break;
default:
GUF_ASSERT(false);
}}
}}
return check;
}}
""") """)
saturating_wrapping_uint = textwrap.dedent(""" saturating_wrapping_uint = textwrap.dedent("""
@ -186,6 +254,24 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
}} }}
return check; return check;
}} }}
static inline guf_math_ckd_result guf_saturating_mul_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_mul_{type_abbr}(a, b);
GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_OVERFLOW);
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_wrapping_add_{type_abbr}({type} a, {type} b, {type} *result) static inline guf_math_ckd_result guf_wrapping_add_{type_abbr}({type} a, {type} b, {type} *result)
{{ {{
@ -203,6 +289,14 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
}} }}
return check; return check;
}} }}
static inline guf_math_ckd_result guf_wrapping_mul_{type_abbr}({type} a, {type} b, {type} *result)
{{
const guf_math_ckd_result check = guf_ckd_mul_{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" text_result = "// Signed integer add/sub checks (generated with libguf/tools/ckdint-gen.py)\n"
@ -216,7 +310,7 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
text_result += "\n\n// Signed saturating/wrapping add/sub (generated with libguf/tools/ckdint-gen.py)\n" text_result += "\n\n// Signed saturating/wrapping add/sub (generated with libguf/tools/ckdint-gen.py)\n"
for type in int_types: for type in int_types:
text_result += saturating_wrapping_int.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX) + "\n" text_result += saturating_wrapping_int.format(type = type.INT_TYPE, type_abbr = type.INT_TYPE_ABBR, int_min = type.INT_MIN, int_max = type.INT_MAX, uint_type = type.UINT_TYPE) + "\n"
text_result += "\n// Unsigned saturating/wrapping add/sub (generated with libguf/tools/ckdint-gen.py)\n" text_result += "\n// Unsigned saturating/wrapping add/sub (generated with libguf/tools/ckdint-gen.py)\n"
for type in uint_types: for type in uint_types:
@ -229,12 +323,12 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
if __name__ == "__main__": if __name__ == "__main__":
int_types = [ int_types = [
IntType(INT_TYPE = "int", INT_TYPE_ABBR = "int", INT_MIN = "INT_MIN", INT_MAX = "INT_MAX"), IntType(INT_TYPE = "int", INT_TYPE_ABBR = "int", INT_MIN = "INT_MIN", INT_MAX = "INT_MAX", UINT_TYPE = "unsigned"),
IntType(INT_TYPE = "int8_t", INT_TYPE_ABBR = "i8", INT_MIN = "INT8_MIN", INT_MAX = "INT8_MAX"), IntType(INT_TYPE = "int8_t", INT_TYPE_ABBR = "i8", INT_MIN = "INT8_MIN", INT_MAX = "INT8_MAX", UINT_TYPE = "uint8_t"),
IntType(INT_TYPE = "int16_t", INT_TYPE_ABBR = "i16", INT_MIN = "INT16_MIN", INT_MAX = "INT16_MAX"), IntType(INT_TYPE = "int16_t", INT_TYPE_ABBR = "i16", INT_MIN = "INT16_MIN", INT_MAX = "INT16_MAX", UINT_TYPE = "uint16_t"),
IntType(INT_TYPE = "int32_t", INT_TYPE_ABBR = "i32", INT_MIN = "INT32_MIN", INT_MAX = "INT32_MAX"), IntType(INT_TYPE = "int32_t", INT_TYPE_ABBR = "i32", INT_MIN = "INT32_MIN", INT_MAX = "INT32_MAX", UINT_TYPE = "uint32_t"),
IntType(INT_TYPE = "int64_t", INT_TYPE_ABBR = "i64", INT_MIN = "INT64_MIN", INT_MAX = "INT64_MAX"), IntType(INT_TYPE = "int64_t", INT_TYPE_ABBR = "i64", INT_MIN = "INT64_MIN", INT_MAX = "INT64_MAX", UINT_TYPE = "uint64_t"),
IntType(INT_TYPE = "ptrdiff_t", INT_TYPE_ABBR = "ptrdiff_t", INT_MIN = "PTRDIFF_MIN", INT_MAX = "PTRDIFF_MAX"), IntType(INT_TYPE = "ptrdiff_t", INT_TYPE_ABBR = "ptrdiff_t", INT_MIN = "PTRDIFF_MIN", INT_MAX = "PTRDIFF_MAX", UINT_TYPE = "size_t"), # TODO: size_t is not necessarily the unsigned ptrdiff_t equivalent
] ]
uint_types = [ uint_types = [