From 17be975d3afbff837ea621470f8a093c0090c5c8 Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 3 Aug 2024 16:23:28 -0400 Subject: Fix integer random functions so they correctly handle all representable ranges --- builtins/integers.c | 24 ++++++++++++++++-------- builtins/integers.h | 2 +- 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'builtins') diff --git a/builtins/integers.c b/builtins/integers.c index ef9b5df4..d50c6573 100644 --- a/builtins/integers.c +++ b/builtins/integers.c @@ -51,14 +51,22 @@ } \ return bit_array; \ } \ - public c_type KindOfInt ## $random(int64_t min, int64_t max) { \ - if (min > max) fail("Random min (%ld) is larger than max (%ld)", min, max); \ - if (min < (int64_t)min_val) fail("Random min (%ld) is smaller than the minimum "#KindOfInt" value", min); \ - if (max > (int64_t)max_val) fail("Random max (%ld) is smaller than the maximum "#KindOfInt" value", max); \ - int64_t range = max - min; \ - if (range > UINT32_MAX) fail("Random range (%ld) is larger than the maximum allowed (%ld)", range, UINT32_MAX); \ - uint32_t r = arc4random_uniform((uint32_t)range); \ - return min + (c_type)r; \ + public c_type KindOfInt ## $random(c_type min, c_type max) { \ + if (min > max) fail("Random minimum value (%ld) is larger than the maximum value (%ld)", min, max); \ + if (min == max) return min; \ + if (min == min_val && max == max_val) { \ + c_type r; \ + arc4random_buf(&r, sizeof(r)); \ + return r; \ + } \ + uint64_t range = (uint64_t)max - (uint64_t)min + 1; \ + uint64_t min_r = -range % range; \ + uint64_t r; \ + for (;;) { \ + arc4random_buf(&r, sizeof(r)); \ + if (r >= min_r) break; \ + } \ + return (c_type)((uint64_t)min + (r % range)); \ } \ public c_type KindOfInt ## $from_text(CORD text, CORD *the_rest) { \ const char *str = CORD_to_const_char_star(text); \ diff --git a/builtins/integers.h b/builtins/integers.h index 2e8f6721..2c638a4a 100644 --- a/builtins/integers.h +++ b/builtins/integers.h @@ -27,7 +27,7 @@ CORD type_name ## $hex(c_type i, int64_t digits, bool uppercase, bool prefix); \ CORD type_name ## $octal(c_type i, int64_t digits, bool prefix); \ array_t type_name ## $bits(c_type x); \ - c_type type_name ## $random(int64_t min, int64_t max); \ + c_type type_name ## $random(c_type min, c_type max); \ c_type type_name ## $from_text(CORD text, CORD *the_rest); \ extern const c_type type_name ## $min, type_name##$max; \ extern const TypeInfo $ ## type_name; -- cgit v1.2.3