summaryrefslogtreecommitdiff
path: root/libmisc
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2024-11-11 18:35:57 -0700
committerLuke T. Shumaker <lukeshu@lukeshu.com>2024-11-11 19:19:48 -0700
commitc08b86b1c07bc45c14393ed0d3e759faa89ea59b (patch)
tree21404cdc25798996acc2113d3ccc7806e28afdf5 /libmisc
parent9f6f267cc4acb6c72872e42593ca701df1f5fdc3 (diff)
Factor out libmisc/rand.h
Diffstat (limited to 'libmisc')
-rw-r--r--libmisc/include/libmisc/rand.h46
1 files changed, 46 insertions, 0 deletions
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_ */