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 }