diff options
author | Jeroen van Wolffelaar <jeroen@php.net> | 2001-08-26 22:47:55 +0000 |
---|---|---|
committer | Jeroen van Wolffelaar <jeroen@php.net> | 2001-08-26 22:47:55 +0000 |
commit | a8aed3118d21fdd9d8b556a026f14a114d275841 (patch) | |
tree | b1386007ee6a098997625abcf7f38aa005b3abee | |
parent | e6d996d41c30a7d4c93393b61ef1221ac47385b8 (diff) | |
download | php-git-a8aed3118d21fdd9d8b556a026f14a114d275841.tar.gz |
- 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.
-rw-r--r-- | ext/standard/Makefile.in | 3 | ||||
-rw-r--r-- | ext/standard/basic_functions.h | 3 | ||||
-rw-r--r-- | ext/standard/php_rand.h | 90 | ||||
-rw-r--r-- | ext/standard/rand.c | 141 | ||||
-rw-r--r-- | ext/standard/rand_mt.c | 37 | ||||
-rw-r--r-- | ext/standard/rand_sys.c | 76 |
6 files changed, 201 insertions, 149 deletions
diff --git a/ext/standard/Makefile.in b/ext/standard/Makefile.in index 628adea8a8..fb6c845467 100644 --- a/ext/standard/Makefile.in +++ b/ext/standard/Makefile.in @@ -5,7 +5,8 @@ LTLIBRARY_SOURCES=\ 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 diff --git a/ext/standard/basic_functions.h b/ext/standard/basic_functions.h index 38e7a10c72..b8dd3f40cc 100644 --- a/ext/standard/basic_functions.h +++ b/ext/standard/basic_functions.h @@ -174,6 +174,9 @@ typedef struct { 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 */ php_uint32 *next; /* next random value is computed from here */ diff --git a/ext/standard/php_rand.h b/ext/standard/php_rand.h index 9088588f2e..05c72b8440 100644 --- a/ext/standard/php_rand.h +++ b/ext/standard/php_rand.h @@ -15,6 +15,7 @@ | 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> | +----------------------------------------------------------------------+ @@ -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 +/* 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; -#if HAVE_LRAND48 -#define php_randmax_sys() 2147483647 -#else -#define php_randmax_sys() RAND_MAX -#endif +php_randgen_entry *php_randgen_entries; -/* - * 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 */ +#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 @@ PHP_FUNCTION(mt_getrandmax); 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 */ diff --git a/ext/standard/rand.c b/ext/standard/rand.c index 72736cc4b7..4306ac77fb 100644 --- a/ext/standard/rand.c +++ b/ext/standard/rand.c @@ -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 @@ static PHP_INI_MH(OnUpdateRandGen) /* 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 @@ static PHP_INI_MH(OnUpdateRandGen) } 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 @@ PHP_INI_END() /* {{{ 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 @@ PHP_FUNCTION(name) \ 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 @@ pim_srand_common(mt_srand,RAND_MT) /* {{{ 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 @@ PHPAPI long php_rand(void) * 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 @@ PHP_FUNCTION(name) \ /* {{{ 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 @@ PHP_FUNCTION_RAND(mt_rand,RAND_MT,mt) 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 @@ PHP_FUNCTION(getrandmax) WRONG_PARAM_COUNT; } - RETURN_LONG( php_randmax_sys() ); + RETURN_LONG( PHP_RANDMAX(PHP_RAND_SYS)); } /* }}} */ @@ -286,7 +263,7 @@ PHP_FUNCTION(mt_getrandmax) WRONG_PARAM_COUNT; } - RETURN_LONG( php_randmax_mt() ); + RETURN_LONG( PHP_RANDMAX(PHP_RAND_MT)); } /* }}} */ diff --git a/ext/standard/rand_mt.c b/ext/standard/rand_mt.c index bb0114951f..a044c8fb0d 100644 --- a/ext/standard/rand_mt.c +++ b/ext/standard/rand_mt.c @@ -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 @@ void php_srand_mt(long seed TSRMLS_DC) } /* }}} */ -/* {{{ 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,26 +205,20 @@ static inline long php_rand_mt_reload(TSRMLS_D) } /*}}}*/ -/* {{{ 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 */ - -/* }}} */ - /* * Local variables: * tab-width: 4 diff --git a/ext/standard/rand_sys.c b/ext/standard/rand_sys.c new file mode 100644 index 0000000000..0d191375dd --- /dev/null +++ b/ext/standard/rand_sys.c @@ -0,0 +1,76 @@ +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2001 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Rasmus Lerdorf <rasmus@lerdorf.on.ca> | + | Zeev Suraski <zeev@zend.com> | + | Jeroen van Wolffelaar <jeroen@php.net> | + +----------------------------------------------------------------------+ + */ +/* $Id$ */ + +#include <stdlib.h> + +#include "php.h" +#include "php_rand.h" + +#include "basic_functions.h" + + +/* rand() & co (thread safe this time!): */ +static void _php_srand_sys(long seed) +{ + BG(rand_sys_seed) = (unsigned int) seed; +} + +static long _php_rand_sys(void) +{ + return (long) rand_r(&BG(rand_sys_seed)); +} + +php_randgen_entries[PHP_RAND_SYS] = { + _php_srand_sys, /* void srand(long seed) */ + _php_rand_sys, /* long rand(void) */ +#ifdef RAND_MAX + (long)RANDMAX, /* long randmax */ +#else + (long)(1<<15), /* long randmax */ +#endif + "system" /* char *ini_str */ +} + +/* random() is left away, no manual page on my system, no bigger range than + * rand() + * --jeroen + */ + +/* lrand48 (_not_ TS) */ +#if HAVE_LRAND48 +php_randgen_entries[PHP_RAND_LRAND48] = { + srand48, /* void srand(long seed) */ + lrand48, /* long rand(void) */ + 2147483647L, /* long randmax */ + "lrand48" /* char *ini_str */ +} +#else +php_randgen_entries[PHP_RAND_LRAND48] = NULL; +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: sw=4 ts=4 tw=78 fdm=marker + * vim<600: sw=4 ts=4 tw=78 + */ |