From 4bde620c4bf71b51f59c69ebcc19d37969e8e4eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rouven=20We=C3=9Fling?= Date: Mon, 24 Feb 2014 01:16:40 +0100 Subject: [PATCH] SplSequenceGenerator --- a.php | 22 +++ ext/spl/config.m4 | 4 +- ext/spl/php_spl.c | 1 + ext/spl/spl_sequence.c | 307 +++++++++++++++++++++++++++++++++ ext/spl/spl_sequence.h | 39 +++++ ext/standard/basic_functions.c | 1 - ext/standard/basic_functions.h | 3 +- ext/standard/mersenne.h | 156 +++++++++++++++++ ext/standard/rand.c | 123 +------------ 9 files changed, 531 insertions(+), 125 deletions(-) create mode 100644 a.php create mode 100644 ext/spl/spl_sequence.c create mode 100644 ext/spl/spl_sequence.h create mode 100644 ext/standard/mersenne.h diff --git a/a.php b/a.php new file mode 100644 index 0000000000000..e3fc775cf6f10 --- /dev/null +++ b/a.php @@ -0,0 +1,22 @@ +getNextNumber(0, 1000)); + var_dump(mt_rand(0, 1000)); +} +$b = clone $obj; +$c = unserialize(serialize($b)); +echo "End\n"; + +var_dump($obj->getNextNumber(0, 1000)); +var_dump($b->getNextNumber(0, 1000)); +var_dump($c->getNextNumber(0, 1000)); +var_dump(mt_rand(0, 1000)); \ No newline at end of file diff --git a/ext/spl/config.m4 b/ext/spl/config.m4 index a0f0d1a06d012..394fca808f3ec 100755 --- a/ext/spl/config.m4 +++ b/ext/spl/config.m4 @@ -22,6 +22,6 @@ int main(int argc, char **argv) { CPPFLAGS=$old_CPPFLAGS AC_DEFINE_UNQUOTED(HAVE_PACKED_OBJECT_VALUE, $ac_result, [Whether struct _zend_object_value is packed]) AC_DEFINE(HAVE_SPL, 1, [Whether you want SPL (Standard PHP Library) support]) - PHP_NEW_EXTENSION(spl, php_spl.c spl_functions.c spl_engine.c spl_iterators.c spl_array.c spl_directory.c spl_exceptions.c spl_observer.c spl_dllist.c spl_heap.c spl_fixedarray.c, no) - PHP_INSTALL_HEADERS([ext/spl], [php_spl.h spl_array.h spl_directory.h spl_engine.h spl_exceptions.h spl_functions.h spl_iterators.h spl_observer.h spl_dllist.h spl_heap.h spl_fixedarray.h]) + PHP_NEW_EXTENSION(spl, php_spl.c spl_functions.c spl_engine.c spl_iterators.c spl_array.c spl_directory.c spl_exceptions.c spl_observer.c spl_dllist.c spl_heap.c spl_fixedarray.c spl_sequence.c, no) + PHP_INSTALL_HEADERS([ext/spl], [php_spl.h spl_array.h spl_directory.h spl_engine.h spl_exceptions.h spl_functions.h spl_iterators.h spl_observer.h spl_dllist.h spl_heap.h spl_fixedarray.h spl_sequence.h]) PHP_ADD_EXTENSION_DEP(spl, pcre, true) diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 5927e05562885..0acb12d60f0df 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -948,6 +948,7 @@ PHP_MINIT_FUNCTION(spl) PHP_MINIT(spl_heap)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(spl_fixedarray)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(spl_observer)(INIT_FUNC_ARGS_PASSTHRU); + PHP_MINIT(spl_sequence)(INIT_FUNC_ARGS_PASSTHRU); return SUCCESS; } diff --git a/ext/spl/spl_sequence.c b/ext/spl/spl_sequence.c new file mode 100644 index 0000000000000..a46049affe925 --- /dev/null +++ b/ext/spl/spl_sequence.c @@ -0,0 +1,307 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.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: Rouven Weßling | + +----------------------------------------------------------------------+ + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "php.h" +#include "zend_interfaces.h" +#include "zend_exceptions.h" +#include "ext/standard/php_rand.h" +#include "ext/standard/php_var.h" +#include "ext/standard/php_lcg.h" +#include "ext/standard/php_smart_str.h" +#include "ext/standard/mersenne.h" + +#include "php_spl.h" +#include "spl_iterators.h" +#include "spl_exceptions.h" +#include "spl_functions.h" + +#define STATE_SIZE MT_N * (4 / sizeof(char)) + +PHPAPI zend_class_entry *spl_ce_SplSequenceGenerator; + +ZEND_BEGIN_ARG_INFO(arginfo_SplSequenceGenerator___construct, 0) + ZEND_ARG_INFO(0, seed) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO_EX(arginfo_SplSequenceGenerator_getNextNumber, 0, 0, 0) + ZEND_ARG_INFO(0, min) + ZEND_ARG_INFO(0, max) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO(arginfo_SplSequenceGenerator_serialize, 0) + ZEND_ARG_INFO(0, seed) +ZEND_END_ARG_INFO(); + +ZEND_BEGIN_ARG_INFO(arginfo_SplSequenceGenerator_unserialize, 0) + ZEND_ARG_INFO(0, serialized) +ZEND_END_ARG_INFO(); + +typedef struct _spl_sequencegenerator_object { /* {{{ */ + zend_object std; + php_uint32 state[MT_N+1]; /* state vector + 1 extra to not violate ANSI C */ + int left; /* can access this many values before reloading */ +} spl_sequencegenerator_object; +/* }}} */ + +PHPAPI zend_object_handlers spl_handler_SplSequenceGenerator; + +SPL_METHOD(SplSequenceGenerator, __construct) +{ + php_uint32 seed = 0; + zval *object; + spl_sequencegenerator_object *intern; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &seed) == FAILURE) { + return; + } + + object = getThis(); + intern = zend_object_store_get_object(object TSRMLS_CC); + + if (ZEND_NUM_ARGS() == 0) { + seed = GENERATE_SEED(); + } + + php_mt_initialize(seed, intern->state); + php_mt_reload(&intern->state, &intern->left TSRMLS_CC); +} + +SPL_METHOD(SplSequenceGenerator, serialize) +{ + spl_sequencegenerator_object *intern; + smart_str buf = {0}; + php_serialize_data_t var_hash; + zval zv, *zv_ptr = &zv; + + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + intern = zend_object_store_get_object(getThis() TSRMLS_CC); + if (!intern->state && !intern->left) { + return; + } + + PHP_VAR_SERIALIZE_INIT(var_hash); + + INIT_PZVAL(zv_ptr); + + ZVAL_LONG(zv_ptr, intern->left); + php_var_serialize(&buf, &zv_ptr, &var_hash TSRMLS_CC); + + ZVAL_STRINGL(zv_ptr, (char *) intern->state, STATE_SIZE, 0); + php_var_serialize(&buf, &zv_ptr, &var_hash TSRMLS_CC); + + Z_ARRVAL_P(zv_ptr) = zend_std_get_properties(getThis() TSRMLS_CC); + Z_TYPE_P(zv_ptr) = IS_ARRAY; + php_var_serialize(&buf, &zv_ptr, &var_hash TSRMLS_CC); + + PHP_VAR_SERIALIZE_DESTROY(var_hash); + + if (buf.c) { + RETURN_STRINGL(buf.c, buf.len, 0); + } +} + +SPL_METHOD(SplSequenceGenerator, unserialize) +{ + spl_sequencegenerator_object *intern; + char *buf; + int buf_len; + php_unserialize_data_t var_hash; + const unsigned char *p, *max; + zval zv, *zv_ptr = &zv; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) { + return; + } + + if (buf_len == 0) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Empty serialized string cannot be empty"); + return; + } + + intern = zend_object_store_get_object(getThis() TSRMLS_CC); + + if (intern->state && intern->left) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Cannot call unserialize() on an already constructed object"); + return; + } + + PHP_VAR_UNSERIALIZE_INIT(var_hash); + + p = (unsigned char *) buf; + max = (unsigned char *) buf + buf_len; + + INIT_ZVAL(zv); + if (!php_var_unserialize(&zv_ptr, &p, max, &var_hash TSRMLS_CC) + || Z_TYPE_P(zv_ptr) != IS_LONG) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len); + goto exit; + } + + intern->left = Z_LVAL_P(zv_ptr); + + INIT_ZVAL(zv); + if (!php_var_unserialize(&zv_ptr, &p, max, &var_hash TSRMLS_CC) + || Z_TYPE_P(zv_ptr) != IS_STRING || Z_STRLEN_P(zv_ptr) == 0) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len); + goto exit; + } + if (STATE_SIZE != Z_STRLEN_P(zv_ptr)) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Expected state size %d, got %d", STATE_SIZE, Z_STRLEN_P(zv_ptr)); + } + + memcpy(intern->state, Z_STRVAL_P(zv_ptr), Z_STRLEN_P(zv_ptr)); + + INIT_ZVAL(zv); + if (!php_var_unserialize(&zv_ptr, &p, max, &var_hash TSRMLS_CC) + || Z_TYPE_P(zv_ptr) != IS_ARRAY) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len); + goto exit; + } + + if (zend_hash_num_elements(Z_ARRVAL_P(zv_ptr)) != 0) { + zend_hash_copy( + zend_std_get_properties(getThis() TSRMLS_CC), Z_ARRVAL_P(zv_ptr), + (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *) + ); + } + +exit: + zval_dtor(zv_ptr); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + return; +} + +SPL_METHOD(SplSequenceGenerator, getNextNumber) +{ + zval *object; + spl_sequencegenerator_object *intern; + + object = getThis(); + intern = zend_object_store_get_object(object TSRMLS_CC); + + long min; + long max; + long number; + int argc = ZEND_NUM_ARGS(); + + if (argc != 0) { + if (zend_parse_parameters(argc TSRMLS_CC, "ll", &min, &max) == FAILURE) { + return; + } else if (max < min) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "max(%ld) is smaller than min(%ld)", max, min); + RETURN_FALSE; + } + } + + number = (long) (php_mt_get_number(&intern->state, &intern->left TSRMLS_CC) >> 1); + if (argc == 2) { + RAND_RANGE(number, min, max, PHP_MT_RAND_MAX); + } + + RETURN_LONG(number); +} + +static const zend_function_entry spl_funcs_SplSequenceGenerator[] = { + SPL_ME(SplSequenceGenerator, __construct, arginfo_SplSequenceGenerator___construct, ZEND_ACC_PUBLIC) + SPL_ME(SplSequenceGenerator, serialize, arginfo_SplSequenceGenerator_serialize, ZEND_ACC_PUBLIC) + SPL_ME(SplSequenceGenerator, unserialize, arginfo_SplSequenceGenerator_unserialize, ZEND_ACC_PUBLIC) + SPL_ME(SplSequenceGenerator, getNextNumber, arginfo_SplSequenceGenerator_getNextNumber, ZEND_ACC_PUBLIC) + PHP_FE_END +}; + +static void spl_sequencegenerator_object_free_storage(spl_sequencegenerator_object *intern TSRMLS_DC) +{ + zend_object_std_dtor(&intern->std TSRMLS_CC); + efree(intern); +} +/* }}} */ + +static zend_object_value spl_sequencegenerator_new(zend_class_entry *class_type TSRMLS_DC) /* {{{ */ +{ + zend_object_value retval; + + spl_sequencegenerator_object *intern = ecalloc(1, sizeof(spl_sequencegenerator_object)); + + zend_object_std_init(&intern->std, class_type TSRMLS_CC); + object_properties_init(&intern->std, class_type); + + retval.handle = zend_objects_store_put( + intern, + (zend_objects_store_dtor_t) zend_objects_destroy_object, + (zend_objects_free_object_storage_t) spl_sequencegenerator_object_free_storage, + NULL TSRMLS_CC + ); + + retval.handlers = &spl_handler_SplSequenceGenerator; + + return retval; +} +/* }}} */ + +static zend_object_value spl_sequencegenerator_object_clone(zval *object TSRMLS_DC) +{ + spl_sequencegenerator_object *old_object = zend_object_store_get_object(object TSRMLS_CC); + + zend_object_value new_object_val = spl_sequencegenerator_new(Z_OBJCE_P(object) TSRMLS_CC); + + spl_sequencegenerator_object *new_object = zend_object_store_get_object_by_handle( + new_object_val.handle TSRMLS_CC + ); + + zend_objects_clone_members( + &new_object->std, new_object_val, + &old_object->std, Z_OBJ_HANDLE_P(object) TSRMLS_CC + ); + + memcpy(new_object->state, old_object->state, sizeof(old_object->state)); + new_object->left = old_object->left; + + return new_object_val; +} + +/* {{{ PHP_MINIT_FUNCTION(spl_sequence) */ +PHP_MINIT_FUNCTION(spl_sequence) +{ + REGISTER_SPL_STD_CLASS_EX(SplSequenceGenerator, spl_sequencegenerator_new, spl_funcs_SplSequenceGenerator); + REGISTER_SPL_IMPLEMENTS(SplSequenceGenerator, Serializable); + memcpy(&spl_handler_SplSequenceGenerator, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + + spl_handler_SplSequenceGenerator.clone_obj = spl_sequencegenerator_object_clone; + + REGISTER_SPL_CLASS_CONST_LONG(SplSequenceGenerator, "MAX", PHP_MT_RAND_MAX); + + return SUCCESS; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: fdm=marker + * vim: noet sw=4 ts=4 + */ diff --git a/ext/spl/spl_sequence.h b/ext/spl/spl_sequence.h new file mode 100644 index 0000000000000..1c423d7e2cd4c --- /dev/null +++ b/ext/spl/spl_sequence.h @@ -0,0 +1,39 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.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: Rouven Weßling | + +----------------------------------------------------------------------+ + */ + +#ifndef SPL_SEQUENCE_H +#define SPL_SEQUENCE_H + +#include "php.h" +#include "php_spl.h" +#include "spl_sequence.h" + +extern PHPAPI zend_class_entry *spl_ce_SplSequenceGenerator; + +PHP_MINIT_FUNCTION(spl_sequence); + +#endif /* SPL_SEQUENCE_H */ + +/* + * Local Variables: + * c-basic-offset: 4 + * tab-width: 4 + * End: + * vim600: fdm=marker + * vim: noet sw=4 ts=4 + */ diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 14acccff96bd0..d7e261d69764f 100644 --- a/ext/standard/basic_functions.c +++ b/ext/standard/basic_functions.c @@ -3442,7 +3442,6 @@ static void basic_globals_ctor(php_basic_globals *basic_globals_p TSRMLS_DC) /* BG(rand_is_seeded) = 0; BG(mt_rand_is_seeded) = 0; BG(umask) = -1; - BG(next) = NULL; BG(left) = -1; BG(user_tick_functions) = NULL; BG(user_filter_map) = NULL; diff --git a/ext/standard/basic_functions.h b/ext/standard/basic_functions.h index 3af85b3d403bb..483975a5048a8 100644 --- a/ext/standard/basic_functions.h +++ b/ext/standard/basic_functions.h @@ -191,8 +191,7 @@ typedef struct _php_basic_globals { /* rand.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 */ - int left; /* can *next++ this many times before reloading */ + int left; /* can access this many numbers before reloading */ unsigned int rand_seed; /* Seed for rand(), in ts version */ diff --git a/ext/standard/mersenne.h b/ext/standard/mersenne.h new file mode 100644 index 0000000000000..0aff5510bcb0b --- /dev/null +++ b/ext/standard/mersenne.h @@ -0,0 +1,156 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2014 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.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 | + | Zeev Suraski | + | Pedro Melo | + | Sterling Hughes | + | | + | Based on code from: Richard J. Wagner | + | Makoto Matsumoto | + | Takuji Nishimura | + | Shawn Cokus | + +----------------------------------------------------------------------+ + */ + +#ifndef MERSENNE_H +#define MERSENNE_H + +#include "basic_functions.h" + +/* + The following code is based on a C++ class MTRand by + Richard J. Wagner. For more information see the web page at + http://www-personal.engin.umich.edu/~wagnerr/MersenneTwister.html + + Mersenne Twister random number generator -- a C++ class MTRand + Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus + Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com + + The Mersenne Twister is an algorithm for generating random numbers. It + was designed with consideration of the flaws in various other generators. + The period, 2^19937-1, and the order of equidistribution, 623 dimensions, + are far greater. The generator is also fast; it avoids multiplication and + division, and it benefits from caches and pipelines. For more information + see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html + + Reference + M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally + Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on + Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + Copyright (C) 2000 - 2003, Richard J. Wagner + 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. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. +*/ + +#define N MT_N /* length of state vector */ +#define M (397) /* a period parameter */ +#define hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */ +#define loBit(u) ((u) & 0x00000001U) /* mask all but lowest bit of u */ +#define loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */ +#define mixBits(u, v) (hiBit(u)|loBits(v)) /* move hi bit of u to hi bit of v */ + +#define twist(m,u,v) (m ^ (mixBits(u,v)>>1) ^ ((php_uint32)(-(php_int32)(loBit(u))) & 0x9908b0dfU)) + +/* {{{ php_mt_initialize + */ +static inline void php_mt_initialize(php_uint32 seed, php_uint32 *state) +{ + /* Initialize generator state with seed + See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. + In previous versions, most significant bits (MSBs) of the seed affect + only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. */ + + register php_uint32 *s = state; + register php_uint32 *r = state; + register int i = 1; + + *s++ = seed & 0xffffffffU; + for( ; i < N; ++i ) { + *s++ = ( 1812433253U * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffU; + r++; + } +} +/* }}} */ + +/* {{{ php_mt_reload + */ +static void php_mt_reload(php_uint32 state[], int *left TSRMLS_DC) +{ + /* Generate N new values in state + Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) */ + + register php_uint32 *p = state; + register int i; + + for (i = N - M; i--; ++p) + *p = twist(p[M], p[0], p[1]); + for (i = M; --i; ++p) + *p = twist(p[M-N], p[0], p[1]); + *p = twist(p[M-N], p[0], state[0]); + *left = N; +} +/* }}} */ + +/* {{{ php_mt_get_number + */ +static php_uint32 php_mt_get_number(php_uint32 state[], int *left TSRMLS_DC) +{ + /* Pull a 32-bit integer from the generator state + Every other access function simply transforms the numbers extracted here */ + + register php_uint32 s1; + + if (*left == 0) { + php_mt_reload(state, left TSRMLS_CC); + } + --*left; + + s1 = state[N - *left - 1]; + s1 ^= (s1 >> 11); + s1 ^= (s1 << 7) & 0x9d2c5680U; + s1 ^= (s1 << 15) & 0xefc60000U; + return ( s1 ^ (s1 >> 18) ); +} +/* }}} */ + +#endif /* MERSENNE_H */ diff --git a/ext/standard/rand.c b/ext/standard/rand.c index b0af30aa9ddd3..d1af515a562c0 100644 --- a/ext/standard/rand.c +++ b/ext/standard/rand.c @@ -33,6 +33,7 @@ #include "php_lcg.h" #include "basic_functions.h" +#include "mersenne.h" /* SYSTEM RAND FUNCTIONS */ @@ -87,117 +88,13 @@ PHPAPI long php_rand(TSRMLS_D) /* MT RAND FUNCTIONS */ -/* - The following php_mt_...() functions are based on a C++ class MTRand by - Richard J. Wagner. For more information see the web page at - http://www-personal.engin.umich.edu/~wagnerr/MersenneTwister.html - - Mersenne Twister random number generator -- a C++ class MTRand - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus - Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com - - The Mersenne Twister is an algorithm for generating random numbers. It - was designed with consideration of the flaws in various other generators. - The period, 2^19937-1, and the order of equidistribution, 623 dimensions, - are far greater. The generator is also fast; it avoids multiplication and - division, and it benefits from caches and pipelines. For more information - see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html - - Reference - M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally - Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on - Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. - - Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, - Copyright (C) 2000 - 2003, Richard J. Wagner - 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. - - 3. The names of its contributors may not be used to endorse or promote - products derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. -*/ - -#define N MT_N /* length of state vector */ -#define M (397) /* a period parameter */ -#define hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */ -#define loBit(u) ((u) & 0x00000001U) /* mask all but lowest bit of u */ -#define loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */ -#define mixBits(u, v) (hiBit(u)|loBits(v)) /* move hi bit of u to hi bit of v */ - -#define twist(m,u,v) (m ^ (mixBits(u,v)>>1) ^ ((php_uint32)(-(php_int32)(loBit(u))) & 0x9908b0dfU)) - -/* {{{ php_mt_initialize - */ -static inline void php_mt_initialize(php_uint32 seed, php_uint32 *state) -{ - /* Initialize generator state with seed - See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. - In previous versions, most significant bits (MSBs) of the seed affect - only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. */ - - register php_uint32 *s = state; - register php_uint32 *r = state; - register int i = 1; - - *s++ = seed & 0xffffffffU; - for( ; i < N; ++i ) { - *s++ = ( 1812433253U * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffU; - r++; - } -} -/* }}} */ - -/* {{{ php_mt_reload - */ -static inline void php_mt_reload(TSRMLS_D) -{ - /* Generate N new values in state - Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) */ - - register php_uint32 *state = BG(state); - register php_uint32 *p = state; - register int i; - - for (i = N - M; i--; ++p) - *p = twist(p[M], p[0], p[1]); - for (i = M; --i; ++p) - *p = twist(p[M-N], p[0], p[1]); - *p = twist(p[M-N], p[0], state[0]); - BG(left) = N; - BG(next) = state; -} -/* }}} */ - /* {{{ php_mt_srand */ PHPAPI void php_mt_srand(php_uint32 seed TSRMLS_DC) { /* Seed the generator with a simple uint32 */ php_mt_initialize(seed, BG(state)); - php_mt_reload(TSRMLS_C); + php_mt_reload(&BG(state), &BG(left) TSRMLS_CC); /* Seed only once */ BG(mt_rand_is_seeded) = 1; @@ -208,21 +105,7 @@ PHPAPI void php_mt_srand(php_uint32 seed TSRMLS_DC) */ PHPAPI php_uint32 php_mt_rand(TSRMLS_D) { - /* Pull a 32-bit integer from the generator state - Every other access function simply transforms the numbers extracted here */ - - register php_uint32 s1; - - if (BG(left) == 0) { - php_mt_reload(TSRMLS_C); - } - --BG(left); - - s1 = *BG(next)++; - s1 ^= (s1 >> 11); - s1 ^= (s1 << 7) & 0x9d2c5680U; - s1 ^= (s1 << 15) & 0xefc60000U; - return ( s1 ^ (s1 >> 18) ); + return php_mt_get_number(&BG(state), &BG(left) TSRMLS_CC); } /* }}} */