zrand.c - libzahl - big integer library
(HTM) git clone git://git.suckless.org/libzahl
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
zrand.c (3981B)
---
1 /* See LICENSE file for copyright and license details. */
2 #include "internals.h"
3
4 #include <fcntl.h>
5 #include <stdlib.h>
6 #include <time.h>
7 #include <unistd.h>
8
9 #ifndef FAST_RANDOM_PATHNAME
10 # define FAST_RANDOM_PATHNAME "/dev/urandom"
11 #endif
12
13 #ifndef SECURE_RANDOM_PATHNAME
14 # define SECURE_RANDOM_PATHNAME "/dev/random"
15 #endif
16
17
18 static void
19 zrand_libc_rand(void *out, size_t n, void *statep)
20 {
21 static char inited = 0;
22
23 unsigned int ri;
24 double rd;
25 unsigned char *buf = out;
26
27 if (!inited) {
28 inited = 1;
29 srand((unsigned)((intptr_t)out | time(NULL)));
30 }
31
32 while (n--) {
33 ri = (unsigned)rand();
34 rd = (double)ri / ((double)RAND_MAX + 1);
35 #ifdef GOOD_RAND
36 rd *= 256 * 256;
37 ri = (unsigned int)rd;
38 buf[n] = (unsigned char)((ri >> 0) & 255);
39 if (!n--) break;
40 buf[n] = (unsigned char)((ri >> 8) & 255);
41 #else
42 rd *= 256;
43 buf[n] = (unsigned char)rd;
44 #endif
45 }
46
47 (void) statep;
48 }
49
50 static void
51 zrand_libc_rand48(void *out, size_t n, void *statep)
52 {
53 static char inited = 0;
54
55 long int r0, r1;
56 unsigned char *buf = out;
57
58 if (!inited) {
59 inited = 1;
60 srand48((intptr_t)out | time(NULL));
61 }
62
63 while (n--) {
64 r0 = lrand48() & 15;
65 r1 = lrand48() & 15;
66 buf[n] = (unsigned char)((r0 << 4) | r1);
67 }
68
69 (void) statep;
70 }
71
72 static void
73 zrand_libc_random(void *out, size_t n, void *statep)
74 {
75 static char inited = 0;
76
77 long int ri;
78 unsigned char *buf = out;
79
80 if (!inited) {
81 inited = 1;
82 srandom((unsigned)((intptr_t)out | time(NULL)));
83 }
84
85 while (n--) {
86 ri = random();
87 buf[n] = (unsigned char)((ri >> 0) & 255);
88 if (!n--) break;
89 buf[n] = (unsigned char)((ri >> 8) & 255);
90 if (!n--) break;
91 buf[n] = (unsigned char)((ri >> 16) & 255);
92 }
93
94 (void) statep;
95 }
96
97 static void
98 zrand_fd(void *out, size_t n, void *statep)
99 {
100 int fd = *(int *)statep;
101 ssize_t read_just;
102 size_t read_total = 0;
103 char *buf = out;
104
105 while (n) {
106 read_just = read(fd, buf + read_total, n);
107 if (check(read_just < 0))
108 libzahl_failure(errno);
109 read_total += (size_t)read_just;
110 n -= (size_t)read_just;
111 }
112 }
113
114 static void
115 zrand_get_random_bits(z_t r, size_t bits, void (*fun)(void *, size_t, void *), void *statep)
116 {
117 size_t n, chars = CEILING_BITS_TO_CHARS(bits);
118 zahl_char_t mask = 1;
119
120 ENSURE_SIZE(r, chars);
121
122 fun(r->chars, chars * sizeof(zahl_char_t), statep);
123
124 bits = BITS_IN_LAST_CHAR(bits);
125 mask <<= bits;
126 mask -= 1;
127
128 r->chars[chars - 1] &= mask;
129 for (n = chars; n--;) {
130 if (likely(r->chars[n])) {
131 r->used = n + 1;
132 SET_SIGNUM(r, 1);
133 return;
134 }
135 }
136 SET_SIGNUM(r, 0);
137 }
138
139 void
140 zrand(z_t r, enum zranddev dev, enum zranddist dist, z_t n)
141 {
142 #define RANDOM_UNIFORM(RETRY)\
143 do {\
144 if (check(znegative(n)))\
145 libzahl_failure(-ZERROR_NEGATIVE);\
146 bits = zbits(n);\
147 do\
148 zrand_get_random_bits(r, bits, random_fun, statep);\
149 while (RETRY && unlikely(zcmpmag(r, n) > 0));\
150 } while (0)
151
152
153 const char *pathname = 0;
154 size_t bits;
155 int fd = -1;
156 void *statep = 0;
157 void (*random_fun)(void *, size_t, void *) = &zrand_fd;
158
159 switch (dev) {
160 case FAST_RANDOM:
161 pathname = FAST_RANDOM_PATHNAME;
162 break;
163 case SECURE_RANDOM:
164 pathname = SECURE_RANDOM_PATHNAME;
165 break;
166 case LIBC_RAND_RANDOM:
167 random_fun = &zrand_libc_rand;
168 break;
169 case DEFAULT_RANDOM:
170 case FASTEST_RANDOM:
171 case LIBC_RANDOM_RANDOM:
172 random_fun = &zrand_libc_random;
173 break;
174 case LIBC_RAND48_RANDOM:
175 random_fun = &zrand_libc_rand48;
176 break;
177 default:
178 libzahl_failure(EINVAL);
179 }
180
181 if (unlikely(zzero(n))) {
182 SET_SIGNUM(r, 0);
183 return;
184 }
185
186 if (pathname) {
187 fd = open(pathname, O_RDONLY);
188 if (check(fd < 0))
189 libzahl_failure(errno);
190 statep = &fd;
191 }
192
193 switch (dist) {
194 case QUASIUNIFORM:
195 RANDOM_UNIFORM(0);
196 zadd(r, r, libzahl_const_1);
197 zmul(r, r, n);
198 zrsh(r, r, bits);
199 break;
200
201 case UNIFORM:
202 RANDOM_UNIFORM(1);
203 break;
204
205 case MODUNIFORM:
206 RANDOM_UNIFORM(0);
207 if (unlikely(zcmpmag(r, n) > 0))
208 zsub(r, r, n);
209 break;
210
211 default:
212 #if !defined(ZAHL_UNSAFE)
213 libzahl_failure(EINVAL);
214 #endif
215 break;
216 }
217
218 if (fd >= 0)
219 close(fd);
220 }