libzahl

big integer library
git clone git://git.suckless.org/libzahl
Log | Files | Refs | README | 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 }