cvs: php4(RAND_REDESIGN) /ext/standard Makefile.in basic_functions.h php_rand.h rand.c rand_mt.c rand_sys.c
| From: | Jeroen van Wolffelaar | Date: | Sun, 26 Aug 2001 22:47:55 +0000 |
| Subject: | cvs: php4(RAND_REDESIGN) /ext/standard Makefile.in basic_functions.h php_rand.h rand.c rand_mt.c rand_sys.c | ||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-7087@lists.php.net to get a copy of this message | ||
jeroen Sun Aug 26 18:47:55 2001 EDT
Added files: (Branch: RAND_REDESIGN)
/php4/ext/standard rand_sys.c
Modified files:
/php4/ext/standard Makefile.in basic_functions.h php_rand.h rand.c
rand_mt.c
Log:
- Different approach to handling multiple random number generators,
now it is way more general, adding a new generator is way more
easier. Also elimination the need for a lot of switches.
- Made rand() Thread Safe
- Made sure this doesn't compile anymore ;-)
- PHPAPI functions now behave (almost) as intended, this is not yet
the case for PHP_FUNCTION functions.
Index: php4/ext/standard/Makefile.in diff -u php4/ext/standard/Makefile.in:1.20.4.1 php4/ext/standard/Makefile.in:1.20.4.2 --- php4/ext/standard/Makefile.in:1.20.4.1 Wed Aug 22 17:53:21 2001 +++ php4/ext/standard/Makefile.in Sun Aug 26 18:47:55 2001 @@ -5,7 +5,8 @@ dir.c dl.c dns.c exec.c file.c filestat.c flock_compat.c \ formatted_print.c fsock.c head.c html.c image.c info.c iptc.c lcg.c \ link.c mail.c math.c md5.c metaphone.c microtime.c pack.c pageinfo.c \ - parsedate.c quot_print.c rand.c rand_mt.c reg.c soundex.c string.c scanf.c \ + parsedate.c quot_print.c rand.c rand_sys.c rand_mt.c reg.c \ + soundex.c string.c scanf.c \ syslog.c type.c uniqid.c url.c url_scanner.c var.c assert.c \ strnatcmp.c levenshtein.c incomplete_class.c url_scanner_ex.c \ ftp_fopen_wrapper.c http_fopen_wrapper.c php_fopen_wrapper.c credits.c Index: php4/ext/standard/basic_functions.h diff -u php4/ext/standard/basic_functions.h:1.88.4.1 php4/ext/standard/basic_functions.h:1.88.4.2 --- php4/ext/standard/basic_functions.h:1.88.4.1 Fri Aug 24 19:47:27 2001 +++ php4/ext/standard/basic_functions.h Sun Aug 26 18:47:55 2001 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: basic_functions.h,v 1.88.4.1 2001/08/24 23:47:27 jeroen Exp $ */ +/* $Id: basic_functions.h,v 1.88.4.2 2001/08/26 22:47:55 jeroen Exp $ */ #ifndef BASIC_FUNCTIONS_H #define BASIC_FUNCTIONS_H @@ -173,6 +173,9 @@ /* rand.c */ int rand_generator; /* current ini-setting */ int rand_generator_current; /* current (by overriding by [mt_]srand) */ + + /* rand_sys.c */ + unsigned int rand_sys_seed; /* Current seed for system-rand() (necessary for thread-safety) */ /* rand_mt.c */ php_uint32 state[MT_N+1]; /* state vector + 1 extra to not violate ANSI C */ Index: php4/ext/standard/php_rand.h diff -u php4/ext/standard/php_rand.h:1.8.8.2 php4/ext/standard/php_rand.h:1.8.8.3 --- php4/ext/standard/php_rand.h:1.8.8.2 Fri Aug 24 19:47:27 2001 +++ php4/ext/standard/php_rand.h Sun Aug 26 18:47:55 2001 @@ -15,11 +15,12 @@ | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> | | Zeev Suraski <zeev@zend.com> | | Pedro Melo <melo@ip.pt> | + | Jeroen van Wolffelaar <jeroen@php.net> | | | | Based on code from: Shawn Cokus <Cokus@math.washington.edu> | +----------------------------------------------------------------------+ */ -/* $Id: php_rand.h,v 1.8.8.2 2001/08/24 23:47:27 jeroen Exp $ */ +/* $Id: php_rand.h,v 1.8.8.3 2001/08/26 22:47:55 jeroen Exp $ */ /* Layout implementation random functions * @@ -56,27 +57,51 @@ * --Jeroen */ +/* TODO: + * - make constants available to PHP-user + * - MINFO section about which random number generators are available + * - Nuke randmax by enhancing PHP_RAND_RANGE to work well in the case of a + * greater request than the real (internal) randmax is + * - Implement LCG + * - Implement a real-random source? (via internet, and/or /dev/urandom?) + * - Can lrand48 be thread-safe? + * - Is random() useful sometimes? + * - Which system algorithms are available, maybe name them after real + * algorithm by compile-time detection? + * - Get this to compile :-) + */ #ifndef PHP_RAND_H #define PHP_RAND_H #include <stdlib.h> -#ifndef RAND_MAX -#define RAND_MAX (1<<15) -#endif - -#if HAVE_LRAND48 -#define php_randmax_sys() 2147483647 -#else -#define php_randmax_sys() RAND_MAX -#endif - -/* - * Melo: it could be 2^^32 but we only use 2^^31 to maintain - * compatibility with the previous php_rand - */ -#define php_randmax_mt() ((long)(0x7FFFFFFF)) /* 2^^31 - 1 */ +/* FIXME: that '_php_randgen_entry' needed, or not? */ +typedef struct _php_randgen_entry { + void (*srand)(long seed); + long (*rand)(void); + long randmax; + char *ini_str; +} php_randgen_entry; + +php_randgen_entry *php_randgen_entries; + +#define PHP_SRAND(which,seed) (php_randgen_entry[which]->srand(seed)) +#define PHP_RAND(which) (php_randgen_entry[which]->rand()) +#define PHP_RANDMAX(which) (php_randgen_entry[which].randmax) +#define PHP_RAND_INISTR(which) (php_randgen_entry[which].ini_str) + +/* Define random generator constants */ +#define PHP_RAND_SYS 0 +#define PHP_RAND_LRAND48 1 +#define PHP_RAND_MT 2 +#define PHP_RAND_LCG 3 + +#define PHP_RAND_DEFAULT PHP_RAND_MT + +/* how many there are */ +#define PHP_RAND_NUMRANDS 4 +/* Proto's */ PHP_FUNCTION(srand); PHP_FUNCTION(rand); PHP_FUNCTION(getrandmax); @@ -87,45 +112,6 @@ PHPAPI long php_rand(void); PHPAPI long php_rand_range(long min, long max); PHPAPI long php_randmax(void); -long php_rand_mt(void); -void php_srand_mt(long seed TSRMLS_DC); - -/* Define rand Function wrapper */ -#ifdef HAVE_RANDOM -#define php_rand_sys() random() -#else -#ifdef HAVE_LRAND48 -#define php_rand_sys() lrand48() -#else -#define php_rand_sys() rand() -#endif -#endif - -/* Define srand Function wrapper */ -#ifdef HAVE_SRANDOM -#define php_srand_sys(seed) srandom((unsigned int)seed) -#else -#ifdef HAVE_SRAND48 -#define php_srand_sys(seed) srand48((long)seed) -#else -#define php_srand_sys(seed) srand((unsigned int)seed) -#endif -#endif - -/* Define random generator constants */ -#define RAND_SYS 1 -#define RAND_MT 2 -#define RAND_LCG 3 -#define RAND_SYS_STR "system" -#define RAND_MT_STR "mt" -#define RAND_LCG_STR "lcg" - -#define RAND_DEFAULT RAND_MT -#define RAND_DEFAULT_STR RAND_MT_STR - - -/* BC */ -#define PHP_RAND_MAX php_randmax() #endif /* PHP_RAND_H */ Index: php4/ext/standard/rand.c diff -u php4/ext/standard/rand.c:1.31.4.2 php4/ext/standard/rand.c:1.31.4.3 --- php4/ext/standard/rand.c:1.31.4.2 Fri Aug 24 19:47:27 2001 +++ php4/ext/standard/rand.c Sun Aug 26 18:47:55 2001 @@ -17,7 +17,7 @@ | Pedro Melo <melo@ip.pt> | +----------------------------------------------------------------------+ */ -/* $Id: rand.c,v 1.31.4.2 2001/08/24 23:47:27 jeroen Exp $ */ +/* $Id: rand.c,v 1.31.4.3 2001/08/26 22:47:55 jeroen Exp $ */ #include <stdlib.h> @@ -32,7 +32,19 @@ /* See php_rand.h for information about layout */ -#define SRAND_A_RANDOM_SEED (time(0) * getpid() * (php_combined_lcg(TSRMLS_C) * 10000.0)) /* something with microtime? */ +#if 0 +#define RANDGEN_ENTRY(intval, lower, upper, has_seed) \ + php_randgen_entry[intval] = { \ + (has_seed) ? php_rand_##lower : NULL, \ + php_rand_##lower, \ + PHP_RANDMAX_##upper, \ + "lower" \ + }; +#endif + +#define FOREACH_RANDGEN(var,i) for ( (var) = php_randgen_entry[(i)=0] ; (var) < PHP_RAND_NUMGENS ; (var) = php_randgen_entry[++(i)] ) + +php_randgen_entry php_randgen_entries[PHP_RAND_NUMGENS]; /* TODO: check that this function is called on the start of each script * execution: not more often, not less often. @@ -43,29 +55,30 @@ */ PHP_RINIT_FUNCTION(rand) { + register php_randgen_entry *randgen; + register int i; + /* seed all number-generators */ /* FIXME: or seed relevant numgen on init/update ini-entry? */ - php_srand_sys(SRAND_A_RANDOM_SEED); - php_srand_mt(SRAND_A_RANDOM_SEED); + FOREACH_RANDGEN(randgen,i) { + if (randgen.srand) { +#define SRAND_A_RANDOM_SEED (time(0) * getpid() * (php_combined_lcg(TSRMLS_C) * 10000.0)) /* something with microtime? */ + randgen->srand(SRAND_A_RANDOM_SEED); + } + } } /* INI */ static int randgen_str_to_int(char *str, int strlen) { - /* manually check all cases, or some loop to automate this - * kind of stuff, so that a new random number generator - * can be added more easily? - * - * --jeroen - */ - if (!strcasecmp(str,RAND_SYS_STR)) { - return RAND_SYS; - } else if (!strcasecmp(str,RAND_MT_STR)) { - return RAND_MT; - } else if (!strcasecmp(str,RAND_LCG_STR)) { - return RAND_LCG; + register php_randgen_entry *randgen; + register int i; + + FOREACH_RANDGEN(randgen,i) { + if (!strcasecmp(str, randgen.ini_str)) + return i; } - return 0; /* FIXME: include that f*** .h that has FALSE */ + return -1; } /* FIXME: check that this is called on initial ini-parsing too */ @@ -75,7 +88,7 @@ /* Set BG(rand_generator) to the correct integer value indicating * ini-setting */ BG(rand_generator) = randgen_str_to_int(new_value, new_value_length); - if (!BG(rand_generator)) { + if (BG(rand_generator) == -1) { /* FIXME: is this possible? What happens if this occurs during * ini-parsing at startup? */ php_error(E_WARNING,"Invalid value for random_number_generator: \"%s\"", new_value); @@ -89,7 +102,7 @@ } PHP_INI_BEGIN() - PHP_INI_ENTRY("random_number_generator", RAND_DEFAULT_STR, PHP_INI_ALL, OnUpdateRandGen) + PHP_INI_ENTRY("random_number_generator", PHP_RAND_INISTR(PHP_RAND_DEFAULT), PHP_INI_ALL, OnUpdateRandGen) PHP_INI_END() /* srand */ @@ -97,25 +110,8 @@ /* {{{ PHPAPI void php_srand(void) */ PHPAPI void php_srand(void) { - /* php_srand_2( 1e6d * microtime() , protocol-specified-in-php.ini ) */ - php_error(E_ERROR,"Not yet implemented"); -} -/* }}} */ - -/* {{{ void php_srand2(long seed, int alg) */ -static inline void php_srand2(long seed, int alg) -{ - switch (alg) { - case RAND_SYS: - php_srand_sys(seed); - return; - case RAND_MT: - php_srand_mt(seed TSRMLS_CC); - return; - default: - php_error(E_ERROR,"Bug, please report (php_srand2-%d)",alg); - return; - } + BG(rand_generator_current) = BG(rand_generator); + PHP_SRAND(BG(rand_generator), SRAND_A_RANDOM_SEED); } /* }}} */ @@ -131,18 +127,19 @@ zend_get_parameters_ex(1, &arg); \ convert_to_long_ex(arg); \ \ - php_srand2((*arg)->value.lval, type); \ + BG(rand_generator_current) = type; \ + PHP_SRAND(type, Z_LVAL_PP(arg)); \ } /* }}} */ /* {{{ proto void srand(int seed) Seeds random number generator */ -pim_srand_common(srand,RAND_SYS) +pim_srand_common(srand,PHP_RAND_SYS) /* }}} */ /* {{{ proto void mt_srand(int seed) Seeds random number generator */ -pim_srand_common(mt_srand,RAND_MT) +pim_srand_common(mt_srand,PHP_RAND_MT) /* }}} */ /* rand */ @@ -150,13 +147,12 @@ /* {{{ PHPAPI long php_rand(void) */ PHPAPI long php_rand(void) { - /* algorithm = BG(current_alg) */ - return php_rand_sys(); + return PHP_RAND(BG(rand_generator_current)); } /* }}} */ -/* {{{ macro: php_map_a_range */ -#define php_map_a_range(number,min,max,MAX) { \ +/* {{{ macro: PHP_RAND_RANGE */ +#define PHP_RAND_RANGE(which,min,max,result) { \ /* * A bit of tricky math here. We want to avoid using a modulus because * that simply tosses the high-order bits and might skew the distribution @@ -178,66 +174,48 @@ * chance, it's ignored. * * --Rasmus and Jeroen - */ \ - if ((max) < (min)) { \ + */ \ + (result) = PHP_RAND(which); \ + if ((max) < (min)) { \ php_error(E_WARNING, "%s(): Invalid range: %ld..%ld (minimum can't be larger than maximum)", \ get_active_function_name(TSRMLS_C), (min), (max)); \ - } else if ( (max) - (min) > (MAX) ) { \ + } else if ( (max) - (min) > PHP_RANDMAX(which) ) { \ /* TODO: this can done better, get two numbers and combine... */ \ php_error(E_WARNING, "%s(): Invalid range: %ld..%ld (can't give that much randomness)", \ get_active_function_name(TSRMLS_C), (min), (max)); \ } \ - (number) = (min) + (int) ((double)((max)-(min)+1) * (number)/(MAX+1.0)); \ -} -/* }}} */ - -/* {{{ long php_rand_range2(long min, long max, int alg) - * Temporary hack function */ -static inline long php_rand_range2(long min, long max, int alg) -{ - long number; - - switch (alg) { - case RAND_SYS: - number = php_rand_sys(); - php_map_a_range(number,min,max,php_randmax_sys()); - return number; - case RAND_MT: - number = php_rand_mt(); - php_map_a_range(number,min,max,php_randmax_mt()); - return number; - default: - php_error(E_ERROR,"Bug, please report (php_rand_range2-%d)",alg); - return; - } + (result) = (min) + (long) ((double)((max)-(min)+1) * (result)/(PHP_RANDMAX(which)+1.0)); \ } /* }}} */ /* {{{ PHPAPI long php_rand_range(long min, long max) */ PHPAPI long php_rand_range(long min, long max) { - /* will be php.ini & srand aware */ - return php_rand_range2(min,max,RAND_SYS); + register long result; + PHP_RAND_RANGE(BG(rand_generator_current), min, max, result); + return result; } /* }}} */ /* {{{ [mt_]rand common */ -#define PHP_FUNCTION_RAND(name,type,lowertype) \ +#define PHP_FUNCTION_RAND(name,which) \ PHP_FUNCTION(name) \ { \ zval **min, **max; \ \ switch (ZEND_NUM_ARGS()) { \ case 0: \ - RETURN_LONG(php_rand_##lowertype()); \ + RETURN_LONG(PHP_RAND(which)); \ case 2: \ if (zend_get_parameters_ex(2, &min, &max)==FAILURE) { \ RETURN_FALSE; \ } \ convert_to_long_ex(min); \ convert_to_long_ex(max); \ - RETURN_LONG(php_rand_range2(Z_LVAL_PP(min), \ - Z_LVAL_PP(max), type)); \ + Z_TYPE_P(return_value) = IS_LONG; \ + PHP_RAND_RANGE(which, Z_LVAL_PP(min), \ + Z_LVAL_PP(max), Z_LVAL_P(return_value)); \ + return; \ default: \ WRONG_PARAM_COUNT; \ break; \ @@ -247,12 +225,12 @@ /* {{{ proto int rand([int min, int max]) Returns a random number */ -PHP_FUNCTION_RAND(rand,RAND_SYS,sys) +PHP_FUNCTION_RAND(rand,PHP_RAND_SYS) /* }}} */ /* {{{ proto int mt_rand([int min, int max]) Returns a random number by means of Mersenne Twister */ -PHP_FUNCTION_RAND(mt_rand,RAND_MT,mt) +PHP_FUNCTION_RAND(mt_rand,PHP_RAND_MT) /* }}} */ /* getrandmax */ @@ -261,8 +239,7 @@ Returns the maximum value a random number can have */ PHPAPI long php_randmax(void) { - /* Get current algorithm */ - return php_randmax_sys(); + return PHP_RANDMAX(BG(rand_generator_current)); } /* }}} */ @@ -274,7 +251,7 @@ WRONG_PARAM_COUNT; } - RETURN_LONG( php_randmax_sys() ); + RETURN_LONG( PHP_RANDMAX(PHP_RAND_SYS)); } /* }}} */ @@ -286,7 +263,7 @@ WRONG_PARAM_COUNT; } - RETURN_LONG( php_randmax_mt() ); + RETURN_LONG( PHP_RANDMAX(PHP_RAND_MT)); } /* }}} */ Index: php4/ext/standard/rand_mt.c diff -u php4/ext/standard/rand_mt.c:1.1.2.1 php4/ext/standard/rand_mt.c:1.1.2.2 --- php4/ext/standard/rand_mt.c:1.1.2.1 Wed Aug 22 17:53:21 2001 +++ php4/ext/standard/rand_mt.c Sun Aug 26 18:47:55 2001 @@ -19,7 +19,7 @@ | Based on code from: Shawn Cokus <Cokus@math.washington.edu> | +----------------------------------------------------------------------+ */ -/* $Id: rand_mt.c,v 1.1.2.1 2001/08/22 21:53:21 jeroen Exp $ */ +/* $Id: rand_mt.c,v 1.1.2.2 2001/08/26 22:47:55 jeroen Exp $ */ #include <stdlib.h> @@ -82,6 +82,21 @@ Melo: we should put some ifdefs here to catch those alphas... */ +static void _php_srand_mt(long seed TSRMLS_DC); +static inline long _php_rand_mt_reload(TSRMLS_D); +static long _php_rand_mt(void); +/* + * Melo: it could be 2^^32 but we only use 2^^31 to maintain + * compatibility with the previous php_rand + */ +#define _PHP_RANDMAX_MT ((long)(0x7FFFFFFF)) /* 2^^31 - 1 */ + +php_randgen_entries[PHP_RAND_MT] = { + _php_srand_mt, /* void srand(long seed) */ + _php_rand_mt, /* long rand(void) */ + PHP_RANDMAX_MT, /* long randmax */ + "mt" /* char *ini_str */ +} #define N MT_N /* length of state vector */ #define M (397) /* a period parameter */ @@ -109,9 +124,9 @@ return (long)((var) >> 1); \ } -/* {{{ void php_srand_mt(long seed) +/* {{{ void _php_srand_mt(long seed) */ -void php_srand_mt(long seed TSRMLS_DC) +static void _php_srand_mt(long seed TSRMLS_DC) { /* We initialize state[0..(N-1)] via the generator @@ -167,15 +182,15 @@ } /* }}} */ -/* {{{ long php_rand_mt_reload(void) [internal function] +/* {{{ long _php_rand_mt_reload(void) [internal function] * Generates a new batch of random numbers, and returns the first of it */ -static inline long php_rand_mt_reload(TSRMLS_D) +static inline long _php_rand_mt_reload(TSRMLS_D) { register php_uint32 *p0=BG(state), *p2=BG(state)+2, *pM=BG(state)+M, s0, s1; register int j; if(BG(left) < -1) - php_srand_mt(4357U TSRMLS_CC); + _php_srand_mt(4357U TSRMLS_CC); BG(left)=N-1, BG(next)=BG(state)+1; @@ -190,24 +205,18 @@ } /*}}}*/ -/* {{{ long php_rand_mt(void) */ -long php_rand_mt(void) +/* {{{ long _php_rand_mt(void) */ +static long _php_rand_mt(void) { php_uint32 y; TSRMLS_FETCH(); if(--BG(left) < 0) - return(php_rand_mt_reload(TSRMLS_C)); + return(_php_rand_mt_reload(TSRMLS_C)); y = *BG(next)++; MIX_AND_RETURN(y); } -/* }}} */ - -/* {{{ long php_randmax_mt(void) */ - -/* in php_rand.h */ - /* }}} */ /* Index: php4/ext/standard/rand_sys.c +++ php4/ext/standard/rand_sys.c
Index: php4/ext/standard/Makefile.in diff -u php4/ext/standard/Makefile.in:1.20.4.1 php4/ext/standard/Makefile.in:1.20.4.2 --- php4/ext/standard/Makefile.in:1.20.4.1 Wed Aug 22 17:53:21 2001 +++ php4/ext/standard/Makefile.in Sun Aug 26 18:47:55 2001 @@ -5,7 +5,8 @@ dir.c dl.c dns.c exec.c file.c filestat.c flock_compat.c \ formatted_print.c fsock.c head.c html.c image.c info.c iptc.c lcg.c \ link.c mail.c math.c md5.c metaphone.c microtime.c pack.c pageinfo.c \ - parsedate.c quot_print.c rand.c rand_mt.c reg.c soundex.c string.c scanf.c \ + parsedate.c quot_print.c rand.c rand_sys.c rand_mt.c reg.c \ + soundex.c string.c scanf.c \ syslog.c type.c uniqid.c url.c url_scanner.c var.c assert.c \ strnatcmp.c levenshtein.c incomplete_class.c url_scanner_ex.c \ ftp_fopen_wrapper.c http_fopen_wrapper.c php_fopen_wrapper.c credits.c Index: php4/ext/standard/basic_functions.h diff -u php4/ext/standard/basic_functions.h:1.88.4.1 php4/ext/standard/basic_functions.h:1.88.4.2 --- php4/ext/standard/basic_functions.h:1.88.4.1 Fri Aug 24 19:47:27 2001 +++ php4/ext/standard/basic_functions.h Sun Aug 26 18:47:55 2001 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: basic_functions.h,v 1.88.4.1 2001/08/24 23:47:27 jeroen Exp $ */ +/* $Id: basic_functions.h,v 1.88.4.2 2001/08/26 22:47:55 jeroen Exp $ */ #ifndef BASIC_FUNCTIONS_H #define BASIC_FUNCTIONS_H @@ -173,6 +173,9 @@ /* rand.c */ int rand_generator; /* current ini-setting */ int rand_generator_current; /* current (by overriding by [mt_]srand) */ + + /* rand_sys.c */ + unsigned int rand_sys_seed; /* Current seed for system-rand() (necessary for thread-safety) */ /* rand_mt.c */ php_uint32 state[MT_N+1]; /* state vector + 1 extra to not violate ANSI C */ Index: php4/ext/standard/php_rand.h diff -u php4/ext/standard/php_rand.h:1.8.8.2 php4/ext/standard/php_rand.h:1.8.8.3 --- php4/ext/standard/php_rand.h:1.8.8.2 Fri Aug 24 19:47:27 2001 +++ php4/ext/standard/php_rand.h Sun Aug 26 18:47:55 2001 @@ -15,11 +15,12 @@ | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> | | Zeev Suraski <zeev@zend.com> | | Pedro Melo <melo@ip.pt> | + | Jeroen van Wolffelaar <jeroen@php.net> | | | | Based on code from: Shawn Cokus <Cokus@math.washington.edu> | +----------------------------------------------------------------------+ */ -/* $Id: php_rand.h,v 1.8.8.2 2001/08/24 23:47:27 jeroen Exp $ */ +/* $Id: php_rand.h,v 1.8.8.3 2001/08/26 22:47:55 jeroen Exp $ */ /* Layout implementation random functions * @@ -56,27 +57,51 @@ * --Jeroen */ +/* TODO: + * - make constants available to PHP-user + * - MINFO section about which random number generators are available + * - Nuke randmax by enhancing PHP_RAND_RANGE to work well in the case of a + * greater request than the real (internal) randmax is + * - Implement LCG + * - Implement a real-random source? (via internet, and/or /dev/urandom?) + * - Can lrand48 be thread-safe? + * - Is random() useful sometimes? + * - Which system algorithms are available, maybe name them after real + * algorithm by compile-time detection? + * - Get this to compile :-) + */ #ifndef PHP_RAND_H #define PHP_RAND_H #include <stdlib.h> -#ifndef RAND_MAX -#define RAND_MAX (1<<15) -#endif - -#if HAVE_LRAND48 -#define php_randmax_sys() 2147483647 -#else -#define php_randmax_sys() RAND_MAX -#endif - -/* - * Melo: it could be 2^^32 but we only use 2^^31 to maintain - * compatibility with the previous php_rand - */ -#define php_randmax_mt() ((long)(0x7FFFFFFF)) /* 2^^31 - 1 */ +/* FIXME: that '_php_randgen_entry' needed, or not? */ +typedef struct _php_randgen_entry { + void (*srand)(long seed); + long (*rand)(void); + long randmax; + char *ini_str; +} php_randgen_entry; + +php_randgen_entry *php_randgen_entries; + +#define PHP_SRAND(which,seed) (php_randgen_entry[which]->srand(seed)) +#define PHP_RAND(which) (php_randgen_entry[which]->rand()) +#define PHP_RANDMAX(which) (php_randgen_entry[which].randmax) +#define PHP_RAND_INISTR(which) (php_randgen_entry[which].ini_str) + +/* Define random generator constants */ +#define PHP_RAND_SYS 0 +#define PHP_RAND_LRAND48 1 +#define PHP_RAND_MT 2 +#define PHP_RAND_LCG 3 + +#define PHP_RAND_DEFAULT PHP_RAND_MT + +/* how many there are */ +#define PHP_RAND_NUMRANDS 4 +/* Proto's */ PHP_FUNCTION(srand); PHP_FUNCTION(rand); PHP_FUNCTION(getrandmax); @@ -87,45 +112,6 @@ PHPAPI long php_rand(void); PHPAPI long php_rand_range(long min, long max); PHPAPI long php_randmax(void); -long php_rand_mt(void); -void php_srand_mt(long seed TSRMLS_DC); - -/* Define rand Function wrapper */ -#ifdef HAVE_RANDOM -#define php_rand_sys() random() -#else -#ifdef HAVE_LRAND48 -#define php_rand_sys() lrand48() -#else -#define php_rand_sys() rand() -#endif -#endif - -/* Define srand Function wrapper */ -#ifdef HAVE_SRANDOM -#define php_srand_sys(seed) srandom((unsigned int)seed) -#else -#ifdef HAVE_SRAND48 -#define php_srand_sys(seed) srand48((long)seed) -#else -#define php_srand_sys(seed) srand((unsigned int)seed) -#endif -#endif - -/* Define random generator constants */ -#define RAND_SYS 1 -#define RAND_MT 2 -#define RAND_LCG 3 -#define RAND_SYS_STR "system" -#define RAND_MT_STR "mt" -#define RAND_LCG_STR "lcg" - -#define RAND_DEFAULT RAND_MT -#define RAND_DEFAULT_STR RAND_MT_STR - - -/* BC */ -#define PHP_RAND_MAX php_randmax() #endif /* PHP_RAND_H */ Index: php4/ext/standard/rand.c diff -u php4/ext/standard/rand.c:1.31.4.2 php4/ext/standard/rand.c:1.31.4.3 --- php4/ext/standard/rand.c:1.31.4.2 Fri Aug 24 19:47:27 2001 +++ php4/ext/standard/rand.c Sun Aug 26 18:47:55 2001 @@ -17,7 +17,7 @@ | Pedro Melo <melo@ip.pt> | +----------------------------------------------------------------------+ */ -/* $Id: rand.c,v 1.31.4.2 2001/08/24 23:47:27 jeroen Exp $ */ +/* $Id: rand.c,v 1.31.4.3 2001/08/26 22:47:55 jeroen Exp $ */ #include <stdlib.h> @@ -32,7 +32,19 @@ /* See php_rand.h for information about layout */ -#define SRAND_A_RANDOM_SEED (time(0) * getpid() * (php_combined_lcg(TSRMLS_C) * 10000.0)) /* something with microtime? */ +#if 0 +#define RANDGEN_ENTRY(intval, lower, upper, has_seed) \ + php_randgen_entry[intval] = { \ + (has_seed) ? php_rand_##lower : NULL, \ + php_rand_##lower, \ + PHP_RANDMAX_##upper, \ + "lower" \ + }; +#endif + +#define FOREACH_RANDGEN(var,i) for ( (var) = php_randgen_entry[(i)=0] ; (var) < PHP_RAND_NUMGENS ; (var) = php_randgen_entry[++(i)] ) + +php_randgen_entry php_randgen_entries[PHP_RAND_NUMGENS]; /* TODO: check that this function is called on the start of each script * execution: not more often, not less often. @@ -43,29 +55,30 @@ */ PHP_RINIT_FUNCTION(rand) { + register php_randgen_entry *randgen; + register int i; + /* seed all number-generators */ /* FIXME: or seed relevant numgen on init/update ini-entry? */ - php_srand_sys(SRAND_A_RANDOM_SEED); - php_srand_mt(SRAND_A_RANDOM_SEED); + FOREACH_RANDGEN(randgen,i) { + if (randgen.srand) { +#define SRAND_A_RANDOM_SEED (time(0) * getpid() * (php_combined_lcg(TSRMLS_C) * 10000.0)) /* something with microtime? */ + randgen->srand(SRAND_A_RANDOM_SEED); + } + } } /* INI */ static int randgen_str_to_int(char *str, int strlen) { - /* manually check all cases, or some loop to automate this - * kind of stuff, so that a new random number generator - * can be added more easily? - * - * --jeroen - */ - if (!strcasecmp(str,RAND_SYS_STR)) { - return RAND_SYS; - } else if (!strcasecmp(str,RAND_MT_STR)) { - return RAND_MT; - } else if (!strcasecmp(str,RAND_LCG_STR)) { - return RAND_LCG; + register php_randgen_entry *randgen; + register int i; + + FOREACH_RANDGEN(randgen,i) { + if (!strcasecmp(str, randgen.ini_str)) + return i; } - return 0; /* FIXME: include that f*** .h that has FALSE */ + return -1; } /* FIXME: check that this is called on initial ini-parsing too */ @@ -75,7 +88,7 @@ /* Set BG(rand_generator) to the correct integer value indicating * ini-setting */ BG(rand_generator) = randgen_str_to_int(new_value, new_value_length); - if (!BG(rand_generator)) { + if (BG(rand_generator) == -1) { /* FIXME: is this possible? What happens if this occurs during * ini-parsing at startup? */ php_error(E_WARNING,"Invalid value for random_number_generator: \"%s\"", new_value); @@ -89,7 +102,7 @@ } PHP_INI_BEGIN() - PHP_INI_ENTRY("random_number_generator", RAND_DEFAULT_STR, PHP_INI_ALL, OnUpdateRandGen) + PHP_INI_ENTRY("random_number_generator", PHP_RAND_INISTR(PHP_RAND_DEFAULT), PHP_INI_ALL, OnUpdateRandGen) PHP_INI_END() /* srand */ @@ -97,25 +110,8 @@ /* {{{ PHPAPI void php_srand(void) */ PHPAPI void php_srand(void) { - /* php_srand_2( 1e6d * microtime() , protocol-specified-in-php.ini ) */ - php_error(E_ERROR,"Not yet implemented"); -} -/* }}} */ - -/* {{{ void php_srand2(long seed, int alg) */ -static inline void php_srand2(long seed, int alg) -{ - switch (alg) { - case RAND_SYS: - php_srand_sys(seed); - return; - case RAND_MT: - php_srand_mt(seed TSRMLS_CC); - return; - default: - php_error(E_ERROR,"Bug, please report (php_srand2-%d)",alg); - return; - } + BG(rand_generator_current) = BG(rand_generator); + PHP_SRAND(BG(rand_generator), SRAND_A_RANDOM_SEED); } /* }}} */ @@ -131,18 +127,19 @@ zend_get_parameters_ex(1, &arg); \ convert_to_long_ex(arg); \ \ - php_srand2((*arg)->value.lval, type); \ + BG(rand_generator_current) = type; \ + PHP_SRAND(type, Z_LVAL_PP(arg)); \ } /* }}} */ /* {{{ proto void srand(int seed) Seeds random number generator */ -pim_srand_common(srand,RAND_SYS) +pim_srand_common(srand,PHP_RAND_SYS) /* }}} */ /* {{{ proto void mt_srand(int seed) Seeds random number generator */ -pim_srand_common(mt_srand,RAND_MT) +pim_srand_common(mt_srand,PHP_RAND_MT) /* }}} */ /* rand */ @@ -150,13 +147,12 @@ /* {{{ PHPAPI long php_rand(void) */ PHPAPI long php_rand(void) { - /* algorithm = BG(current_alg) */ - return php_rand_sys(); + return PHP_RAND(BG(rand_generator_current)); } /* }}} */ -/* {{{ macro: php_map_a_range */ -#define php_map_a_range(number,min,max,MAX) { \ +/* {{{ macro: PHP_RAND_RANGE */ +#define PHP_RAND_RANGE(which,min,max,result) { \ /* * A bit of tricky math here. We want to avoid using a modulus because * that simply tosses the high-order bits and might skew the distribution @@ -178,66 +174,48 @@ * chance, it's ignored. * * --Rasmus and Jeroen - */ \ - if ((max) < (min)) { \ + */ \ + (result) = PHP_RAND(which); \ + if ((max) < (min)) { \ php_error(E_WARNING, "%s(): Invalid range: %ld..%ld (minimum can't be larger than maximum)", \ get_active_function_name(TSRMLS_C), (min), (max)); \ - } else if ( (max) - (min) > (MAX) ) { \ + } else if ( (max) - (min) > PHP_RANDMAX(which) ) { \ /* TODO: this can done better, get two numbers and combine... */ \ php_error(E_WARNING, "%s(): Invalid range: %ld..%ld (can't give that much randomness)", \ get_active_function_name(TSRMLS_C), (min), (max)); \ } \ - (number) = (min) + (int) ((double)((max)-(min)+1) * (number)/(MAX+1.0)); \ -} -/* }}} */ - -/* {{{ long php_rand_range2(long min, long max, int alg) - * Temporary hack function */ -static inline long php_rand_range2(long min, long max, int alg) -{ - long number; - - switch (alg) { - case RAND_SYS: - number = php_rand_sys(); - php_map_a_range(number,min,max,php_randmax_sys()); - return number; - case RAND_MT: - number = php_rand_mt(); - php_map_a_range(number,min,max,php_randmax_mt()); - return number; - default: - php_error(E_ERROR,"Bug, please report (php_rand_range2-%d)",alg); - return; - } + (result) = (min) + (long) ((double)((max)-(min)+1) * (result)/(PHP_RANDMAX(which)+1.0)); \ } /* }}} */ /* {{{ PHPAPI long php_rand_range(long min, long max) */ PHPAPI long php_rand_range(long min, long max) { - /* will be php.ini & srand aware */ - return php_rand_range2(min,max,RAND_SYS); + register long result; + PHP_RAND_RANGE(BG(rand_generator_current), min, max, result); + return result; } /* }}} */ /* {{{ [mt_]rand common */ -#define PHP_FUNCTION_RAND(name,type,lowertype) \ +#define PHP_FUNCTION_RAND(name,which) \ PHP_FUNCTION(name) \ { \ zval **min, **max; \ \ switch (ZEND_NUM_ARGS()) { \ case 0: \ - RETURN_LONG(php_rand_##lowertype()); \ + RETURN_LONG(PHP_RAND(which)); \ case 2: \ if (zend_get_parameters_ex(2, &min, &max)==FAILURE) { \ RETURN_FALSE; \ } \ convert_to_long_ex(min); \ convert_to_long_ex(max); \ - RETURN_LONG(php_rand_range2(Z_LVAL_PP(min), \ - Z_LVAL_PP(max), type)); \ + Z_TYPE_P(return_value) = IS_LONG; \ + PHP_RAND_RANGE(which, Z_LVAL_PP(min), \ + Z_LVAL_PP(max), Z_LVAL_P(return_value)); \ + return; \ default: \ WRONG_PARAM_COUNT; \ break; \ @@ -247,12 +225,12 @@ /* {{{ proto int rand([int min, int max]) Returns a random number */ -PHP_FUNCTION_RAND(rand,RAND_SYS,sys) +PHP_FUNCTION_RAND(rand,PHP_RAND_SYS) /* }}} */ /* {{{ proto int mt_rand([int min, int max]) Returns a random number by means of Mersenne Twister */ -PHP_FUNCTION_RAND(mt_rand,RAND_MT,mt) +PHP_FUNCTION_RAND(mt_rand,PHP_RAND_MT) /* }}} */ /* getrandmax */ @@ -261,8 +239,7 @@ Returns the maximum value a random number can have */ PHPAPI long php_randmax(void) { - /* Get current algorithm */ - return php_randmax_sys(); + return PHP_RANDMAX(BG(rand_generator_current)); } /* }}} */ @@ -274,7 +251,7 @@ WRONG_PARAM_COUNT; } - RETURN_LONG( php_randmax_sys() ); + RETURN_LONG( PHP_RANDMAX(PHP_RAND_SYS)); } /* }}} */ @@ -286,7 +263,7 @@ WRONG_PARAM_COUNT; } - RETURN_LONG( php_randmax_mt() ); + RETURN_LONG( PHP_RANDMAX(PHP_RAND_MT)); } /* }}} */ Index: php4/ext/standard/rand_mt.c diff -u php4/ext/standard/rand_mt.c:1.1.2.1 php4/ext/standard/rand_mt.c:1.1.2.2 --- php4/ext/standard/rand_mt.c:1.1.2.1 Wed Aug 22 17:53:21 2001 +++ php4/ext/standard/rand_mt.c Sun Aug 26 18:47:55 2001 @@ -19,7 +19,7 @@ | Based on code from: Shawn Cokus <Cokus@math.washington.edu> | +----------------------------------------------------------------------+ */ -/* $Id: rand_mt.c,v 1.1.2.1 2001/08/22 21:53:21 jeroen Exp $ */ +/* $Id: rand_mt.c,v 1.1.2.2 2001/08/26 22:47:55 jeroen Exp $ */ #include <stdlib.h> @@ -82,6 +82,21 @@ Melo: we should put some ifdefs here to catch those alphas... */ +static void _php_srand_mt(long seed TSRMLS_DC); +static inline long _php_rand_mt_reload(TSRMLS_D); +static long _php_rand_mt(void); +/* + * Melo: it could be 2^^32 but we only use 2^^31 to maintain + * compatibility with the previous php_rand + */ +#define _PHP_RANDMAX_MT ((long)(0x7FFFFFFF)) /* 2^^31 - 1 */ + +php_randgen_entries[PHP_RAND_MT] = { + _php_srand_mt, /* void srand(long seed) */ + _php_rand_mt, /* long rand(void) */ + PHP_RANDMAX_MT, /* long randmax */ + "mt" /* char *ini_str */ +} #define N MT_N /* length of state vector */ #define M (397) /* a period parameter */ @@ -109,9 +124,9 @@ return (long)((var) >> 1); \ } -/* {{{ void php_srand_mt(long seed) +/* {{{ void _php_srand_mt(long seed) */ -void php_srand_mt(long seed TSRMLS_DC) +static void _php_srand_mt(long seed TSRMLS_DC) { /* We initialize state[0..(N-1)] via the generator @@ -167,15 +182,15 @@ } /* }}} */ -/* {{{ long php_rand_mt_reload(void) [internal function] +/* {{{ long _php_rand_mt_reload(void) [internal function] * Generates a new batch of random numbers, and returns the first of it */ -static inline long php_rand_mt_reload(TSRMLS_D) +static inline long _php_rand_mt_reload(TSRMLS_D) { register php_uint32 *p0=BG(state), *p2=BG(state)+2, *pM=BG(state)+M, s0, s1; register int j; if(BG(left) < -1) - php_srand_mt(4357U TSRMLS_CC); + _php_srand_mt(4357U TSRMLS_CC); BG(left)=N-1, BG(next)=BG(state)+1; @@ -190,24 +205,18 @@ } /*}}}*/ -/* {{{ long php_rand_mt(void) */ -long php_rand_mt(void) +/* {{{ long _php_rand_mt(void) */ +static long _php_rand_mt(void) { php_uint32 y; TSRMLS_FETCH(); if(--BG(left) < 0) - return(php_rand_mt_reload(TSRMLS_C)); + return(_php_rand_mt_reload(TSRMLS_C)); y = *BG(next)++; MIX_AND_RETURN(y); } -/* }}} */ - -/* {{{ long php_randmax_mt(void) */ - -/* in php_rand.h */ - /* }}} */ /* Index: php4/ext/standard/rand_sys.c +++ php4/ext/standard/rand_sys.c