diff --git a/ethermess.c b/ethermess.c index 0c780ad..0dfaf3c 100644 --- a/ethermess.c +++ b/ethermess.c @@ -138,6 +138,84 @@ struct timespec ms_in_future(intmax_t ms) { return ts; } +intmax_t saturating_add(intmax_t a, intmax_t b) { + if (a < 0 && b < 0) { + // a: [min, -1] + // b: [min, -1] + // min + min -> underflow + // min + -1 -> underflow + // -1 + -1 -> ok + // a + b < INTMAX_MIN || - a + // b < INTMAX_MIN - a + if (b < INTMAX_MIN - a) { + // Underflow + fprintf(stderr, "a+b underflow\n"); // debg + return INTMAX_MIN; + } + } else if (a < 0 && b >= 0) { + // a: [min, -1] + // b: [0, max] + // min + 0 -> ok + // min + max -> ok + // -1 + 0 -> ok + // -1 + max -> ok + } else if (a >= 0 && b < 0) { + // See above but swap a and b + } else if (a >= 0 && b >= 0) { + // a: [0, max] + // b: [0, max] + // 0 + 0 -> ok + // 0 + max -> ok + // max + max -> overflow + // a + b > INTMAX_MAX || -a + // b > INTMAX_MAX - a + if (b > INTMAX_MAX - a) { + // Overflow + fprintf(stderr, "a+b overflow\n"); // debg + return INTMAX_MAX; + } + } + + return a + b; +} + +intmax_t saturating_sub(intmax_t a, intmax_t b) { + // a - b = a + (-b) + // Only case where -b is not safe is when b < -INTMAX_MAX (because + // INTMAX_MIN can be smaller than -INTMAX_MAX, but INTMAX_MAX can't be + // larger than -INTMAX_MIN) + if (b < -INTMAX_MAX) { + // a - b || + c - c + // a - b + c - c + // a - (b - c) - c + // a + (c - b) - c + fprintf(stderr, "-b overflow\n"); // debg + intmax_t c = saturating_sub(b, -INTMAX_MAX); + return saturating_sub(saturating_add(a, c - b), c); + } else { + return saturating_sub(a, -b); + } +} + +intmax_t saturating_mul(intmax_t a, intmax_t b) { + // Doesn't give 100% right results when one parameter is INTMAX_MIN, + // but at least it won't ever overflow + if (a < 0) { + return saturating_sub(0, saturating_mul(saturating_sub(0, a), b)); + } + if (b < 0) { + return saturating_sub(0, saturating_mul(a, saturating_sub(0, b))); + } + + if (INTMAX_MAX / a < b) { + // Overflow + fprintf(stderr, "a*b overflow\n"); // debg + return INTMAX_MAX; + } + + return a * b; +} + int wait_ms_until(struct timespec then) { // This function basically returns the difference in ms between the // current time and the given time, clamped to [0, INT_MAX] @@ -151,11 +229,13 @@ int wait_ms_until(struct timespec then) { err(1, "clock_gettime"); } - // Overflow not checked because fuck overflow checking in C - // TODO: Check overflow - intmax_t sec_diff = then.tv_sec - now.tv_sec; - intmax_t ns_diff = then.tv_nsec - now.tv_nsec; - intmax_t ms = sec_diff * 1000 + ns_diff / 1000 / 1000; + // Uses saturating arithmetic. I guess this might be wrong in some cases + // but can't be bothered to deal with it any other way, especially as a + // clamping the values to even something like [0, 1] would result in + // mostly correct functioning + intmax_t sec_diff = saturating_sub(then.tv_sec, now.tv_sec); + intmax_t ns_diff = saturating_sub(then.tv_nsec, now.tv_nsec); + intmax_t ms = saturating_add(saturating_mul(sec_diff, 1000), ns_diff / 1000 / 1000); // Clamp if (ms > INT_MAX) {