diff options
-rw-r--r-- | libcr_ipc/include/libcr_ipc/select.h | 13 | ||||
-rw-r--r-- | libmisc/include/libmisc/rand.h | 46 |
2 files changed, 48 insertions, 11 deletions
diff --git a/libcr_ipc/include/libcr_ipc/select.h b/libcr_ipc/include/libcr_ipc/select.h index ce14416..5e36a18 100644 --- a/libcr_ipc/include/libcr_ipc/select.h +++ b/libcr_ipc/include/libcr_ipc/select.h @@ -7,9 +7,9 @@ #include <alloca.h> /* for alloca() */ #include <stdarg.h> /* for va_* */ #include <stddef.h> /* for size_t */ -#include <stdlib.h> /* for random() */ #include <libmisc/assert.h> +#include <libmisc/rand.h> #include <libcr_ipc/chan.h> @@ -69,15 +69,6 @@ struct cr_select_arg { /* cr_select_v(arg_cnt, arg_vec) **********************************************/ -static inline size_t _cr_select_pickone(size_t cnt) { - long fair_cnt = (0x80000000L / cnt) * cnt; - long rnd; - do { - rnd = random(); - } while (rnd >= fair_cnt); - return rnd % cnt; -} - enum _cr_select_class { _CR_SELECT_CLASS_DEFAULT, _CR_SELECT_CLASS_BLOCKING, @@ -140,7 +131,7 @@ static size_t cr_select_v(size_t arg_cnt, struct cr_select_arg arg_vec[]) { } if (cnt_nonblock) { - size_t choice = _cr_select_pickone(cnt_nonblock); + size_t choice = rand_uint63n(cnt_nonblock); for (size_t i = 0, seen = 0; i < arg_cnt; i++) { if (_cr_select_getclass(arg_vec[i]) == _CR_SELECT_CLASS_NONBLOCK) { if (seen == choice) { diff --git a/libmisc/include/libmisc/rand.h b/libmisc/include/libmisc/rand.h new file mode 100644 index 0000000..bdb0db9 --- /dev/null +++ b/libmisc/include/libmisc/rand.h @@ -0,0 +1,46 @@ +/* libmisc/rand.h - Non-crytpographic random-number utilities + * + * Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +#ifndef _LIBMISC_RAND_H_ +#define _LIBMISC_RAND_H_ + +#include <stdint.h> /* for uint{n}_t, UINT{n}_C() */ +#include <stdlib.h> /* for random() */ + +#include <libmisc/assert.h> + +/** + * Return a psuedo-random number in the half-open interval [0,cnt). + * `cnt` must not be greater than 1<<63. + */ +static inline uint64_t rand_uint63n(uint64_t cnt) { + if (cnt <= UINT64_C(1)<<31) { + uint32_t fair_cnt = ((UINT32_C(1)<<31) / cnt) * cnt; + uint32_t rnd; + do { + rnd = random(); + } while (rnd >= fair_cnt); + return rnd % cnt; + } else if (cnt <= UINT64_C(1)<<62) { + uint64_t fair_cnt = ((UINT64_C(1)<<62) / cnt) * cnt; + uint64_t rnd; + do { + rnd = (random() << 31) | random(); + } while (rnd >= fair_cnt); + return rnd % cnt; + } else if (cnt <= UINT64_C(1)<<63) { + uint64_t fair_cnt = ((UINT64_C(1)<<63) / cnt) * cnt; + uint64_t rnd; + do { + rnd = (random() << 62) | (random() << 31) | random(); + } while (rnd >= fair_cnt); + return rnd % cnt; + } else { + assert_notreached("cnt is out of bounds"); + } +} + +#endif /* _LIBMISC_RAND_H_ */ |