*/
#include "config.h"
+#define _CRT_RAND_S
#include <math.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-
#include "grand.h"
+#include "genviron.h"
#include "gmain.h"
#include "gmem.h"
#include "gtestutils.h"
#include "gthread.h"
-#include "gthreadprivate.h"
+
+#ifdef G_OS_UNIX
+#include <unistd.h>
+#endif
#ifdef G_OS_WIN32
-#include <process.h> /* For getpid() */
+#include <stdlib.h>
#endif
/**
- * SECTION: random_numbers
+ * SECTION:random_numbers
* @title: Random Numbers
* @short_description: pseudo-random number generator
*
* The following functions allow you to use a portable, fast and good
- * pseudo-random number generator (PRNG). It uses the Mersenne Twister
- * PRNG, which was originally developed by Makoto Matsumoto and Takuji
- * Nishimura. Further information can be found at
- * <ulink url="http://www.math.keio.ac.jp/~matumoto/emt.html">
- * www.math.keio.ac.jp/~matumoto/emt.html</ulink>.
+ * pseudo-random number generator (PRNG).
+ *
+ * <warning><para>Do not use this API for cryptographic purposes such as key
+ * generation, nonces, salts or one-time pads.</para></warning>
+ *
+ * This PRNG is suitable for non-cryptographic use such as in games
+ * (shuffling a card deck, generating levels), generating data for a
+ * test suite, etc. If you need random data for cryptographic
+ * purposes, it is recommended to use platform-specific APIs such as
+ * <literal>/dev/random</literal> on Unix, or CryptGenRandom() on
+ * Windows.
+ *
+ * GRand uses the Mersenne Twister PRNG, which was originally
+ * developed by Makoto Matsumoto and Takuji Nishimura. Further
+ * information can be found at <ulink
+ * url="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html">
+ * http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html</ulink>.
*
* If you just need a random number, you simply call the
* <function>g_random_*</function> functions, which will create a
**/
G_LOCK_DEFINE_STATIC (global_random);
-static GRand* global_random = NULL;
/* Period parameters */
#define N 624
static guint
get_random_version (void)
{
- static gboolean initialized = FALSE;
+ static gsize initialized = FALSE;
static guint random_version;
-
- if (!initialized)
+
+ if (g_once_init_enter (&initialized))
{
const gchar *version_string = g_getenv ("G_RANDOM_VERSION");
if (!version_string || version_string[0] == '\000' ||
version_string);
random_version = 22;
}
- initialized = TRUE;
+ g_once_init_leave (&initialized, TRUE);
}
return random_version;
}
-/* This is called from g_thread_init(). It's used to
- * initialize some static data in a threadsafe way.
- */
-void
-_g_rand_thread_init (void)
-{
- (void)get_random_version ();
-}
-
struct _GRand
{
guint32 mt[N]; /* the array for the state vector */
*
* Creates a new random number generator initialized with a seed taken
* either from <filename>/dev/urandom</filename> (if existing) or from
- * the current time (as a fallback).
+ * the current time (as a fallback). On Windows, the seed is taken from
+ * rand_s().
*
* Return value: the new #GRand.
**/
g_rand_new (void)
{
guint32 seed[4];
- GTimeVal now;
#ifdef G_OS_UNIX
static gboolean dev_urandom_exists = TRUE;
+ GTimeVal now;
if (dev_urandom_exists)
{
FILE* dev_urandom;
do
- {
- errno = 0;
+ {
dev_urandom = fopen("/dev/urandom", "rb");
}
- while G_UNLIKELY (errno == EINTR);
+ while G_UNLIKELY (dev_urandom == NULL && errno == EINTR);
if (dev_urandom)
{
else
dev_urandom_exists = FALSE;
}
-#else
- static gboolean dev_urandom_exists = FALSE;
-#endif
if (!dev_urandom_exists)
{
seed[0] = now.tv_sec;
seed[1] = now.tv_usec;
seed[2] = getpid ();
-#ifdef G_OS_UNIX
seed[3] = getppid ();
-#else
- seed[3] = 0;
-#endif
}
+#else /* G_OS_WIN32 */
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (seed); i++)
+ rand_s (&seed[i]);
+#endif
return g_rand_new_with_seed_array (seed, 4);
}
/* [KNUTH 1981, The Art of Computer Programming */
/* Vol. 2 (2nd Ed.), pp102] */
- if (seed == 0) /* This would make the PRNG procude only zeros */
+ if (seed == 0) /* This would make the PRNG produce only zeros */
seed = 0x6b842128; /* Just set it to another number */
rand->mt[0]= seed;
/**
* g_rand_boolean:
* @rand_: a #GRand.
- * @Returns: a random #gboolean.
*
* Returns a random #gboolean from @rand_. This corresponds to a
* unbiased coin toss.
+ *
+ * Returns: a random #gboolean.
**/
/**
* g_rand_int:
gdouble
g_rand_double_range (GRand* rand, gdouble begin, gdouble end)
{
- return g_rand_double (rand) * (end - begin) + begin;
+ gdouble r;
+
+ r = g_rand_double (rand);
+
+ return r * end - (r - 1) * begin;
+}
+
+static GRand *
+get_global_random (void)
+{
+ static GRand *global_random;
+
+ /* called while locked */
+ if (!global_random)
+ global_random = g_rand_new ();
+
+ return global_random;
}
/**
* g_random_boolean:
- * @Returns: a random #gboolean.
*
* Returns a random #gboolean. This corresponds to a unbiased coin toss.
+ *
+ * Returns: a random #gboolean.
**/
/**
* g_random_int:
{
guint32 result;
G_LOCK (global_random);
- if (!global_random)
- global_random = g_rand_new ();
-
- result = g_rand_int (global_random);
+ result = g_rand_int (get_global_random ());
G_UNLOCK (global_random);
return result;
}
{
gint32 result;
G_LOCK (global_random);
- if (!global_random)
- global_random = g_rand_new ();
-
- result = g_rand_int_range (global_random, begin, end);
+ result = g_rand_int_range (get_global_random (), begin, end);
G_UNLOCK (global_random);
return result;
}
{
double result;
G_LOCK (global_random);
- if (!global_random)
- global_random = g_rand_new ();
-
- result = g_rand_double (global_random);
+ result = g_rand_double (get_global_random ());
G_UNLOCK (global_random);
return result;
}
{
double result;
G_LOCK (global_random);
- if (!global_random)
- global_random = g_rand_new ();
-
- result = g_rand_double_range (global_random, begin, end);
+ result = g_rand_double_range (get_global_random (), begin, end);
G_UNLOCK (global_random);
return result;
}
g_random_set_seed (guint32 seed)
{
G_LOCK (global_random);
- if (!global_random)
- global_random = g_rand_new_with_seed (seed);
- else
- g_rand_set_seed (global_random, seed);
+ g_rand_set_seed (get_global_random (), seed);
G_UNLOCK (global_random);
}