summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeroen van Wolffelaar <jeroen@php.net>2001-08-26 22:47:55 +0000
committerJeroen van Wolffelaar <jeroen@php.net>2001-08-26 22:47:55 +0000
commita8aed3118d21fdd9d8b556a026f14a114d275841 (patch)
treeb1386007ee6a098997625abcf7f38aa005b3abee
parente6d996d41c30a7d4c93393b61ef1221ac47385b8 (diff)
downloadphp-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.in3
-rw-r--r--ext/standard/basic_functions.h3
-rw-r--r--ext/standard/php_rand.h90
-rw-r--r--ext/standard/rand.c141
-rw-r--r--ext/standard/rand_mt.c37
-rw-r--r--ext/standard/rand_sys.c76
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
+ */