On 11/18/21 19:12, Eric Blake wrote:
Perhaps you can do this with a function signature like:
bool check_op_overflow (uint64_t a, uint64_t b, uint64_t max, uint64_t *r)
- deduce the actual result limit from ((typeof (*r))-1),
- assign the low-order bits in any case -- if either the uintmax_t operation overflows or the actual result limit is exceeded, it all happens in unsigned, so it's well-defined (modular reduction). Return the overflow status correctly.
and then utilize it something like:
#define CHECK_OP_OVERFLOW(a, b, r) \ ({ \ bool overflow; uint64_t tmp; \ CHECK_UNSIGNED_INT (a); CHECK_UNSIGNED_INT (b); \ overflow = check_op_overflow (a, b, (typeof (*(r)))-1, &tmp); \ *(r) = tmp; \ overflow; \ })
and maybe even have CHECK_UNSIGNED_INT() permit signed values >= 0, rather than insisting that the underlying type be unsigned.
At any rate, the approach sounds good to me.
How about this: -------- #include <stdbool.h> #include <stdint.h> /* Assert, at compile time, that the expression "x" has some unsigned integer * type. * * The expression "x" is not evaluated, unless it has variably modified type. */ #define STATIC_ASSERT_UNSIGNED_INT(x) \ do { \ typedef char x_has_uint_type[(typeof (x))-1 > 0 ? 1 : -1]; \ } while (0) /* Assign the sum (a + b) to (*r), using uintmax_t modular arithmetic. * * Return true iff the addition overflows or the result exceeds (max). */ static inline bool check_add_overflow (uintmax_t a, uintmax_t b, uintmax_t max, uintmax_t *r) { *r = a + b; return a > UINTMAX_MAX - b || *r > max; } /* Assign the product (a * b) to (*r), using uintmax_t modular arithmetic. * * Return true iff the multiplication overflows or the result exceeds (max). */ static inline bool check_mul_overflow (uintmax_t a, uintmax_t b, uintmax_t max, uintmax_t *r) { *r = a * b; return b > 0 && (a > UINTMAX_MAX / b || *r > max); } /* Add (a) and (b), both of (possibly different) unsigned integer types, and * store the sum in (*r), which must also have some unsigned integer type. * * Each macro argument is evaluated exactly once, as long as it does not have * variably modified type. * * The macro evaluates to (false) if (*r) can represent the mathematical sum. * Otherwise, the macro evaluates to (true), and the low order bits of the * mathematical sum are stored to (*r). */ #define ADD_OVERFLOW(a, b, r) \ ({ \ bool overflow; \ uintmax_t tmp; \ \ STATIC_ASSERT_UNSIGNED_INT (a); \ STATIC_ASSERT_UNSIGNED_INT (b); \ STATIC_ASSERT_UNSIGNED_INT (*(r)); \ overflow = check_add_overflow ((a), (b), (typeof (*(r)))-1, &tmp); \ *(r) = tmp; \ overflow; \ }) /* Multiply (a) and (b), both of (possibly different) unsigned integer types, * and store the product in (*r), which must also have some unsigned integer * type. * * Each macro argument is evaluated exactly once, as long as it does not have * variably modified type. * * The macro evaluates to (false) if (*r) can represent the mathematical * product. Otherwise, the macro evaluates to (true), and the low order bits of * the mathematical product are stored to (*r). */ #define MUL_OVERFLOW(a, b, r) \ ({ \ bool overflow; \ uintmax_t tmp; \ \ STATIC_ASSERT_UNSIGNED_INT (a); \ STATIC_ASSERT_UNSIGNED_INT (b); \ STATIC_ASSERT_UNSIGNED_INT (*(r)); \ overflow = check_mul_overflow ((a), (b), (typeof (*(r)))-1, &tmp); \ *(r) = tmp; \ overflow; \ }) -------- Questions: (1) What is the usual style in nbdkit to highlight parameters of functions and function-like macros in documentation (comments)? Here I used parentheses. Normally I use quotes "". (2) In the actual functions, we return "true" for "overflow". My original conditions expressed "no overflow". In this source code, I *open-coded* the negations of my original conditions, using De Morgan's laws. That's because I dislike expressions with an outermost "!" operator -- whenever I face such an expression, I always work the outermost negation *into* the formula, as deeply as I can. However, I realize the resultant conditions may not be easy to read this way. Here's an alternative for the body of check_add_overflow(), based on the original formulation: register bool in_range; *r = a + b; in_range = a <= UINTMAX_MAX - b && *r <= max; return !in_range; (3) In the overflow conditions, I used parentheses as sparingly as possible; only when needed. I could also use as many as possible. - Compare, for the addition: return ((a > UINTMAX_MAX - b) || (*r > max)); or in_range = ((a <= UINTMAX_MAX - b) && (*r <= max)); return !in_range; - Compare, for the multiplication: return ((b > 0) && ((a > UINTMAX_MAX / b) || (*r > max))); or in_range = ((b == 0) || ((a <= UINTMAX_MAX / b) && (*r <= max))); return !in_range; What's the preferred form? Thanks, Laszlo