sn_random.[ch]: Add a module for random number handling.
[sort-networks.git] / src / sn_random.c
1 #define _ISOC99_SOURCE
2 #define _POSIX_C_SOURCE 200112L
3
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <fcntl.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <assert.h>
12 #include <pthread.h>
13
14 #include "sn_random.h"
15
16 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
17 static unsigned int seed;
18 static int have_init = 0;
19
20 static int read_dev_random (void *buffer, size_t buffer_size)
21 {
22   int fd;
23   int status = 0;
24
25   char *buffer_position;
26   size_t yet_to_read;
27
28   fd = open ("/dev/random", O_RDONLY);
29   if (fd < 0)
30   {
31     perror ("open");
32     return (-1);
33   }
34
35   buffer_position = (char *) buffer;
36   yet_to_read = buffer_size;
37
38   while (yet_to_read > 0)
39   {
40     status = read (fd, (void *) buffer_position, yet_to_read);
41     if (status < 0)
42     {
43       if (errno == EINTR)
44         continue;
45
46       fprintf (stderr, "read_dev_random: read failed.\n");
47       break;
48     }
49
50     buffer_position += status;
51     yet_to_read -= (size_t) status;
52   }
53
54   close (fd);
55
56   if (status < 0)
57     return (-1);
58   return (0);
59 } /* int read_dev_random */
60
61 static void do_init (void)
62 {
63   int status;
64
65   status = read_dev_random (&seed, sizeof (seed));
66   if (status == 0)
67     have_init = 1;
68 } /* void do_init */
69
70 int sn_random (void)
71 {
72   int ret;
73
74   pthread_mutex_lock (&lock);
75
76   if (have_init == 0)
77     do_init ();
78
79   ret = rand_r (&seed);
80
81   pthread_mutex_unlock (&lock);
82
83   return (ret);
84 } /* int sn_random */
85
86 int sn_true_random (void)
87 {
88   int ret = 0;
89   int status;
90
91   status = read_dev_random (&ret, sizeof (ret));
92   if (status != 0)
93     return (sn_random ());
94
95   return (ret);
96 } /* int sn_true_random */
97
98 int sn_bounded_random (int min, int max)
99 {
100   int range;
101   int rand;
102
103   if (min == max)
104     return (min);
105   else if (min > max)
106   {
107     range = min;
108     min = max;
109     max = range;
110   }
111
112   range = 1 + max - min;
113   rand = min + (int) (((double) range)
114       * (((double) sn_random ()) / (((double) RAND_MAX) + 1.0)));
115
116   assert (rand >= min);
117   assert (rand <= max);
118
119   return (rand);
120 } /* int sn_bounded_random */
121
122 /* vim: set shiftwidth=2 softtabstop=2 : */