sn_random.[ch]: Add a module for random number handling.
authorFlorian Forster <octo@leeloo.home.verplant.org>
Fri, 25 Apr 2008 09:05:50 +0000 (11:05 +0200)
committerFlorian Forster <octo@leeloo.home.verplant.org>
Fri, 25 Apr 2008 09:05:50 +0000 (11:05 +0200)
The random number handling should be thread-safe.

src/sn_random.c [new file with mode: 0644]
src/sn_random.h [new file with mode: 0644]

diff --git a/src/sn_random.c b/src/sn_random.c
new file mode 100644 (file)
index 0000000..c67b451
--- /dev/null
@@ -0,0 +1,122 @@
+#define _ISOC99_SOURCE
+#define _POSIX_C_SOURCE 200112L
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include "sn_random.h"
+
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+static unsigned int seed;
+static int have_init = 0;
+
+static int read_dev_random (void *buffer, size_t buffer_size)
+{
+  int fd;
+  int status = 0;
+
+  char *buffer_position;
+  size_t yet_to_read;
+
+  fd = open ("/dev/random", O_RDONLY);
+  if (fd < 0)
+  {
+    perror ("open");
+    return (-1);
+  }
+
+  buffer_position = (char *) buffer;
+  yet_to_read = buffer_size;
+
+  while (yet_to_read > 0)
+  {
+    status = read (fd, (void *) buffer_position, yet_to_read);
+    if (status < 0)
+    {
+      if (errno == EINTR)
+       continue;
+
+      fprintf (stderr, "read_dev_random: read failed.\n");
+      break;
+    }
+
+    buffer_position += status;
+    yet_to_read -= (size_t) status;
+  }
+
+  close (fd);
+
+  if (status < 0)
+    return (-1);
+  return (0);
+} /* int read_dev_random */
+
+static void do_init (void)
+{
+  int status;
+
+  status = read_dev_random (&seed, sizeof (seed));
+  if (status == 0)
+    have_init = 1;
+} /* void do_init */
+
+int sn_random (void)
+{
+  int ret;
+
+  pthread_mutex_lock (&lock);
+
+  if (have_init == 0)
+    do_init ();
+
+  ret = rand_r (&seed);
+
+  pthread_mutex_unlock (&lock);
+
+  return (ret);
+} /* int sn_random */
+
+int sn_true_random (void)
+{
+  int ret = 0;
+  int status;
+
+  status = read_dev_random (&ret, sizeof (ret));
+  if (status != 0)
+    return (sn_random ());
+
+  return (ret);
+} /* int sn_true_random */
+
+int sn_bounded_random (int min, int max)
+{
+  int range;
+  int rand;
+
+  if (min == max)
+    return (min);
+  else if (min > max)
+  {
+    range = min;
+    min = max;
+    max = range;
+  }
+
+  range = 1 + max - min;
+  rand = min + (int) (((double) range)
+      * (((double) sn_random ()) / (((double) RAND_MAX) + 1.0)));
+
+  assert (rand >= min);
+  assert (rand <= max);
+
+  return (rand);
+} /* int sn_bounded_random */
+
+/* vim: set shiftwidth=2 softtabstop=2 : */
diff --git a/src/sn_random.h b/src/sn_random.h
new file mode 100644 (file)
index 0000000..3c01c56
--- /dev/null
@@ -0,0 +1,9 @@
+#ifndef SN_RANDOM_H
+#define SN_RANDOM_H 1
+
+int sn_random (void);
+int sn_true_random (void);
+
+int sn_bounded_random (int min, int max);
+
+#endif /* SN_RANDOM_H */