1/*
2 *   Copyright (c) International Business Machines Corp., 2001-2004
3 *
4 *   This program is free software;  you can redistribute it and/or modify
5 *   it under the terms of the GNU General Public License as published by
6 *   the Free Software Foundation; either version 2 of the License, or
7 *   (at your option) any later version.
8 *
9 *   This program is distributed in the hope that it will be useful,
10 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
11 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12 *   the GNU General Public License for more details.
13 *
14 *   You should have received a copy of the GNU General Public License
15 *   along with this program;  if not, write to the Free Software
16 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18#include <stdlib.h>
19#include <stdio.h>
20#include <sys/types.h>
21#include <sys/stat.h>
22#include <sys/time.h>
23#include <fcntl.h>
24#include <unistd.h>
25#include <limits.h>
26#include <inttypes.h>
27#include <assert.h>
28
29#include "config.h"
30#include "rand.h"
31#include "util.h"
32
33#define RANDSRC "/dev/urandom"
34
35static int randfd = -1;
36
37/* close the file after we're done with the benchmark */
38void randcleanup(void)
39{
40	if (randfd > 0)
41		close(randfd);
42}
43
44/* We fill up the array with random bits from RANDSRC here and set index */
45/* to 0 */
46/* pre: state->size must be set and state->mt must be allocated! */
47static void sgenrand(randdata_t * state)
48{
49	int got = 0;
50	got = read(randfd, state->mt, state->size);
51	if (got != state->size) {
52		int i;
53		/* fall back on lrand48 */
54		/* printf("fallback_rand\n"); */
55
56		for (i = got; i < state->size; i += 4) {
57			long int rand = 0;
58#ifdef HAVE_LRAND48
59			lrand48_r(&(state->data), &rand);
60#else
61			rand = random();
62#endif
63			assert(rand != 0);
64			state->mt[i] = (rand >> 24) & (512 - 1);
65			state->mt[i + 1] = (rand >> 16) & (512 - 1);
66			state->mt[i + 2] = (rand >> 8) & (512 - 1);
67			state->mt[i + 3] = (rand) & (512 - 1);
68		}
69
70	}
71	state->mti = 0;
72}
73
74/* returns 8 random bits */
75static uint8_t genrand8(randdata_t * state)
76{
77	unsigned long ret = 0;
78	if (state->mti >= state->size) {
79/*		sgenrand(state); */
80		state->mti = 0;
81	}
82	ret = state->mt[state->mti];
83	state->mti++;
84	return ret;
85}
86
87/* returns 32 random bits */
88static uint32_t genrand32(randdata_t * state)
89{
90	uint8_t bytes[4];
91	uint32_t ret = 0;
92
93	bytes[0] = genrand8(state);
94	bytes[1] = genrand8(state);
95	bytes[2] = genrand8(state);
96	bytes[3] = genrand8(state);
97
98	ret = *((uint32_t *) bytes);	/* !!! hack */
99	return ret;
100}
101
102void init_random(randdata_t * state, uint32_t iter)
103{
104	struct timeval time;
105	if (iter == 0)
106		state->size = MIN_RANDBUF_SIZE * AVG_ITR_RNDBTS;
107	else if (iter > MAX_RANDBUF_SIZE)
108		state->size = MAX_RANDBUF_SIZE * AVG_ITR_RNDBTS;
109	else
110		state->size = iter * AVG_ITR_RNDBTS;
111
112	state->mt = ffsb_malloc(state->size);
113
114	/* !!!! racy? add pthread_once stuff later  */
115	if ((randfd < 0) && (randfd = open(RANDSRC, O_RDONLY)) < 0) {
116		perror("open " RANDSRC);
117		exit(1);
118	}
119	sgenrand(state);
120	gettimeofday(&time, NULL);
121#ifdef HAVE_LRAND48
122	srand48_r(time.tv_sec, &state->data);
123#endif
124}
125
126void destroy_random(randdata_t * rd)
127{
128	free(rd->mt);
129}
130
131/*
132 * I've taken the liberty of slightly redesigning this stuff.
133 * Instead of simply getting the full word of random bits
134 * and throwing away most of it using the mod operator,
135 * we should only get byte-sized chunks of random bits and
136 * construct our random number that way with less wasteage - SR
137 */
138uint32_t getrandom(randdata_t * state, uint32_t mod)
139{
140
141	uint8_t bytes[4] = { 0, 0, 0, 0 };
142	uint32_t ret;
143	int num_bytes = 4;
144	int i;
145
146	if ((mod == 0) || (mod == 1))
147		return 0;
148
149	if (!(mod >> 8))
150		num_bytes = 1;
151	else if (!(mod >> 16))
152		num_bytes = 2;
153	else if (!(mod >> 24))
154		num_bytes = 3;
155
156	for (i = 0; i < num_bytes; i++)
157		bytes[i] = genrand8(state);
158
159	ret = (bytes[3] << 24) + (bytes[2] << 16) + (bytes[1] << 8) + bytes[0];
160
161	return ret % mod;
162}
163
164uint64_t getllrandom(randdata_t * state, uint64_t mod)
165{
166	uint64_t result = 0;
167	uint64_t high = 0;
168	uint32_t low = 0;
169
170	if (mod == 0)
171		return 0;
172
173	/* ULONG_MAX comes from limits.h */
174	if (mod < ULONG_MAX)
175		return (uint64_t) getrandom(state, (uint32_t) mod);
176
177	high = genrand32(state);
178
179	low = genrand32(state);
180
181	result = high << 32;
182	result |= (uint64_t) low;
183
184	assert(result != 0);
185	assert(result > 0);
186
187	return result % mod;
188}
189