Replace the rand() which reads from /dev/random with a Mersenne PRNG.

Much better now.

Also, it is seeded by reading from /dev/random at startup. Not sure if that's the best idea, but we'll see.
This commit is contained in:
apio 2022-10-15 16:46:54 +02:00
parent 18f1f8b7ca
commit 45afd3e243
4 changed files with 78 additions and 40 deletions

View File

@ -117,25 +117,7 @@ int main()
if (fclose(config) < 0) { perror("fclose"); }
FILE* fp = fopen("/dev/random", "rw");
if (!fp)
{
perror("fopen");
return 1;
}
long int random = 0;
fread(&random, sizeof(random), 1, fp);
if (ferror(fp))
{
perror("fread");
return 1;
}
fclose(fp);
printf("\n\nGot random number %ld\n\n", random);
printf("\n\nGot random number %d\n\n", rand());
sleep(2);

View File

@ -53,7 +53,7 @@ extern "C"
/* Returns a random number. */
int rand(void);
/* Does nothing. */
/* Seeds the random number generator with the specified seed. */
void srand(unsigned int seed);
#ifdef __cplusplus

View File

@ -10,6 +10,33 @@ static void terminate_libc()
fclose(stderr);
}
static void initialize_random()
{
unsigned int seed = 3735928559U;
FILE* fp = fopen("/dev/random", "rw");
if (!fp)
{
errno = 0;
goto failed_to_read_dev_random;
}
fread(&seed, sizeof(seed), 1, fp);
if (ferror(fp))
{
errno = 0;
fclose(fp);
goto failed_to_read_dev_random;
}
fclose(fp);
failed_to_read_dev_random:
srand(seed); // If we failed, this will be seeded with a known value. Else, it will be seeded with a random value
// from the kernel.
return;
}
static void check_for_file(int fd, FILE** target_stream, const char* path, const char* mode)
{
if (lseek(fd, 0, SEEK_CUR) < 0)
@ -27,5 +54,7 @@ extern "C" void initialize_libc()
{
check_for_file(STDOUT_FILENO, &stdout, "/dev/console", "rw");
check_for_file(STDERR_FILENO, &stderr, "/dev/console", "rw");
initialize_random();
atexit(terminate_libc);
}

View File

@ -1,37 +1,64 @@
#include <assert.h>
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
static FILE* randfp = nullptr;
typedef uint32_t word_t;
#define STATE_SIZE 624
#define MIDDLE 397
#define INIT_SHIFT 30
#define INIT_FACT 1812433253
#define TWIST_MASK 0x9908b0df
#define SHIFT1 11
#define MASK1 0xffffffff
#define SHIFT2 7
#define MASK2 0x9d2c5680
#define SHIFT3 15
#define MASK3 0xefc60000
#define SHIFT4 18
void close_randfp()
#define LOWER_MASK 0x7fffffff
#define UPPER_MASK (~(word_t)LOWER_MASK)
static word_t state[STATE_SIZE];
static size_t index = STATE_SIZE + 1;
static void twist()
{
fclose(randfp);
for (size_t i = 0; i < STATE_SIZE; i++)
{
word_t x = (state[i] & UPPER_MASK) | (state[(i + 1) % STATE_SIZE] & LOWER_MASK);
x = (x >> 1) ^ (x & 1 ? TWIST_MASK : 0);
state[i] = state[(i + MIDDLE) % STATE_SIZE] ^ x;
}
index = 0;
}
extern "C"
{
int rand() // FIXME: This function is a dirty and extremely hacky way to get going, /dev/random should only be used
// for initial seeding, userspace should do their own random calculations.
int rand()
{
if (!randfp)
if (index >= STATE_SIZE)
{
randfp = fopen("/dev/random", "rw");
assert(randfp != nullptr);
atexit(close_randfp);
assert(index == STATE_SIZE && "Mersenne generator was never seeded");
twist();
}
int result;
fread(&result, sizeof(result), 1, randfp);
if (ferror(randfp))
{
perror("read(randfp)");
abort();
}
return result;
word_t y = state[index];
y ^= (y >> SHIFT1) & MASK1;
y ^= (y << SHIFT2) & MASK2;
y ^= (y << SHIFT3) & MASK3;
y ^= y >> SHIFT4;
index++;
return y;
}
void srand(unsigned int)
void srand(unsigned int seed)
{
return;
index = STATE_SIZE;
state[0] = seed;
for (size_t i = 1; i < STATE_SIZE; i++)
state[i] = (word_t)((INIT_FACT * (state[i - 1] ^ (state[i - 1] >> INIT_SHIFT))) + i);
}
}