This is rebased against the current head of gnulib. 'struct random_data' is now in gnulib, so I've removed the addition of this structure from the patch.
This version also preserves the glibc locking code, albeit as empty macros, so that the code more closely follows what was originally in glibc. Rich. -- Richard Jones, Emerging Technologies, Red Hat http://et.redhat.com/~rjones virt-p2v converts physical machines to virtual machines. Boot with a live CD or over the network (PXE) and turn machines into Xen guests. http://et.redhat.com/~rjones/virt-p2v
>From bffee80359b966e119c62e75e0d102b6be1184bd Mon Sep 17 00:00:00 2001 From: Richard Jones <[EMAIL PROTECTED]> Date: Thu, 23 Oct 2008 16:53:27 +0100 Subject: [PATCH] [PATCH] Implementations of random, srandom, initstate, setstate, rand, srand. --- MODULES.html.sh | 1 + doc/posix-functions/initstate.texi | 8 +- doc/posix-functions/rand.texi | 4 +- doc/posix-functions/random.texi | 8 +- doc/posix-functions/setstate.texi | 8 +- doc/posix-functions/srand.texi | 4 +- doc/posix-functions/srandom.texi | 8 +- lib/random.c | 228 ++++++++++++++++++++++++++++++++++++ lib/stdlib.in.h | 64 ++++++++++- m4/random.m4 | 21 ++++ m4/stdlib_h.m4 | 2 + modules/random | 25 ++++ modules/stdlib | 2 + 13 files changed, 362 insertions(+), 21 deletions(-) create mode 100644 lib/random.c create mode 100644 m4/random.m4 create mode 100644 modules/random diff --git a/MODULES.html.sh b/MODULES.html.sh index e61a644..d70e715 100755 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -2943,6 +2943,7 @@ func_all_modules () func_module quotearg func_module quote func_module readutmp + func_module random func_module random_r func_module sysexits func_module verror diff --git a/doc/posix-functions/initstate.texi b/doc/posix-functions/initstate.texi index fc777f5..e97f631 100644 --- a/doc/posix-functions/initstate.texi +++ b/doc/posix-functions/initstate.texi @@ -4,15 +4,15 @@ POSIX specification: @url{http://www.opengroup.org/susv3xsh/initstate.html} -Gnulib module: --- +Gnulib module: random Portability problems fixed by Gnulib: @itemize [EMAIL PROTECTED] +This function is missing on some platforms: +Solaris 2.4, mingw. @end itemize Portability problems not fixed by Gnulib: @itemize [EMAIL PROTECTED] -This function is missing on some platforms: -Solaris 2.4, mingw. @end itemize diff --git a/doc/posix-functions/rand.texi b/doc/posix-functions/rand.texi index 8b2090f..aecfb10 100644 --- a/doc/posix-functions/rand.texi +++ b/doc/posix-functions/rand.texi @@ -4,10 +4,12 @@ POSIX specification: @url{http://www.opengroup.org/susv3xsh/rand.html} -Gnulib module: --- +Gnulib module: random Portability problems fixed by Gnulib: @itemize [EMAIL PROTECTED] +Provides a high quality PRNG on all platforms. @end itemize Portability problems not fixed by Gnulib: diff --git a/doc/posix-functions/random.texi b/doc/posix-functions/random.texi index 0111a9c..0da0ca4 100644 --- a/doc/posix-functions/random.texi +++ b/doc/posix-functions/random.texi @@ -4,15 +4,15 @@ POSIX specification: @url{http://www.opengroup.org/susv3xsh/random.html} -Gnulib module: --- +Gnulib module: random Portability problems fixed by Gnulib: @itemize [EMAIL PROTECTED] +This function is missing on some platforms: +Solaris 2.4, mingw. @end itemize Portability problems not fixed by Gnulib: @itemize [EMAIL PROTECTED] -This function is missing on some platforms: -Solaris 2.4, mingw. @end itemize diff --git a/doc/posix-functions/setstate.texi b/doc/posix-functions/setstate.texi index 17b43cc..bdb1c09 100644 --- a/doc/posix-functions/setstate.texi +++ b/doc/posix-functions/setstate.texi @@ -4,15 +4,15 @@ POSIX specification: @url{http://www.opengroup.org/susv3xsh/setstate.html} -Gnulib module: --- +Gnulib module: random Portability problems fixed by Gnulib: @itemize [EMAIL PROTECTED] +This function is missing on some platforms: +Solaris 2.4, mingw. @end itemize Portability problems not fixed by Gnulib: @itemize [EMAIL PROTECTED] -This function is missing on some platforms: -Solaris 2.4, mingw. @end itemize diff --git a/doc/posix-functions/srand.texi b/doc/posix-functions/srand.texi index 0a93e2b..a0aab06 100644 --- a/doc/posix-functions/srand.texi +++ b/doc/posix-functions/srand.texi @@ -4,10 +4,12 @@ POSIX specification: @url{http://www.opengroup.org/susv3xsh/srand.html} -Gnulib module: --- +Gnulib module: random Portability problems fixed by Gnulib: @itemize [EMAIL PROTECTED] +Provides a high quality PRNG on all platforms. @end itemize Portability problems not fixed by Gnulib: diff --git a/doc/posix-functions/srandom.texi b/doc/posix-functions/srandom.texi index c083dcb..f4c6a47 100644 --- a/doc/posix-functions/srandom.texi +++ b/doc/posix-functions/srandom.texi @@ -4,15 +4,15 @@ POSIX specification: @url{http://www.opengroup.org/susv3xsh/srandom.html} -Gnulib module: --- +Gnulib module: random Portability problems fixed by Gnulib: @itemize [EMAIL PROTECTED] +This function is missing on some platforms: +Solaris 2.4, mingw. @end itemize Portability problems not fixed by Gnulib: @itemize [EMAIL PROTECTED] -This function is missing on some platforms: -Solaris 2.4, mingw. @end itemize diff --git a/lib/random.c b/lib/random.c new file mode 100644 index 0000000..85310fa --- /dev/null +++ b/lib/random.c @@ -0,0 +1,228 @@ +/* + Copyright (C) 1995, 2005, 2008 Free Software Foundation + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* + * This is derived from the Berkeley source: + * @(#)random.c 5.5 (Berkeley) 7/6/88 + * It was reworked for the GNU C Library by Roland McGrath. + * Rewritten to use reentrant functions by Ulrich Drepper, 1995. + */ + +/* + Copyright (C) 1983 Regents of the University of California. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 4. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE.*/ + +#include <config.h> + +#include <stdlib.h> +#include <inttypes.h> + +/* Locks are not yet implemented in gnulib. These macros allow us to + * preserve the ones which were in glibc. + */ +#define __libc_lock_lock(x) +#define __libc_lock_unlock(x) + +/* x**31 + x**3 + 1. */ +#define TYPE_3 3 +#define BREAK_3 128 +#define DEG_3 31 +#define SEP_3 3 + +/* Initially, everything is set up as if from: + initstate(1, randtbl, 128); + Note that this initialization takes advantage of the fact that srandom + advances the front and rear pointers 10*rand_deg times, and hence the + rear pointer which starts at 0 will also end up at zero; thus the zeroth + element of the state information, which contains info about the current + position of the rear pointer is just + (MAX_TYPES * (rptr - state)) + TYPE_3 == TYPE_3. */ + +static int32_t randtbl[DEG_3 + 1] = + { + TYPE_3, + + -1726662223, 379960547, 1735697613, 1040273694, 1313901226, + 1627687941, -179304937, -2073333483, 1780058412, -1989503057, + -615974602, 344556628, 939512070, -1249116260, 1507946756, + -812545463, 154635395, 1388815473, -1926676823, 525320961, + -1009028674, 968117788, -123449607, 1284210865, 435012392, + -2017506339, -911064859, -370259173, 1132637927, 1398500161, + -205601318, + }; + +static struct random_data unsafe_state = + { +/* FPTR and RPTR are two pointers into the state info, a front and a rear + pointer. These two pointers are always rand_sep places aparts, as they + cycle through the state information. (Yes, this does mean we could get + away with just one pointer, but the code for random is more efficient + this way). The pointers are left positioned as they would be from the call: + initstate(1, randtbl, 128); + (The position of the rear pointer, rptr, is really 0 (as explained above + in the initialization of randtbl) because the state table pointer is set + to point to randtbl[1] (as explained below).) */ + + .fptr = &randtbl[SEP_3 + 1], + .rptr = &randtbl[1], + +/* The following things are the pointer to the state information table, + the type of the current generator, the degree of the current polynomial + being used, and the separation between the two pointers. + Note that for efficiency of random, we remember the first location of + the state information, not the zeroth. Hence it is valid to access + state[-1], which is used to store the type of the R.N.G. + Also, we remember the last location, since this is more efficient than + indexing every time to find the address of the last element to see if + the front and rear pointers have wrapped. */ + + .state = &randtbl[1], + + .rand_type = TYPE_3, + .rand_deg = DEG_3, + .rand_sep = SEP_3, + + .end_ptr = &randtbl[sizeof (randtbl) / sizeof (randtbl[0])] +}; + + +/* Initialize the random number generator based on the given seed. If the + type is the trivial no-state-information type, just remember the seed. + Otherwise, initializes state[] based on the given "seed" via a linear + congruential generator. Then, the pointers are set to known locations + that are exactly rand_sep places apart. Lastly, it cycles the state + information a given number of times to get rid of any initial dependencies + introduced by the L.C.R.N.G. Note that the initialization of randtbl[] + for default usage relies on values produced by this routine. */ +void +srandom (x) + unsigned int x; +{ + __libc_lock_lock (lock); + (void) srandom_r (x, &unsafe_state); + __libc_lock_unlock (lock); +} + + +/* Initialize the state information in the given array of N bytes for + future random number generation. Based on the number of bytes we + are given, and the break values for the different R.N.G.'s, we choose + the best (largest) one we can and set things up for it. srandom is + then called to initialize the state information. Note that on return + from srandom, we set state[-1] to be the type multiplexed with the current + value of the rear pointer; this is so successive calls to initstate won't + lose this information and will be able to restart with setstate. + Note: The first thing we do is save the current state, if any, just like + setstate so that it doesn't matter when initstate is called. + Returns a pointer to the old state. */ +char * +initstate (seed, arg_state, n) + unsigned int seed; + char *arg_state; + size_t n; +{ + int32_t *ostate; + + __libc_lock_lock (lock); + + ostate = &unsafe_state.state[-1]; + + initstate_r (seed, arg_state, n, &unsafe_state); + + __libc_lock_unlock (lock); + + return (char *) ostate; +} + + +/* Restore the state from the given state array. + Note: It is important that we also remember the locations of the pointers + in the current state information, and restore the locations of the pointers + from the old state information. This is done by multiplexing the pointer + location into the zeroth word of the state information. Note that due + to the order in which things are done, it is OK to call setstate with the + same state as the current state + Returns a pointer to the old state information. */ +char * +setstate (arg_state) + char *arg_state; +{ + int32_t *ostate; + + __libc_lock_lock (lock); + + ostate = &unsafe_state.state[-1]; + + if (setstate_r (arg_state, &unsafe_state) < 0) + ostate = NULL; + + __libc_lock_unlock (lock); + + return (char *) ostate; +} + + +/* If we are using the trivial TYPE_0 R.N.G., just do the old linear + congruential bit. Otherwise, we do our fancy trinomial stuff, which is the + same in all the other cases due to all the global variables that have been + set up. The basic operation is to add the number at the rear pointer into + the one at the front pointer. Then both pointers are advanced to the next + location cyclically in the table. The value returned is the sum generated, + reduced to 31 bits by throwing away the "least random" low bit. + Note: The code takes advantage of the fact that both the front and + rear pointers can't wrap on the same call by not testing the rear + pointer if the front one has wrapped. Returns a 31-bit random number. */ + +long int +random () +{ + int32_t retval; + + __libc_lock_lock (lock); + + (void) random_r (&unsafe_state, &retval); + + __libc_lock_unlock (lock); + + return retval; +} diff --git a/lib/stdlib.in.h b/lib/stdlib.in.h index f6ebe25..d69ce52 100644 --- a/lib/stdlib.in.h +++ b/lib/stdlib.in.h @@ -234,12 +234,70 @@ extern int putenv (char *string); #endif +#if @GNULIB_RANDOM@ +# if [EMAIL PROTECTED]@ + +/* Remove any old definitions of RAND_MAX, since they will be + * incorrect for the functions we are replacing. + * eg. On Windows this is defined as 0x7fff. + */ +# undef RAND_MAX +# define RAND_MAX 2147483647 + +void srandom (unsigned int seed); +char *initstate (unsigned int seed, char *buf, size_t buf_size); +char *setstate (char *arg_state); +long int random (); +# undef srand +# define srand(s) srandom(s) +# undef rand +# define rand() ((int)random()) +# endif +#elif defined GNULIB_POSIXCHECK +# undef random +# define random() \ + (GL_LINK_WARNING ("random is unportable - " \ + "use gnulib module random for portability"), \ + random ()) +# undef initstate +# define initstate(s,b,sz) \ + (GL_LINK_WARNING ("initstate is unportable - " \ + "use gnulib module random for portability"), \ + initstate (s,b,sz)) +# undef srandom +# define srandom(s) \ + (GL_LINK_WARNING ("srandom is unportable - " \ + "use gnulib module random for portability"), \ + srandom (s)) +# undef setstate +# define setstate(a) \ + (GL_LINK_WARNING ("setstate is unportable - " \ + "use gnulib module random for portability"), \ + setstate (a)) +# undef srand +# define srand(s) \ + (GL_LINK_WARNING ("srand is unportable and often poor quality - " \ + "use gnulib module random for portability"), \ + srand (a)) +# undef rand +# define rand() \ + (GL_LINK_WARNING ("rand is unportable and often poor quality - " \ + "use gnulib module random for portability"), \ + rand ()) +#endif + + #if @GNULIB_RANDOM_R@ # if [EMAIL PROTECTED]@ -# ifndef RAND_MAX -# define RAND_MAX 2147483647 -# endif +/* Remove any old definitions of RAND_MAX, since they will be + * incorrect for the functions we are replacing. + * eg. On Windows this is defined as 0x7fff. + */ +#undef RAND_MAX +#define RAND_MAX 2147483647 + +#include <stdint.h> /* For int32_t */ int srandom_r (unsigned int seed, struct random_data *rand_state); int initstate_r (unsigned int seed, char *buf, size_t buf_size, diff --git a/m4/random.m4 b/m4/random.m4 new file mode 100644 index 0000000..d3987a8 --- /dev/null +++ b/m4/random.m4 @@ -0,0 +1,21 @@ +# serial 1 +dnl Copyright (C) 2008 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_RANDOM], +[ + AC_REQUIRE([gl_STDLIB_H_DEFAULTS]) + AC_CHECK_FUNCS([random]) + if test $ac_cv_func_random = no; then + HAVE_RANDOM=0 + AC_LIBOBJ([random]) + gl_PREREQ_RANDOM + fi +]) + +# Prerequisites of lib/random.c. +AC_DEFUN([gl_PREREQ_RANDOM], [ + : +]) diff --git a/m4/stdlib_h.m4 b/m4/stdlib_h.m4 index 582db13..73e078e 100644 --- a/m4/stdlib_h.m4 +++ b/m4/stdlib_h.m4 @@ -31,6 +31,7 @@ AC_DEFUN([gl_STDLIB_H_DEFAULTS], GNULIB_MKDTEMP=0; AC_SUBST([GNULIB_MKDTEMP]) GNULIB_MKSTEMP=0; AC_SUBST([GNULIB_MKSTEMP]) GNULIB_PUTENV=0; AC_SUBST([GNULIB_PUTENV]) + GNULIB_RANDOM=0; AC_SUBST([GNULIB_RANDOM]) GNULIB_RANDOM_R=0; AC_SUBST([GNULIB_RANDOM_R]) GNULIB_RPMATCH=0; AC_SUBST([GNULIB_RPMATCH]) GNULIB_SETENV=0; AC_SUBST([GNULIB_SETENV]) @@ -45,6 +46,7 @@ AC_DEFUN([gl_STDLIB_H_DEFAULTS], HAVE_MALLOC_POSIX=1; AC_SUBST([HAVE_MALLOC_POSIX]) HAVE_MKDTEMP=1; AC_SUBST([HAVE_MKDTEMP]) HAVE_REALLOC_POSIX=1; AC_SUBST([HAVE_REALLOC_POSIX]) + HAVE_RANDOM=1; AC_SUBST([HAVE_RANDOM]) HAVE_RANDOM_R=1; AC_SUBST([HAVE_RANDOM_R]) HAVE_RPMATCH=1; AC_SUBST([HAVE_RPMATCH]) HAVE_SETENV=1; AC_SUBST([HAVE_SETENV]) diff --git a/modules/random b/modules/random new file mode 100644 index 0000000..ab33752 --- /dev/null +++ b/modules/random @@ -0,0 +1,25 @@ +Description: +random number generator + +Files: +lib/random.c +m4/random.m4 + +Depends-on: +stdlib +random_r + +configure.ac: +gl_FUNC_RANDOM +gl_STDLIB_MODULE_INDICATOR([random]) + +Makefile.am: + +Include: +#include <stdlib.h> + +License: +LGPLv2+ + +Maintainer: +Richard W.M. Jones diff --git a/modules/stdlib b/modules/stdlib index edd9e45..1417fce 100644 --- a/modules/stdlib +++ b/modules/stdlib @@ -33,6 +33,7 @@ stdlib.h: stdlib.in.h -e 's|@''GNULIB_MKDTEMP''@|$(GNULIB_MKDTEMP)|g' \ -e 's|@''GNULIB_MKSTEMP''@|$(GNULIB_MKSTEMP)|g' \ -e 's|@''GNULIB_PUTENV''@|$(GNULIB_PUTENV)|g' \ + -e 's|@''GNULIB_RANDOM''@|$(GNULIB_RANDOM)|g' \ -e 's|@''GNULIB_RANDOM_R''@|$(GNULIB_RANDOM_R)|g' \ -e 's|@''GNULIB_RPMATCH''@|$(GNULIB_RPMATCH)|g' \ -e 's|@''GNULIB_SETENV''@|$(GNULIB_SETENV)|g' \ @@ -46,6 +47,7 @@ stdlib.h: stdlib.in.h -e 's|@''HAVE_MALLOC_POSIX''@|$(HAVE_MALLOC_POSIX)|g' \ -e 's|@''HAVE_MKDTEMP''@|$(HAVE_MKDTEMP)|g' \ -e 's|@''HAVE_REALLOC_POSIX''@|$(HAVE_REALLOC_POSIX)|g' \ + -e 's|@''HAVE_RANDOM''@|$(HAVE_RANDOM)|g' \ -e 's|@''HAVE_RANDOM_R''@|$(HAVE_RANDOM_R)|g' \ -e 's|@''HAVE_RPMATCH''@|$(HAVE_RPMATCH)|g' \ -e 's|@''HAVE_SETENV''@|$(HAVE_SETENV)|g' \ -- 1.6.0.3