diff --git a/src/guf_math_ckdint.h b/src/guf_math_ckdint.h index ca3cea2..3cdd37e 100644 --- a/src/guf_math_ckdint.h +++ b/src/guf_math_ckdint.h @@ -81,12 +81,21 @@ typedef enum guf_math_ckd_result {GUF_MATH_CKD_SUCCESS = 0, GUF_MATH_CKD_OVERFLOW_POS, GUF_MATH_CKD_OVERFLOW_NEG} guf_math_ckd_result; #if !defined(GUF_MATH_CKDINT_IMPL_STATIC) && !defined(GUF_MATH_CKDINT_IMPL) + // Signed integer arithmetic checks (generated with libguf/tools/ckdint-gen.py) GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_int(int a, int b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_int(int a, int b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_int(int a, int b); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_long(long a, long b); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_long(long a, long b); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_long(long a, long b); + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_long_long(long long a, long long b); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_long_long(long long a, long long b); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_long_long(long long a, long long b); + GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_i8(int8_t a, int8_t b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_i8(int8_t a, int8_t b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_i8(int8_t a, int8_t b); @@ -117,6 +126,14 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_unsigned(unsigned a, unsig GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_unsigned(unsigned a, unsigned b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_unsigned(unsigned a, unsigned b); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_ulong(unsigned long a, unsigned long b); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_ulong(unsigned long a, unsigned long b); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_ulong(unsigned long a, unsigned long b); + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_ulong_long(unsigned long long a, unsigned long long b); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_ulong_long(unsigned long long a, unsigned long long b); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_ulong_long(unsigned long long a, unsigned long long b); + GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_u8(uint8_t a, uint8_t b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_u8(uint8_t a, uint8_t b); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_u8(uint8_t a, uint8_t b); @@ -149,6 +166,24 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_int(int a, int b, int GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_int(int a, int b, int *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_long(long a, long b, long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_long(long a, long b, long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_long(long a, long b, long *result); + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_long(long a, long b, long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_long(long a, long b, long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_long(long a, long b, long *result); + + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_long_long(long long a, long long b, long long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_long_long(long long a, long long b, long long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_long_long(long long a, long long b, long long *result); + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_long_long(long long a, long long b, long long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_long_long(long long a, long long b, long long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_long_long(long long a, long long b, long long *result); + + GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_i8(int8_t a, int8_t b, int8_t *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_i8(int8_t a, int8_t b, int8_t *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_i8(int8_t a, int8_t b, int8_t *result); @@ -214,6 +249,24 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_unsigned(unsigned a, GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_unsigned(unsigned a, unsigned b, unsigned *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_ulong(unsigned long a, unsigned long b, unsigned long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_ulong(unsigned long a, unsigned long b, unsigned long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_ulong(unsigned long a, unsigned long b, unsigned long *result); + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_ulong(unsigned long a, unsigned long b, unsigned long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_ulong(unsigned long a, unsigned long b, unsigned long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_ulong(unsigned long a, unsigned long b, unsigned long *result); + + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_ulong_long(unsigned long long a, unsigned long long b, unsigned long long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_ulong_long(unsigned long long a, unsigned long long b, unsigned long long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_ulong_long(unsigned long long a, unsigned long long b, unsigned long long *result); + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_ulong_long(unsigned long long a, unsigned long long b, unsigned long long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_ulong_long(unsigned long long a, unsigned long long b, unsigned long long *result); +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_ulong_long(unsigned long long a, unsigned long long b, unsigned long long *result); + + GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_u8(uint8_t a, uint8_t b, uint8_t *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_u8(uint8_t a, uint8_t b, uint8_t *result); GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_u8(uint8_t a, uint8_t b, uint8_t *result); @@ -309,6 +362,96 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_int(int a, int b) } } +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_long(long a, long b) +{ + if (b > 0 && a > LONG_MAX - b) { + return GUF_MATH_CKD_OVERFLOW_POS; + } else if (b < 0 && a < LONG_MIN - b) { + return GUF_MATH_CKD_OVERFLOW_NEG; + } else { + return GUF_MATH_CKD_SUCCESS; + } +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_long(long a, long b) +{ + if (b < 0 && a > LONG_MAX + b) { + return GUF_MATH_CKD_OVERFLOW_POS; + } else if (b > 0 && a < LONG_MIN + b) { + return GUF_MATH_CKD_OVERFLOW_NEG; + } else { + return GUF_MATH_CKD_SUCCESS; + } +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_long(long a, long b) +{ + if (b > 0) { + if (a > LONG_MAX / b) { + return GUF_MATH_CKD_OVERFLOW_POS; + } else if (a < LONG_MIN / b) { + return GUF_MATH_CKD_OVERFLOW_NEG; + } else { + return GUF_MATH_CKD_SUCCESS; + } + } else if (b < 0) { + if (LONG_MIN != -LONG_MAX && b == -1) { // Prevent potential (LONG_MIN / b) overflow for b == -1 + return a == LONG_MIN ? GUF_MATH_CKD_OVERFLOW_POS : GUF_MATH_CKD_SUCCESS; + } else if (a < LONG_MAX / b) { + return GUF_MATH_CKD_OVERFLOW_POS; + } else if (a > LONG_MIN / b) { + return GUF_MATH_CKD_OVERFLOW_NEG; + } else { + return GUF_MATH_CKD_SUCCESS; + } + } else { + return GUF_MATH_CKD_SUCCESS; + } +} + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_long_long(long long a, long long b) +{ + if (b > 0 && a > LLONG_MAX - b) { + return GUF_MATH_CKD_OVERFLOW_POS; + } else if (b < 0 && a < LLONG_MIN - b) { + return GUF_MATH_CKD_OVERFLOW_NEG; + } else { + return GUF_MATH_CKD_SUCCESS; + } +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_long_long(long long a, long long b) +{ + if (b < 0 && a > LLONG_MAX + b) { + return GUF_MATH_CKD_OVERFLOW_POS; + } else if (b > 0 && a < LLONG_MIN + b) { + return GUF_MATH_CKD_OVERFLOW_NEG; + } else { + return GUF_MATH_CKD_SUCCESS; + } +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_long_long(long long a, long long b) +{ + if (b > 0) { + if (a > LLONG_MAX / b) { + return GUF_MATH_CKD_OVERFLOW_POS; + } else if (a < LLONG_MIN / b) { + return GUF_MATH_CKD_OVERFLOW_NEG; + } else { + return GUF_MATH_CKD_SUCCESS; + } + } else if (b < 0) { + if (LLONG_MIN != -LLONG_MAX && b == -1) { // Prevent potential (LLONG_MIN / b) overflow for b == -1 + return a == LLONG_MIN ? GUF_MATH_CKD_OVERFLOW_POS : GUF_MATH_CKD_SUCCESS; + } else if (a < LLONG_MAX / b) { + return GUF_MATH_CKD_OVERFLOW_POS; + } else if (a > LLONG_MIN / b) { + return GUF_MATH_CKD_OVERFLOW_NEG; + } else { + return GUF_MATH_CKD_SUCCESS; + } + } else { + return GUF_MATH_CKD_SUCCESS; + } +} + GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_i8(int8_t a, int8_t b) { if (b > 0 && a > INT8_MAX - b) { @@ -580,6 +723,50 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_unsigned(unsigned a, unsig return a != 0 && ((1u * c / a) != b) ? GUF_MATH_CKD_OVERFLOW_POS : GUF_MATH_CKD_SUCCESS; } +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_ulong(unsigned long a, unsigned long b) +{ + if (b > 0 && a > ULONG_MAX - b) { + return GUF_MATH_CKD_OVERFLOW_POS; + } else { + return GUF_MATH_CKD_SUCCESS; + } +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_ulong(unsigned long a, unsigned long b) +{ + if (b > a) { + return GUF_MATH_CKD_OVERFLOW_NEG; + } else { + return GUF_MATH_CKD_SUCCESS; + } +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_ulong(unsigned long a, unsigned long b) +{ + const unsigned long c = 1u * a * b; + return a != 0 && ((1u * c / a) != b) ? GUF_MATH_CKD_OVERFLOW_POS : GUF_MATH_CKD_SUCCESS; +} + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_ulong_long(unsigned long long a, unsigned long long b) +{ + if (b > 0 && a > ULLONG_MAX - b) { + return GUF_MATH_CKD_OVERFLOW_POS; + } else { + return GUF_MATH_CKD_SUCCESS; + } +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_sub_ulong_long(unsigned long long a, unsigned long long b) +{ + if (b > a) { + return GUF_MATH_CKD_OVERFLOW_NEG; + } else { + return GUF_MATH_CKD_SUCCESS; + } +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_mul_ulong_long(unsigned long long a, unsigned long long b) +{ + const unsigned long long c = 1u * a * b; + return a != 0 && ((1u * c / a) != b) ? GUF_MATH_CKD_OVERFLOW_POS : GUF_MATH_CKD_SUCCESS; +} + GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_ckd_add_u8(uint8_t a, uint8_t b) { if (b > 0 && a > UINT8_MAX - b) { @@ -824,6 +1011,268 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_int(int a, int b, int } +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_long(long a, long b, long *result) +{ + const guf_math_ckd_result check = guf_ckd_add_long(a, b); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a + b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + *result = LONG_MAX; + break; + case GUF_MATH_CKD_OVERFLOW_NEG: + *result = LONG_MIN; + break; + default: + GUF_ASSERT(false); + } + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_long(long a, long b, long *result) +{ + const guf_math_ckd_result check = guf_ckd_sub_long(a, b); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a - b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + *result = LONG_MAX; + break; + case GUF_MATH_CKD_OVERFLOW_NEG: + *result = LONG_MIN; + break; + default: + GUF_ASSERT(false); + } + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_long(long a, long b, long *result) +{ + const guf_math_ckd_result check = guf_ckd_mul_long(a, b); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a * b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + *result = LONG_MAX; + break; + case GUF_MATH_CKD_OVERFLOW_NEG: + *result = LONG_MIN; + break; + default: + GUF_ASSERT(false); + } + } + return check; +} + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_long(long a, long b, long *result) +{ + const guf_math_ckd_result check = guf_ckd_add_long(a, b); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a + b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + *result = (a + LONG_MIN) + (b + LONG_MIN); + break; + case GUF_MATH_CKD_OVERFLOW_NEG: + *result = (a - LONG_MIN) + (b - LONG_MIN); + break; + default: + GUF_ASSERT(false); + } + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_long(long a, long b, long *result) +{ + const guf_math_ckd_result check = guf_ckd_sub_long(a, b); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a - b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + GUF_ASSERT(b < 0); + *result = (a + LONG_MIN) - (b - LONG_MIN); // TODO: not sure + break; + case GUF_MATH_CKD_OVERFLOW_NEG: + GUF_ASSERT(b > 0); + *result = (a - LONG_MIN) - (b + LONG_MIN); // TODO: not sure + break; + default: + GUF_ASSERT(false); + } + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_long(long a, long b, long *result) +{ + const guf_math_ckd_result check = guf_ckd_mul_long(a, b); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a * b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + case GUF_MATH_CKD_OVERFLOW_NEG: { + unsigned long res = 1u * (unsigned long)a * (unsigned long)b; + if (res > LONG_MAX) { // This is the fix for implementation defined conversion from unsigned to signed. + const unsigned long mod = (1u + (unsigned long)LONG_MAX); + res = mod > 0 ? (1u * res % mod) : res; + *result = LONG_MIN + (long)res; + } else { + *result = (long)res; + } + break; + } + default: + GUF_ASSERT(false); + } + } + return check; +} + + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_long_long(long long a, long long b, long long *result) +{ + const guf_math_ckd_result check = guf_ckd_add_long_long(a, b); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a + b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + *result = LLONG_MAX; + break; + case GUF_MATH_CKD_OVERFLOW_NEG: + *result = LLONG_MIN; + break; + default: + GUF_ASSERT(false); + } + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_long_long(long long a, long long b, long long *result) +{ + const guf_math_ckd_result check = guf_ckd_sub_long_long(a, b); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a - b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + *result = LLONG_MAX; + break; + case GUF_MATH_CKD_OVERFLOW_NEG: + *result = LLONG_MIN; + break; + default: + GUF_ASSERT(false); + } + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_long_long(long long a, long long b, long long *result) +{ + const guf_math_ckd_result check = guf_ckd_mul_long_long(a, b); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a * b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + *result = LLONG_MAX; + break; + case GUF_MATH_CKD_OVERFLOW_NEG: + *result = LLONG_MIN; + break; + default: + GUF_ASSERT(false); + } + } + return check; +} + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_long_long(long long a, long long b, long long *result) +{ + const guf_math_ckd_result check = guf_ckd_add_long_long(a, b); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a + b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + *result = (a + LLONG_MIN) + (b + LLONG_MIN); + break; + case GUF_MATH_CKD_OVERFLOW_NEG: + *result = (a - LLONG_MIN) + (b - LLONG_MIN); + break; + default: + GUF_ASSERT(false); + } + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_long_long(long long a, long long b, long long *result) +{ + const guf_math_ckd_result check = guf_ckd_sub_long_long(a, b); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a - b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + GUF_ASSERT(b < 0); + *result = (a + LLONG_MIN) - (b - LLONG_MIN); // TODO: not sure + break; + case GUF_MATH_CKD_OVERFLOW_NEG: + GUF_ASSERT(b > 0); + *result = (a - LLONG_MIN) - (b + LLONG_MIN); // TODO: not sure + break; + default: + GUF_ASSERT(false); + } + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_long_long(long long a, long long b, long long *result) +{ + const guf_math_ckd_result check = guf_ckd_mul_long_long(a, b); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a * b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + case GUF_MATH_CKD_OVERFLOW_NEG: { + unsigned long long res = 1u * (unsigned long long)a * (unsigned long long)b; + if (res > LLONG_MAX) { // This is the fix for implementation defined conversion from unsigned to signed. + const unsigned long long mod = (1u + (unsigned long long)LLONG_MAX); + res = mod > 0 ? (1u * res % mod) : res; + *result = LLONG_MIN + (long long)res; + } else { + *result = (long long)res; + } + break; + } + default: + GUF_ASSERT(false); + } + } + return check; +} + + GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_i8(int8_t a, int8_t b, int8_t *result) { const guf_math_ckd_result check = guf_ckd_add_i8(a, b); @@ -1643,6 +2092,168 @@ GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_unsigned(unsigned a, } +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_ulong(unsigned long a, unsigned long b, unsigned long *result) +{ + const guf_math_ckd_result check = guf_ckd_add_ulong(a, b); + GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_OVERFLOW_POS); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a + b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + *result = ULONG_MAX; + break; + default: + GUF_ASSERT(false); + } + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_ulong(unsigned long a, unsigned long b, unsigned long *result) +{ + const guf_math_ckd_result check = guf_ckd_sub_ulong(a, b); + GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_OVERFLOW_NEG); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a - b; + break; + case GUF_MATH_CKD_OVERFLOW_NEG: + *result = 0; + break; + default: + GUF_ASSERT(false); + } + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_ulong(unsigned long a, unsigned long b, unsigned long *result) +{ + const guf_math_ckd_result check = guf_ckd_mul_ulong(a, b); + GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_OVERFLOW_POS); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a * b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + *result = ULONG_MAX; + break; + default: + GUF_ASSERT(false); + } + } + return check; +} + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_ulong(unsigned long a, unsigned long b, unsigned long *result) +{ + const guf_math_ckd_result check = guf_ckd_add_ulong(a, b); + if (result) { + *result = 1u * a + b; + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_ulong(unsigned long a, unsigned long b, unsigned long *result) +{ + const guf_math_ckd_result check = guf_ckd_sub_ulong(a, b); + if (result) { + *result = 1u * a - b; + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_ulong(unsigned long a, unsigned long b, unsigned long *result) +{ + const guf_math_ckd_result check = guf_ckd_mul_ulong(a, b); + if (result) { + *result = 1u * a * b; + } + return check; +} + + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_ulong_long(unsigned long long a, unsigned long long b, unsigned long long *result) +{ + const guf_math_ckd_result check = guf_ckd_add_ulong_long(a, b); + GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_OVERFLOW_POS); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a + b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + *result = ULLONG_MAX; + break; + default: + GUF_ASSERT(false); + } + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_sub_ulong_long(unsigned long long a, unsigned long long b, unsigned long long *result) +{ + const guf_math_ckd_result check = guf_ckd_sub_ulong_long(a, b); + GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_OVERFLOW_NEG); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a - b; + break; + case GUF_MATH_CKD_OVERFLOW_NEG: + *result = 0; + break; + default: + GUF_ASSERT(false); + } + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_mul_ulong_long(unsigned long long a, unsigned long long b, unsigned long long *result) +{ + const guf_math_ckd_result check = guf_ckd_mul_ulong_long(a, b); + GUF_ASSERT(check == GUF_MATH_CKD_SUCCESS || check == GUF_MATH_CKD_OVERFLOW_POS); + if (result) { + switch (check) { + case GUF_MATH_CKD_SUCCESS: + *result = a * b; + break; + case GUF_MATH_CKD_OVERFLOW_POS: + *result = ULLONG_MAX; + break; + default: + GUF_ASSERT(false); + } + } + return check; +} + +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_add_ulong_long(unsigned long long a, unsigned long long b, unsigned long long *result) +{ + const guf_math_ckd_result check = guf_ckd_add_ulong_long(a, b); + if (result) { + *result = 1u * a + b; + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_sub_ulong_long(unsigned long long a, unsigned long long b, unsigned long long *result) +{ + const guf_math_ckd_result check = guf_ckd_sub_ulong_long(a, b); + if (result) { + *result = 1u * a - b; + } + return check; +} +GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_wrapping_mul_ulong_long(unsigned long long a, unsigned long long b, unsigned long long *result) +{ + const guf_math_ckd_result check = guf_ckd_mul_ulong_long(a, b); + if (result) { + *result = 1u * a * b; + } + return check; +} + + GUF_MATH_CKDINT_KWRDS guf_math_ckd_result guf_saturating_add_u8(uint8_t a, uint8_t b, uint8_t *result) { const guf_math_ckd_result check = guf_ckd_add_u8(a, b); diff --git a/src/guf_str.h b/src/guf_str.h index a386ad9..a135671 100644 --- a/src/guf_str.h +++ b/src/guf_str.h @@ -82,6 +82,8 @@ typedef struct guf_str_tok_state { // 1.) guf_str_view: +GUF_STR_KWRDS guf_str_view guf_str_view_from_str(const guf_str* str); + // Return a new guf_str_view corresponding to the substring in range [pos, pos + count) of str GUF_STR_KWRDS guf_str_view guf_str_view_substr(guf_str_view str, ptrdiff_t pos, ptrdiff_t count); @@ -889,9 +891,10 @@ GUF_STR_KWRDS guf_str *guf_str_copy(guf_str *dst, const guf_str *src, void *ctx) guf_str_init_empty(dst, src->allocator); GUF_ASSERT(guf_str_is_short_internal_(dst)); + char *dst_cstr = NULL; if (!guf_str_is_short_internal_(src)) { const size_t src_cap_with_null = guf_str_cap_internal_(src) + 1; - char *dst_cstr = src->allocator->alloc(src_cap_with_null, src->allocator->ctx); + dst_cstr = src->allocator->alloc(src_cap_with_null, src->allocator->ctx); if (!dst_cstr) { *dst = guf_str_new_uninitialised(); return NULL; @@ -901,15 +904,16 @@ GUF_STR_KWRDS guf_str *guf_str_copy(guf_str *dst, const guf_str *src, void *ctx) dst->data.lng.size = src->data.lng.size; } else { dst->data.shrt.size = src->data.shrt.size; + dst_cstr = dst->data.shrt.c_str; } - + GUF_ASSERT(dst_cstr); + const size_t src_len_with_null = guf_str_len_internal_(src) + 1; GUF_ASSERT(src_len_with_null == (guf_str_len_internal_(dst) + 1)); - GUF_ASSERT(guf_str_is_short(dst) == guf_str_is_short(src)); + GUF_ASSERT(guf_str_is_short_internal_(dst) == guf_str_is_short_internal_(src)); const char *src_cstr = guf_str_const_cstr(src); - char *dst_cstr = guf_str_cstr(dst); - GUF_ASSERT(src_cstr && dst_cstr); + GUF_ASSERT(src_cstr); memcpy(dst_cstr, src_cstr, src_len_with_null); GUF_ASSERT(guf_str_is_valid(dst)); @@ -1205,6 +1209,8 @@ GUF_STR_KWRDS uint32_t guf_str_hash32(const guf_str *str) } + + // guf_str_view: GUF_STR_KWRDS bool guf_str_view_is_valid(guf_str_view sv) @@ -1216,6 +1222,15 @@ GUF_STR_KWRDS bool guf_str_view_is_valid(guf_str_view sv) } } +GUF_STR_KWRDS guf_str_view guf_str_view_from_str(const guf_str *str) +{ + GUF_ASSERT(str); + GUF_ASSERT(guf_str_is_valid(str)); + + return (guf_str_view){.str = guf_str_const_cstr(str), .len = guf_str_len(str)}; + +} + /* cf. "str_pop_first_split": - https://accu.org/conf-docs/PDFs_2021/luca_sass_modern_c_and_what_we_can_learn_from_it.pdf ("String handling in Modern C", page 128 of the pdf) diff --git a/src/test/impls/dbuf_impl.c b/src/test/impls/dbuf_impl.c index f490d8c..c0cf1b8 100644 --- a/src/test/impls/dbuf_impl.c +++ b/src/test/impls/dbuf_impl.c @@ -44,3 +44,12 @@ #define GUF_T_EQ guf_str_view_equal #define GUF_DBUF_IMPL #include "guf_dbuf.h" + +#define GUF_T guf_str +#define GUF_DBUF_NAME dbuf_str +#define GUF_T_COPY guf_str_copy +#define GUF_T_MOVE guf_str_move +#define GUF_T_FREE guf_str_free +#define GUF_T_EQ guf_str_equal +#define GUF_DBUF_IMPL +#include "guf_dbuf.h" diff --git a/src/test/impls/dbuf_impl.h b/src/test/impls/dbuf_impl.h index 8039abb..5c6d04a 100644 --- a/src/test/impls/dbuf_impl.h +++ b/src/test/impls/dbuf_impl.h @@ -42,4 +42,12 @@ #define GUF_T_EQ guf_str_view_equal #include "guf_dbuf.h" +#define GUF_T guf_str +#define GUF_DBUF_NAME dbuf_str +#define GUF_T_COPY guf_str_copy +#define GUF_T_MOVE guf_str_move +#define GUF_T_FREE guf_str_free +#define GUF_T_EQ guf_str_equal +#include "guf_dbuf.h" + #endif diff --git a/src/test/test.cpp b/src/test/test.cpp index cd37834..415a8af 100644 --- a/src/test/test.cpp +++ b/src/test/test.cpp @@ -21,6 +21,8 @@ static void init_tests() { g_tests.push_back(std::make_unique("DbufIntTest")); g_tests.push_back(std::make_unique("DbufCstringTest")); + g_tests.push_back(std::make_unique("DbufStrTest")); + g_tests.push_back(std::make_unique("DictSvToIntTest")); g_tests.push_back(std::make_unique("UTF8Test")); g_tests.push_back(std::make_unique("StrTest")); diff --git a/src/test/test_dbuf.cpp b/src/test/test_dbuf.cpp index d1f9230..70c06e4 100644 --- a/src/test/test_dbuf.cpp +++ b/src/test/test_dbuf.cpp @@ -221,6 +221,8 @@ void DbufIntTest::test_insert_remove(int n) } + + /* DbufCstringTest */ @@ -539,3 +541,86 @@ void DbufCstringTest::test_find(int n) dbuf_heap_cstr_free(&str_dbuf, NULL); } + + + +/* + DbufStrTest: +*/ + +void DbufStrTest::run() +{ + test_push_insert_erase(16); +} + + +void DbufStrTest::test_push_insert_erase(size_t n, ptrdiff_t start_cap) +{ + dbuf_str strings = start_cap < 0 ? dbuf_str_new(&allocator) : dbuf_str_new_with_capacity(start_cap, &allocator); + std::vector strings_cpp {}; + + guf_libc_alloc_ctx str_allocator_ctx = { + .tracker = guf_alloc_tracker_new(42, "test_push_insert_erase: local str allocator", NULL, NULL), + .zero_init = false + }; + guf_allocator str_allocator = guf_libc_allocator_new(&str_allocator_ctx); + + for (size_t i = 0; i < n; ++i) { + std::string str; + for (size_t c = 0; c < 512 + GUF_STR_SSO_BUF_CAP; ++c) { + str += c % 10 + '0'; + } + const guf_str_view sv_long = guf_str_view{.str = str.data(), .len = (ptrdiff_t)str.size()}; + const guf_str_view sv_short = guf_str_view{.str = str.data(), .len = (ptrdiff_t)GUF_STR_SSO_BUF_CAP - 1}; + + guf_str long_str = GUF_STR_UNINITIALISED_CPP, short_str = GUF_STR_UNINITIALISED_CPP; + guf_str_init(&long_str, sv_long, &str_allocator); + guf_str_init(&short_str, sv_short, &str_allocator); + + TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 1 + (i*3)); + + // Move + guf_err err; + dbuf_str_try_push(&strings, &long_str, GUF_CPY_MOVE, &err); + TEST_CHECK(err == GUF_ERR_NONE); + dbuf_str_try_push(&strings, &short_str, GUF_CPY_MOVE, &err); + TEST_CHECK(err == GUF_ERR_NONE); + + TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 1 + (i*3)); + + TEST_CHECK(guf_str_is_uninit(&long_str)); + TEST_CHECK(guf_str_is_uninit(&short_str)); + + // Deep-copy + guf_str_init(&long_str, sv_long, &str_allocator); + guf_str_init(&short_str, sv_short, &str_allocator); + + TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 2 + (i*3)); + + dbuf_str_try_push(&strings, &long_str, GUF_CPY_DEEP, &err); + TEST_CHECK(err == GUF_ERR_NONE); + + dbuf_str_try_push(&strings, &short_str, GUF_CPY_DEEP, &err); + TEST_CHECK(err == GUF_ERR_NONE); + + TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 3 + (i*3)); + + + TEST_CHECK(guf_str_is_valid(&long_str) && guf_str_is_valid(&short_str)); + TEST_CHECK(guf_str_view_equal_val_arg(guf_str_view_from_str(&long_str), sv_long)); + TEST_CHECK(guf_str_view_equal_val_arg(guf_str_view_from_str(&short_str), sv_short)); + + guf_str_free(&long_str, NULL); + guf_str_free(&short_str, NULL); + + TEST_CHECK(str_allocator_ctx.tracker.free_count == 1 + (i*1)); + TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 3 + (i*3)); + } + + TEST_CHECK(str_allocator_ctx.tracker.free_count == n); + TEST_CHECK(str_allocator_ctx.tracker.alloc_count == 3 * n); + + dbuf_str_free(&strings, NULL); + TEST_CHECK(!guf_alloc_tracker_found_leak(&str_allocator_ctx.tracker)); + +} \ No newline at end of file diff --git a/src/test/test_dbuf.hpp b/src/test/test_dbuf.hpp index 3bb4d36..832d88a 100644 --- a/src/test/test_dbuf.hpp +++ b/src/test/test_dbuf.hpp @@ -48,3 +48,22 @@ private: void test_push_insert_erase(int n, ptrdiff_t start_cap = 0); void test_find(int n = 32); }; + + +struct DbufStrTest : public Test +{ + DbufStrTest(std::string name) : Test(name) + { + allocator_ctx.zero_init = false; + guf_alloc_tracker_init(&allocator_ctx.tracker, 3, "DbufStrTest_allocator", NULL, NULL); + guf_libc_allocator_init(&allocator, &allocator_ctx); + } + + void run() override; + +private: + guf_allocator allocator; + guf_libc_alloc_ctx allocator_ctx; + + void test_push_insert_erase(size_t n, ptrdiff_t start_cap = 0); +}; diff --git a/tools/ckdint-gen.py b/tools/ckdint-gen.py index 05a543f..6ce4c92 100644 --- a/tools/ckdint-gen.py +++ b/tools/ckdint-gen.py @@ -372,6 +372,9 @@ if __name__ == "__main__": int_types = [ IntType(INT_TYPE = "int", INT_TYPE_ABBR = "int", INT_MIN = "INT_MIN", INT_MAX = "INT_MAX", UINT_TYPE = "unsigned"), + IntType(INT_TYPE = "long", INT_TYPE_ABBR = "long", INT_MIN = "LONG_MIN", INT_MAX = "LONG_MAX", UINT_TYPE = "unsigned long"), + IntType(INT_TYPE = "long long", INT_TYPE_ABBR = "long_long", INT_MIN = "LLONG_MIN", INT_MAX = "LLONG_MAX", UINT_TYPE = "unsigned long long"), + IntType(INT_TYPE = "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"), @@ -380,13 +383,16 @@ if __name__ == "__main__": ] 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"), + 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 = "ULONG_MIN", INT_MAX = "ULONG_MAX"), + UintType(INT_TYPE = "unsigned long long", INT_TYPE_ABBR = "ulong_long", INT_MIN = "ULLONG_MIN", INT_MAX = "ULLONG_MAX"), + + UintType(INT_TYPE = "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_header, code_impl = generate_ckdint_functions(int_types = int_types, uint_types= uint_types)