Add checked mul arithmetic
This commit is contained in:
parent
9c417d2aa1
commit
873cdf20b1
@ -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 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
|
||||
// (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 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
|
||||
// (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 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/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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
|
||||
@ -198,6 +361,11 @@ static inline guf_math_ckd_result guf_ckd_sub_uchar(unsigned char a, unsigned ch
|
||||
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)
|
||||
{
|
||||
@ -215,6 +383,11 @@ static inline guf_math_ckd_result guf_ckd_sub_unsigned(unsigned a, unsigned b)
|
||||
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)
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
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)
|
||||
@ -344,6 +542,26 @@ static inline guf_math_ckd_result guf_saturating_sub_int(int a, int b, int *resu
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -387,6 +605,24 @@ static inline guf_math_ckd_result guf_wrapping_sub_int(int a, int b, int *result
|
||||
}
|
||||
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)
|
||||
@ -429,6 +665,26 @@ static inline guf_math_ckd_result guf_saturating_sub_i8(int8_t a, int8_t b, int8
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -472,6 +728,24 @@ static inline guf_math_ckd_result guf_wrapping_sub_i8(int8_t a, int8_t b, int8_t
|
||||
}
|
||||
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)
|
||||
@ -514,6 +788,26 @@ static inline guf_math_ckd_result guf_saturating_sub_i16(int16_t a, int16_t b, i
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -557,6 +851,24 @@ static inline guf_math_ckd_result guf_wrapping_sub_i16(int16_t a, int16_t b, int
|
||||
}
|
||||
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)
|
||||
@ -599,6 +911,26 @@ static inline guf_math_ckd_result guf_saturating_sub_i32(int32_t a, int32_t b, i
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -642,6 +974,24 @@ static inline guf_math_ckd_result guf_wrapping_sub_i32(int32_t a, int32_t b, int
|
||||
}
|
||||
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)
|
||||
@ -684,6 +1034,26 @@ static inline guf_math_ckd_result guf_saturating_sub_i64(int64_t a, int64_t b, i
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -727,6 +1097,24 @@ static inline guf_math_ckd_result guf_wrapping_sub_i64(int64_t a, int64_t b, int
|
||||
}
|
||||
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)
|
||||
@ -769,6 +1157,26 @@ static inline guf_math_ckd_result guf_saturating_sub_ptrdiff_t(ptrdiff_t a, ptrd
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -812,6 +1220,24 @@ static inline guf_math_ckd_result guf_wrapping_sub_ptrdiff_t(ptrdiff_t a, ptrdif
|
||||
}
|
||||
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)
|
||||
@ -852,6 +1278,24 @@ static inline guf_math_ckd_result guf_saturating_sub_uchar(unsigned char a, unsi
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -869,6 +1313,14 @@ static inline guf_math_ckd_result guf_wrapping_sub_uchar(unsigned char a, unsign
|
||||
}
|
||||
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)
|
||||
@ -907,6 +1359,24 @@ static inline guf_math_ckd_result guf_saturating_sub_unsigned(unsigned a, unsign
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -924,6 +1394,14 @@ static inline guf_math_ckd_result guf_wrapping_sub_unsigned(unsigned a, unsigned
|
||||
}
|
||||
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)
|
||||
@ -962,6 +1440,24 @@ static inline guf_math_ckd_result guf_saturating_sub_u8(uint8_t a, uint8_t b, ui
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -979,6 +1475,14 @@ static inline guf_math_ckd_result guf_wrapping_sub_u8(uint8_t a, uint8_t b, uint
|
||||
}
|
||||
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)
|
||||
@ -1017,6 +1521,24 @@ static inline guf_math_ckd_result guf_saturating_sub_u16(uint16_t a, uint16_t b,
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -1034,6 +1556,14 @@ static inline guf_math_ckd_result guf_wrapping_sub_u16(uint16_t a, uint16_t b, u
|
||||
}
|
||||
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)
|
||||
@ -1072,6 +1602,24 @@ static inline guf_math_ckd_result guf_saturating_sub_u32(uint32_t a, uint32_t b,
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -1089,6 +1637,14 @@ static inline guf_math_ckd_result guf_wrapping_sub_u32(uint32_t a, uint32_t b, u
|
||||
}
|
||||
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)
|
||||
@ -1127,6 +1683,24 @@ static inline guf_math_ckd_result guf_saturating_sub_u64(uint64_t a, uint64_t b,
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -1144,6 +1718,14 @@ static inline guf_math_ckd_result guf_wrapping_sub_u64(uint64_t a, uint64_t b, u
|
||||
}
|
||||
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)
|
||||
@ -1182,6 +1764,24 @@ static inline guf_math_ckd_result guf_saturating_sub_size_t(size_t a, size_t b,
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -1199,5 +1799,13 @@ static inline guf_math_ckd_result guf_wrapping_sub_size_t(size_t a, size_t b, si
|
||||
}
|
||||
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
|
||||
|
||||
3
todo.txt
3
todo.txt
@ -1,5 +1,4 @@
|
||||
- abs functions with signed result assume two's complement (x)
|
||||
- abs functions with unsigned result assume two's complement (x)
|
||||
- guf_wrapping_mul_TYPE: Don't rely on implementation defined behaviour
|
||||
|
||||
- header guards for optional int64_t types maybe...
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ class IntType:
|
||||
INT_TYPE_ABBR: str
|
||||
INT_MIN: str
|
||||
INT_MAX: str
|
||||
UINT_TYPE: str = "size_t"
|
||||
|
||||
@dataclass
|
||||
class UintType:
|
||||
@ -38,6 +39,11 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
|
||||
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("""
|
||||
@ -61,6 +67,30 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
|
||||
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("""
|
||||
@ -104,6 +134,26 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
|
||||
}}
|
||||
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)
|
||||
{{
|
||||
@ -147,6 +197,24 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
|
||||
}}
|
||||
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("""
|
||||
@ -186,6 +254,24 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
|
||||
}}
|
||||
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)
|
||||
{{
|
||||
@ -203,6 +289,14 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
|
||||
}}
|
||||
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"
|
||||
@ -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"
|
||||
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"
|
||||
for type in uint_types:
|
||||
@ -229,12 +323,12 @@ def generate_ckdint_functions(int_types: list, uint_types: list) -> str:
|
||||
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"),
|
||||
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", UINT_TYPE = "uint8_t"),
|
||||
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", UINT_TYPE = "uint32_t"),
|
||||
IntType(INT_TYPE = "int64_t", INT_TYPE_ABBR = "i64", INT_MIN = "INT64_MIN", INT_MAX = "INT64_MAX", UINT_TYPE = "uint64_t"),
|
||||
IntType(INT_TYPE = "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 = [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user