From 6a593ab8a062a2040430279b4342df4a9589373b Mon Sep 17 00:00:00 2001 From: Jordan Bancino Date: Thu, 16 Feb 2023 00:31:13 +0000 Subject: [PATCH] [#48] Add Rand API and make StrRandom() use it. --- src/Rand.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++ src/Str.c | 20 ++++++----- src/include/Rand.h | 35 +++++++++++++++++++ 3 files changed, 129 insertions(+), 9 deletions(-) create mode 100644 src/Rand.c create mode 100644 src/include/Rand.h diff --git a/src/Rand.c b/src/Rand.c new file mode 100644 index 0000000..f387c7c --- /dev/null +++ b/src/Rand.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include + +#include + +#include +#include +#include + +/* Generate random numbers using rejection sampling. +* The basic idea is to "reroll" if a number happens to be +* outside the range. However this could be extremely inefficient. +* +* Another idea would just be to "reroll" if the generated number +* ends up in the previously "biased" range, and THEN do a modulo. +* +* This would be far more efficient for small values of max, +* and fixes the bias issue. */ + +/* This algorithm therefore computes N random numbers generally in + * O(N) time, while being less biased. */ +void +RandIntN(int * buf, size_t size, unsigned int max) +{ + static pthread_mutex_t seedLock = PTHREAD_MUTEX_INITIALIZER; + static unsigned int seed = 0; + int tmp; + /* Limit the range to banish all previously biased results */ + const int allowed = RAND_MAX - RAND_MAX % max; + + size_t i; + + pthread_mutex_lock(&seedLock); + if (!seed) + { + /* Generate a seed from the system time, PID, and TID */ + seed = UtilServerTs() ^ getpid() ^ (unsigned long) pthread_self(); + } + + /* Generate {size} random numbers. */ + for (i = 0; i < size; i++) + { + /* Most of the time, this will take about 1 loop */ + do + { + tmp = rand_r(&seed); + } while (tmp >= allowed); + /* Since a generated number here is never in the biased range, + * we can now safely use modulo. */ + buf[i] = tmp % max; + } + pthread_mutex_unlock(&seedLock); +} +/* Generate just 1 random number */ +int +RandInt(unsigned int max) +{ + int val = 0; + RandIntN(&val, 1, max); + return val; +} diff --git a/src/Str.c b/src/Str.c index 785f2fa..b4172b1 100644 --- a/src/Str.c +++ b/src/Str.c @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -153,10 +154,9 @@ char * StrRandom(size_t len) { static const char charset[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - static pthread_mutex_t seedLock = PTHREAD_MUTEX_INITIALIZER; - static unsigned int seed = 0; char *str; + int * nums; size_t i; if (!len) @@ -165,25 +165,27 @@ StrRandom(size_t len) } str = Malloc(len + 1); + if (!str) { return NULL; } - pthread_mutex_lock(&seedLock); - - if (!seed) + nums = Malloc(len); + if (!nums) { - seed = UtilServerTs() ^ getpid() ^ (unsigned long) pthread_self(); + Free(str); + return NULL; } + /* TODO: This seems slow. */ + RandIntN(nums, len, sizeof(charset) - 1); for (i = 0; i < len; i++) { - str[i] = charset[rand_r(&seed) % (sizeof(charset) - 1)]; + str[i] = charset[nums[i]]; } - pthread_mutex_unlock(&seedLock); - + Free(nums); str[len] = '\0'; return str; } diff --git a/src/include/Rand.h b/src/include/Rand.h new file mode 100644 index 0000000..8542e91 --- /dev/null +++ b/src/include/Rand.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022-2023 Jordan Bancino <@jordan:bancino.net> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef TELODENDRIA_RAND_H +#define TELODENDRIA_RAND_H +#include + +extern int + RandInt(unsigned int); + +extern void + RandIntN(int *, size_t, unsigned int); + +#endif /* TELODENDRIA_RAND_H */