mirror of
https://github.com/php/php-src.git
synced 2024-11-27 20:03:40 +08:00
Merge RNG fixes RFC. PR #1986
* rng-fixes: Fix legacy mode RAND_RANGE and 32/64-bit consistency Fix crypt salt not being converted to b64 Make mode selection part of mt_srand() Use zend_bitset Improve array_rand distribution Fix some insecure usages of php_rand Alias rand to mt_rand Fix RAND_RANGE for mt_rand Fix mt_rand impl. Provide legacy impl. access. Split rand and mt_rand into separate files
This commit is contained in:
commit
ab834f472f
1
NEWS
1
NEWS
@ -23,6 +23,7 @@ PHP NEWS
|
||||
phpize on Windows). (Yuji Uchiyama)
|
||||
. Fixed bug #29368 (The destructor is called when an exception is thrown from
|
||||
the constructor). (Dmitry)
|
||||
. Implemented RFC: RNG Fixes. (Leigh)
|
||||
|
||||
- COM:
|
||||
. Fixed bug #72569 (DOTNET/COM array parameters broke in PHP7). (Anatol)
|
||||
|
10
UPGRADING
10
UPGRADING
@ -49,6 +49,16 @@ PHP 7.1 UPGRADE NOTES
|
||||
the notice level only.
|
||||
. Don't call destructors of incompletely constructed objects, even if they
|
||||
are kept referenced. See bug #29368 and Zend/tests/bug29368_1.phpt.
|
||||
. rand() and srand() are now aliases of mt_rand() and mt_srand().
|
||||
Consequently the output of the following functions has changed:
|
||||
. rand()
|
||||
. shuffle()
|
||||
. str_shuffle()
|
||||
. array_rand()
|
||||
. Fixes to random number generators mean that mt_rand() now produces a
|
||||
different sequence of outputs to previous versions. If you relied on
|
||||
mt_srand() to produce a deterministic sequence, it can be called using
|
||||
mt_srand($seed, MT_RAND_PHP) to produce old the sequences.
|
||||
|
||||
- JSON:
|
||||
. The serialize_precision is used instead of precision when encoding double
|
||||
|
@ -22,7 +22,7 @@
|
||||
#include "php_soap.h"
|
||||
#include "ext/standard/base64.h"
|
||||
#include "ext/standard/md5.h"
|
||||
#include "ext/standard/php_rand.h"
|
||||
#include "ext/standard/php_random.h"
|
||||
|
||||
static char *get_http_header_value(char *headers, char *type);
|
||||
static zend_string *get_http_body(php_stream *socketd, int close, char *headers);
|
||||
@ -639,11 +639,15 @@ try_again:
|
||||
if ((digest = zend_hash_str_find(Z_OBJPROP_P(this_ptr), "_digest", sizeof("_digest")-1)) != NULL) {
|
||||
if (Z_TYPE_P(digest) == IS_ARRAY) {
|
||||
char HA1[33], HA2[33], response[33], cnonce[33], nc[9];
|
||||
zend_long nonce;
|
||||
PHP_MD5_CTX md5ctx;
|
||||
unsigned char hash[16];
|
||||
|
||||
php_random_bytes_throw(&nonce, sizeof(nonce));
|
||||
nonce &= 0x7fffffff;
|
||||
|
||||
PHP_MD5Init(&md5ctx);
|
||||
snprintf(cnonce, sizeof(cnonce), ZEND_LONG_FMT, php_rand());
|
||||
snprintf(cnonce, sizeof(cnonce), ZEND_LONG_FMT, nonce);
|
||||
PHP_MD5Update(&md5ctx, (unsigned char*)cnonce, strlen(cnonce));
|
||||
PHP_MD5Final(hash, &md5ctx);
|
||||
make_digest(cnonce, hash);
|
||||
|
@ -39,8 +39,7 @@
|
||||
#include "spl_heap.h"
|
||||
#include "zend_exceptions.h"
|
||||
#include "zend_interfaces.h"
|
||||
#include "ext/standard/php_rand.h"
|
||||
#include "ext/standard/php_lcg.h"
|
||||
#include "ext/standard/php_mt_rand.h"
|
||||
#include "main/snprintf.h"
|
||||
|
||||
#ifdef COMPILE_DL_SPL
|
||||
@ -747,10 +746,6 @@ PHPAPI zend_string *php_spl_object_hash(zval *obj) /* {{{*/
|
||||
intptr_t hash_handle, hash_handlers;
|
||||
|
||||
if (!SPL_G(hash_mask_init)) {
|
||||
if (!BG(mt_rand_is_seeded)) {
|
||||
php_mt_srand((uint32_t)GENERATE_SEED());
|
||||
}
|
||||
|
||||
SPL_G(hash_mask_handle) = (intptr_t)(php_mt_rand() >> 1);
|
||||
SPL_G(hash_mask_handlers) = (intptr_t)(php_mt_rand() >> 1);
|
||||
SPL_G(hash_mask_init) = 1;
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "php_string.h"
|
||||
#include "php_rand.h"
|
||||
#include "zend_smart_str.h"
|
||||
#include "zend_bitset.h"
|
||||
#include "ext/spl/spl_array.h"
|
||||
|
||||
/* {{{ defines */
|
||||
@ -5033,9 +5034,13 @@ PHP_FUNCTION(array_rand)
|
||||
{
|
||||
zval *input;
|
||||
zend_long randval, num_req = 1;
|
||||
int num_avail;
|
||||
zend_string *string_key;
|
||||
zend_ulong num_key;
|
||||
int i;
|
||||
int num_avail;
|
||||
zend_bitset bitset;
|
||||
int negative_bitset = 0;
|
||||
uint32_t bitset_len;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &input, &num_req) == FAILURE) {
|
||||
return;
|
||||
@ -5043,46 +5048,65 @@ PHP_FUNCTION(array_rand)
|
||||
|
||||
num_avail = zend_hash_num_elements(Z_ARRVAL_P(input));
|
||||
|
||||
if (ZEND_NUM_ARGS() > 1) {
|
||||
if (num_req <= 0 || num_req > num_avail) {
|
||||
php_error_docref(NULL, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");
|
||||
return;
|
||||
}
|
||||
if (num_avail == 0) {
|
||||
php_error_docref(NULL, E_WARNING, "Array is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make the return value an array only if we need to pass back more than one result. */
|
||||
if (num_req > 1) {
|
||||
array_init_size(return_value, (uint32_t)num_req);
|
||||
}
|
||||
if (num_req == 1) {
|
||||
randval = php_mt_rand_range(0, num_avail - 1);
|
||||
|
||||
/* We can't use zend_hash_index_find() because the array may have string keys or gaps. */
|
||||
ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
|
||||
if (!num_req) {
|
||||
break;
|
||||
}
|
||||
|
||||
randval = php_rand();
|
||||
|
||||
if ((double) (randval / (PHP_RAND_MAX + 1.0)) < (double) num_req / (double) num_avail) {
|
||||
/* If we are returning a single result, just do it. */
|
||||
if (Z_TYPE_P(return_value) != IS_ARRAY) {
|
||||
ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
|
||||
if (randval-- == 0) {
|
||||
if (string_key) {
|
||||
RETURN_STR_COPY(string_key);
|
||||
} else {
|
||||
RETURN_LONG(num_key);
|
||||
}
|
||||
} else {
|
||||
/* Append the result to the return value. */
|
||||
if (string_key) {
|
||||
add_next_index_str(return_value, zend_string_copy(string_key));
|
||||
} else {
|
||||
add_next_index_long(return_value, num_key);
|
||||
}
|
||||
}
|
||||
num_req--;
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
|
||||
if (num_req <= 0 || num_req > num_avail) {
|
||||
php_error_docref(NULL, E_WARNING, "Second argument has to be between 1 and the number of elements in the array");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Make the return value an array only if we need to pass back more than one result. */
|
||||
array_init_size(return_value, (uint32_t)num_req);
|
||||
if (num_req > (num_avail >> 1)) {
|
||||
negative_bitset = 1;
|
||||
num_req = num_avail - num_req;
|
||||
}
|
||||
|
||||
ALLOCA_FLAG(use_heap);
|
||||
bitset_len = zend_bitset_len(num_avail);
|
||||
bitset = ZEND_BITSET_ALLOCA(bitset_len, use_heap);
|
||||
zend_bitset_clear(bitset, bitset_len);
|
||||
|
||||
i = num_req;
|
||||
while (i) {
|
||||
randval = php_mt_rand_range(0, num_avail - 1);
|
||||
if (!zend_bitset_in(bitset, randval)) {
|
||||
zend_bitset_incl(bitset, randval);
|
||||
i--;
|
||||
}
|
||||
num_avail--;
|
||||
}
|
||||
|
||||
/* We can't use zend_hash_index_find() because the array may have string keys or gaps. */
|
||||
i = 0;
|
||||
ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
|
||||
if (zend_bitset_in(bitset, i) ^ negative_bitset) {
|
||||
if (string_key) {
|
||||
add_next_index_str(return_value, zend_string_copy(string_key));
|
||||
} else {
|
||||
add_next_index_long(return_value, num_key);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
free_alloca(bitset, use_heap);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "zend_operators.h"
|
||||
#include "ext/standard/php_dns.h"
|
||||
#include "ext/standard/php_uuencode.h"
|
||||
#include "ext/standard/php_mt_rand.h"
|
||||
|
||||
#ifdef PHP_WIN32
|
||||
#include "win32/php_win32_globals.h"
|
||||
@ -1883,18 +1884,10 @@ ZEND_BEGIN_ARG_INFO(arginfo_quoted_printable_encode, 0)
|
||||
ZEND_ARG_INFO(0, str)
|
||||
ZEND_END_ARG_INFO()
|
||||
/* }}} */
|
||||
/* {{{ rand.c */
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_srand, 0, 0, 0)
|
||||
ZEND_ARG_INFO(0, seed)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
/* {{{ mt_rand.c */
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_mt_srand, 0, 0, 0)
|
||||
ZEND_ARG_INFO(0, seed)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_rand, 0, 0, 0)
|
||||
ZEND_ARG_INFO(0, min)
|
||||
ZEND_ARG_INFO(0, max)
|
||||
ZEND_ARG_INFO(0, mode)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_mt_rand, 0, 0, 0)
|
||||
@ -1902,9 +1895,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_mt_rand, 0, 0, 0)
|
||||
ZEND_ARG_INFO(0, max)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO(arginfo_getrandmax, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_BEGIN_ARG_INFO(arginfo_mt_getrandmax, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
/* }}} */
|
||||
@ -2860,10 +2850,10 @@ const zend_function_entry basic_functions[] = { /* {{{ */
|
||||
PHP_FE(proc_nice, arginfo_proc_nice)
|
||||
#endif
|
||||
|
||||
PHP_FE(rand, arginfo_rand)
|
||||
PHP_FE(srand, arginfo_srand)
|
||||
PHP_FE(getrandmax, arginfo_getrandmax)
|
||||
PHP_FE(mt_rand, arginfo_mt_rand)
|
||||
PHP_FALIAS(rand, mt_rand, arginfo_mt_rand)
|
||||
PHP_FALIAS(srand, mt_srand, arginfo_mt_srand)
|
||||
PHP_FALIAS(getrandmax, mt_getrandmax, arginfo_mt_getrandmax)
|
||||
PHP_FE(mt_rand, arginfo_mt_rand)
|
||||
PHP_FE(mt_srand, arginfo_mt_srand)
|
||||
PHP_FE(mt_getrandmax, arginfo_mt_getrandmax)
|
||||
|
||||
@ -3478,8 +3468,8 @@ static void php_putenv_destructor(zval *zv) /* {{{ */
|
||||
|
||||
static void basic_globals_ctor(php_basic_globals *basic_globals_p) /* {{{ */
|
||||
{
|
||||
BG(rand_is_seeded) = 0;
|
||||
BG(mt_rand_is_seeded) = 0;
|
||||
BG(mt_rand_mode) = MT_RAND_MT19937;
|
||||
BG(umask) = -1;
|
||||
BG(next) = NULL;
|
||||
BG(left) = -1;
|
||||
@ -3661,6 +3651,7 @@ PHP_MINIT_FUNCTION(basic) /* {{{ */
|
||||
BASIC_MINIT_SUBMODULE(standard_filters)
|
||||
BASIC_MINIT_SUBMODULE(user_filters)
|
||||
BASIC_MINIT_SUBMODULE(password)
|
||||
BASIC_MINIT_SUBMODULE(mt_rand)
|
||||
|
||||
#if defined(HAVE_LOCALECONV) && defined(ZTS)
|
||||
BASIC_MINIT_SUBMODULE(localeconv)
|
||||
|
@ -191,15 +191,13 @@ typedef struct _php_basic_globals {
|
||||
char *CurrentStatFile, *CurrentLStatFile;
|
||||
php_stream_statbuf ssb, lssb;
|
||||
|
||||
/* rand.c */
|
||||
/* mt_rand.c */
|
||||
uint32_t state[MT_N+1]; /* state vector + 1 extra to not violate ANSI C */
|
||||
uint32_t *next; /* next random value is computed from here */
|
||||
int left; /* can *next++ this many times before reloading */
|
||||
|
||||
unsigned int rand_seed; /* Seed for rand(), in ts version */
|
||||
|
||||
zend_bool rand_is_seeded; /* Whether rand() has been seeded */
|
||||
zend_bool mt_rand_is_seeded; /* Whether mt_rand() has been seeded */
|
||||
zend_long mt_rand_mode;
|
||||
|
||||
/* syslog.c */
|
||||
char *syslog_device;
|
||||
|
@ -557,7 +557,7 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32.
|
||||
cyr_convert.c datetime.c 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 quot_print.c rand.c \
|
||||
microtime.c pack.c pageinfo.c quot_print.c rand.c mt_rand.c \
|
||||
soundex.c string.c scanf.c syslog.c type.c uniqid.c url.c \
|
||||
var.c versioning.c assert.c strnatcmp.c levenshtein.c \
|
||||
incomplete_class.c url_scanner_ex.c ftp_fopen_wrapper.c \
|
||||
|
@ -14,7 +14,7 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \
|
||||
cyr_convert.c datetime.c dir.c dl.c dns.c dns_win32.c exec.c \
|
||||
file.c filestat.c formatted_print.c fsock.c head.c html.c image.c \
|
||||
info.c iptc.c lcg.c link_win32.c mail.c math.c md5.c metaphone.c microtime.c \
|
||||
pack.c pageinfo.c quot_print.c rand.c soundex.c \
|
||||
pack.c pageinfo.c quot_print.c rand.c mt_rand.c soundex.c \
|
||||
string.c scanf.c syslog.c type.c uniqid.c url.c var.c \
|
||||
versioning.c assert.c strnatcmp.c levenshtein.c incomplete_class.c \
|
||||
url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \
|
||||
|
@ -54,15 +54,12 @@
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
#include "php_lcg.h"
|
||||
#include "php_crypt.h"
|
||||
#include "php_rand.h"
|
||||
#include "php_random.h"
|
||||
|
||||
/* sha512 crypt has the maximal salt length of 123 characters */
|
||||
#define PHP_MAX_SALT_LEN 123
|
||||
|
||||
#define PHP_CRYPT_RAND php_rand()
|
||||
|
||||
/* Used to check DES salts to ensure that they contain only valid characters */
|
||||
#define IS_VALID_SALT_CHARACTER(c) (((c) >= '.' && (c) <= '9') || ((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
|
||||
|
||||
@ -99,11 +96,10 @@ PHP_MSHUTDOWN_FUNCTION(crypt) /* {{{ */
|
||||
|
||||
static unsigned char itoa64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
static void php_to64(char *s, zend_long v, int n) /* {{{ */
|
||||
static void php_to64(char *s, int n) /* {{{ */
|
||||
{
|
||||
while (--n >= 0) {
|
||||
*s++ = itoa64[v&0x3f];
|
||||
v >>= 6;
|
||||
*s++ = itoa64[*s&0x3f];
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
@ -266,9 +262,9 @@ PHP_FUNCTION(crypt)
|
||||
|
||||
/* The automatic salt generation covers standard DES, md5-crypt and Blowfish (simple) */
|
||||
if (!*salt) {
|
||||
strncpy(salt, "$1$", PHP_MAX_SALT_LEN);
|
||||
php_to64(&salt[3], PHP_CRYPT_RAND, 4);
|
||||
php_to64(&salt[7], PHP_CRYPT_RAND, 4);
|
||||
strncpy(salt, "$1$", 3);
|
||||
php_random_bytes_throw(&salt[3], 8);
|
||||
php_to64(&salt[3], 8);
|
||||
strncpy(&salt[11], "$", PHP_MAX_SALT_LEN - 11);
|
||||
salt_in_len = strlen(salt);
|
||||
} else {
|
||||
|
315
ext/standard/mt_rand.c
Normal file
315
ext/standard/mt_rand.c
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 7 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2016 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 <rasmus@php.net> |
|
||||
| Zeev Suraski <zeev@zend.com> |
|
||||
| Pedro Melo <melo@ip.pt> |
|
||||
| Sterling Hughes <sterling@php.net> |
|
||||
| |
|
||||
| Based on code from: Richard J. Wagner <rjwagner@writeme.com> |
|
||||
| Makoto Matsumoto <matumoto@math.keio.ac.jp> |
|
||||
| Takuji Nishimura |
|
||||
| Shawn Cokus <Cokus@math.washington.edu> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
#include "php.h"
|
||||
#include "php_rand.h"
|
||||
#include "php_mt_rand.h"
|
||||
|
||||
/* 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) ^ ((uint32_t)(-(int32_t)(loBit(v))) & 0x9908b0dfU))
|
||||
#define twist_php(m,u,v) (m ^ (mixBits(u,v)>>1) ^ ((uint32_t)(-(int32_t)(loBit(u))) & 0x9908b0dfU))
|
||||
|
||||
/* {{{ php_mt_initialize
|
||||
*/
|
||||
static inline void php_mt_initialize(uint32_t seed, uint32_t *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 uint32_t *s = state;
|
||||
register uint32_t *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(void)
|
||||
{
|
||||
/* Generate N new values in state
|
||||
Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) */
|
||||
|
||||
register uint32_t *state = BG(state);
|
||||
register uint32_t *p = state;
|
||||
register int i;
|
||||
|
||||
if (BG(mt_rand_mode) == MT_RAND_MT19937) {
|
||||
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]);
|
||||
}
|
||||
else {
|
||||
for (i = N - M; i--; ++p)
|
||||
*p = twist_php(p[M], p[0], p[1]);
|
||||
for (i = M; --i; ++p)
|
||||
*p = twist_php(p[M-N], p[0], p[1]);
|
||||
*p = twist_php(p[M-N], p[0], state[0]);
|
||||
}
|
||||
BG(left) = N;
|
||||
BG(next) = state;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ php_mt_srand
|
||||
*/
|
||||
PHPAPI void php_mt_srand(uint32_t seed)
|
||||
{
|
||||
/* Seed the generator with a simple uint32 */
|
||||
php_mt_initialize(seed, BG(state));
|
||||
php_mt_reload();
|
||||
|
||||
/* Seed only once */
|
||||
BG(mt_rand_is_seeded) = 1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ php_mt_rand
|
||||
*/
|
||||
PHPAPI uint32_t php_mt_rand(void)
|
||||
{
|
||||
/* Pull a 32-bit integer from the generator state
|
||||
Every other access function simply transforms the numbers extracted here */
|
||||
|
||||
register uint32_t s1;
|
||||
|
||||
if (UNEXPECTED(!BG(mt_rand_is_seeded))) {
|
||||
php_mt_srand(GENERATE_SEED());
|
||||
}
|
||||
|
||||
if (BG(left) == 0) {
|
||||
php_mt_reload();
|
||||
}
|
||||
--BG(left);
|
||||
|
||||
s1 = *BG(next)++;
|
||||
s1 ^= (s1 >> 11);
|
||||
s1 ^= (s1 << 7) & 0x9d2c5680U;
|
||||
s1 ^= (s1 << 15) & 0xefc60000U;
|
||||
return ( s1 ^ (s1 >> 18) );
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto void mt_srand([int seed])
|
||||
Seeds Mersenne Twister random number generator */
|
||||
PHP_FUNCTION(mt_srand)
|
||||
{
|
||||
zend_long seed = 0;
|
||||
zend_long mode = MT_RAND_MT19937;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &seed, &mode) == FAILURE)
|
||||
return;
|
||||
|
||||
if (ZEND_NUM_ARGS() == 0)
|
||||
seed = GENERATE_SEED();
|
||||
|
||||
switch (mode) {
|
||||
case MT_RAND_PHP:
|
||||
BG(mt_rand_mode) = MT_RAND_PHP;
|
||||
break;
|
||||
default:
|
||||
BG(mt_rand_mode) = MT_RAND_MT19937;
|
||||
}
|
||||
|
||||
php_mt_srand(seed);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ php_mt_rand_range
|
||||
*/
|
||||
PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max)
|
||||
{
|
||||
zend_ulong umax = max - min;
|
||||
zend_ulong limit;
|
||||
zend_ulong result;
|
||||
|
||||
result = php_mt_rand();
|
||||
if (umax > UINT32_MAX) {
|
||||
result = (result << 32) | php_mt_rand();
|
||||
}
|
||||
|
||||
/* Special case where no modulus is required */
|
||||
if (UNEXPECTED(umax == ZEND_ULONG_MAX)) {
|
||||
return (zend_long)result;
|
||||
}
|
||||
|
||||
/* Increment the max so the range is inclusive of max */
|
||||
umax++;
|
||||
|
||||
/* Powers of two are not biased */
|
||||
if (EXPECTED((umax & (umax - 1)) != 0)) {
|
||||
/* Ceiling under which ZEND_LONG_MAX % max == 0 */
|
||||
limit = ZEND_ULONG_MAX - (ZEND_ULONG_MAX % umax) - 1;
|
||||
|
||||
/* Discard numbers over the limit to avoid modulo bias */
|
||||
while (UNEXPECTED(result > limit)) {
|
||||
result = (result << 32) | php_mt_rand();
|
||||
}
|
||||
}
|
||||
|
||||
return (zend_long)((result % umax) + min);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto int mt_rand([int min, int max])
|
||||
Returns a random number from Mersenne Twister */
|
||||
PHP_FUNCTION(mt_rand)
|
||||
{
|
||||
zend_long min;
|
||||
zend_long max;
|
||||
zend_long n;
|
||||
int argc = ZEND_NUM_ARGS();
|
||||
|
||||
if (argc == 0) {
|
||||
// genrand_int31 in mt19937ar.c performs a right shift
|
||||
RETURN_LONG(php_mt_rand() >> 1);
|
||||
}
|
||||
|
||||
if (zend_parse_parameters(argc, "ll", &min, &max) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (UNEXPECTED(max < min)) {
|
||||
php_error_docref(NULL, E_WARNING, "max(" ZEND_LONG_FMT ") is smaller than min(" ZEND_LONG_FMT ")", max, min);
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
||||
if (BG(mt_rand_mode) == MT_RAND_MT19937) {
|
||||
RETURN_LONG(php_mt_rand_range(min, max));
|
||||
}
|
||||
|
||||
/* Legacy mode deliberately not inside php_mt_rand_range()
|
||||
* to prevent other functions being affected */
|
||||
n = (zend_long)php_mt_rand() >> 1;
|
||||
RAND_RANGE_BADSCALING(n, min, max, PHP_MT_RAND_MAX);
|
||||
RETURN_LONG(n);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto int mt_getrandmax(void)
|
||||
Returns the maximum value a random number from Mersenne Twister can have */
|
||||
PHP_FUNCTION(mt_getrandmax)
|
||||
{
|
||||
if (zend_parse_parameters_none() == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Melo: it could be 2^^32 but we only use 2^^31 to maintain
|
||||
* compatibility with the previous php_rand
|
||||
*/
|
||||
RETURN_LONG(PHP_MT_RAND_MAX); /* 2^^31 */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
PHP_MINIT_FUNCTION(mt_rand)
|
||||
{
|
||||
REGISTER_LONG_CONSTANT("MT_RAND_MT19937", MT_RAND_MT19937, CONST_CS | CONST_PERSISTENT);
|
||||
REGISTER_LONG_CONSTANT("MT_RAND_PHP", MT_RAND_PHP, CONST_CS | CONST_PERSISTENT);
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* tab-width: 4
|
||||
* c-basic-offset: 4
|
||||
* End:
|
||||
* vim600: noet sw=4 ts=4 fdm=marker
|
||||
* vim<600: noet sw=4 ts=4
|
||||
*/
|
||||
|
43
ext/standard/php_mt_rand.h
Normal file
43
ext/standard/php_mt_rand.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| PHP Version 7 |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) 1997-2016 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 <rasmus@php.net> |
|
||||
| Zeev Suraski <zeev@zend.com> |
|
||||
| Pedro Melo <melo@ip.pt> |
|
||||
| Sterling Hughes <sterling@php.net> |
|
||||
| |
|
||||
| Based on code from: Shawn Cokus <Cokus@math.washington.edu> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef PHP_MT_RAND_H
|
||||
#define PHP_MT_RAND_H
|
||||
|
||||
#include "php_lcg.h"
|
||||
#include "php_rand.h"
|
||||
|
||||
#define PHP_MT_RAND_MAX ((zend_long) (0x7FFFFFFF)) /* (1<<31) - 1 */
|
||||
|
||||
#define MT_RAND_MT19937 0
|
||||
#define MT_RAND_PHP 1
|
||||
|
||||
PHPAPI void php_mt_srand(uint32_t seed);
|
||||
PHPAPI uint32_t php_mt_rand(void);
|
||||
PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max);
|
||||
|
||||
PHP_MINIT_FUNCTION(mt_rand);
|
||||
|
||||
#endif /* PHP_MT_RAND_H */
|
||||
|
@ -25,27 +25,46 @@
|
||||
#ifndef PHP_RAND_H
|
||||
#define PHP_RAND_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "basic_functions.h"
|
||||
#include "php_lcg.h"
|
||||
#include "php_mt_rand.h"
|
||||
|
||||
/* System Rand functions */
|
||||
#ifndef RAND_MAX
|
||||
#define RAND_MAX (1<<15)
|
||||
#define RAND_MAX PHP_MT_RAND_MAX
|
||||
#endif
|
||||
|
||||
/* In ZTS mode we rely on rand_r() so we must use RAND_MAX. */
|
||||
#if !defined(ZTS) && (defined(HAVE_LRAND48) || defined(HAVE_RANDOM))
|
||||
#define PHP_RAND_MAX 2147483647
|
||||
#else
|
||||
#define PHP_RAND_MAX RAND_MAX
|
||||
#endif
|
||||
#define PHP_RAND_MAX PHP_MT_RAND_MAX
|
||||
|
||||
/*
|
||||
* 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
|
||||
* of random values over the range. Instead we map the range directly.
|
||||
*
|
||||
* We need to map the range from 0...M evenly to the range a...b
|
||||
* Let n = the random number and n' = the mapped random number
|
||||
*
|
||||
* Then we have: n' = a + n(b-a)/M
|
||||
*
|
||||
* We have a problem here in that only n==M will get mapped to b which
|
||||
# means the chances of getting b is much much less than getting any of
|
||||
# the other values in the range. We can fix this by increasing our range
|
||||
# artificially and using:
|
||||
#
|
||||
# n' = a + n(b-a+1)/M
|
||||
*
|
||||
# Now we only have a problem if n==M which would cause us to produce a
|
||||
# number of b+1 which would be bad. So we bump M up by one to make sure
|
||||
# this will never happen, and the final algorithm looks like this:
|
||||
#
|
||||
# n' = a + n(b-a+1)/(M+1)
|
||||
*
|
||||
* -RL
|
||||
*/
|
||||
#define RAND_RANGE_BADSCALING(__n, __min, __max, __tmax) \
|
||||
(__n) = (__min) + (zend_long) ((double) ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0)))
|
||||
|
||||
#define RAND_RANGE(__n, __min, __max, __tmax) \
|
||||
(__n) = (__min) + (zend_long) ((double) ( (double) (__max) - (__min) + 1.0) * ((__n) / ((__tmax) + 1.0)))
|
||||
|
||||
/* MT Rand */
|
||||
#define PHP_MT_RAND_MAX ((zend_long) (0x7FFFFFFF)) /* (1<<31) - 1 */
|
||||
(__n) = php_mt_rand_range((__min), (__max))
|
||||
|
||||
#ifdef PHP_WIN32
|
||||
#define GENERATE_SEED() (((zend_long) (time(0) * GetCurrentProcessId())) ^ ((zend_long) (1000000.0 * php_combined_lcg())))
|
||||
@ -55,7 +74,5 @@
|
||||
|
||||
PHPAPI void php_srand(zend_long seed);
|
||||
PHPAPI zend_long php_rand(void);
|
||||
PHPAPI void php_mt_srand(uint32_t seed);
|
||||
PHPAPI uint32_t php_mt_rand(void);
|
||||
|
||||
#endif /* PHP_RAND_H */
|
||||
|
@ -25,35 +25,15 @@
|
||||
*/
|
||||
/* $Id$ */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "php.h"
|
||||
#include "php_math.h"
|
||||
#include "php_rand.h"
|
||||
|
||||
#include "basic_functions.h"
|
||||
|
||||
|
||||
/* SYSTEM RAND FUNCTIONS */
|
||||
#include "php_mt_rand.h"
|
||||
|
||||
/* {{{ php_srand
|
||||
*/
|
||||
PHPAPI void php_srand(zend_long seed)
|
||||
{
|
||||
#ifdef ZTS
|
||||
BG(rand_seed) = (unsigned int) seed;
|
||||
#else
|
||||
# if defined(HAVE_SRANDOM)
|
||||
srandom((unsigned int) seed);
|
||||
# elif defined(HAVE_SRAND48)
|
||||
srand48(seed);
|
||||
# else
|
||||
srand((unsigned int) seed);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Seed only once */
|
||||
BG(rand_is_seeded) = 1;
|
||||
php_mt_srand(seed);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -61,314 +41,7 @@ PHPAPI void php_srand(zend_long seed)
|
||||
*/
|
||||
PHPAPI zend_long php_rand(void)
|
||||
{
|
||||
zend_long ret;
|
||||
|
||||
if (!BG(rand_is_seeded)) {
|
||||
php_srand(GENERATE_SEED());
|
||||
}
|
||||
|
||||
#ifdef ZTS
|
||||
ret = php_rand_r(&BG(rand_seed));
|
||||
#else
|
||||
# if defined(HAVE_RANDOM)
|
||||
ret = random();
|
||||
# elif defined(HAVE_LRAND48)
|
||||
ret = lrand48();
|
||||
# else
|
||||
ret = rand();
|
||||
# endif
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/* 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) ^ ((uint32_t)(-(int32_t)(loBit(u))) & 0x9908b0dfU))
|
||||
|
||||
/* {{{ php_mt_initialize
|
||||
*/
|
||||
static inline void php_mt_initialize(uint32_t seed, uint32_t *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 uint32_t *s = state;
|
||||
register uint32_t *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(void)
|
||||
{
|
||||
/* Generate N new values in state
|
||||
Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) */
|
||||
|
||||
register uint32_t *state = BG(state);
|
||||
register uint32_t *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(uint32_t seed)
|
||||
{
|
||||
/* Seed the generator with a simple uint32 */
|
||||
php_mt_initialize(seed, BG(state));
|
||||
php_mt_reload();
|
||||
|
||||
/* Seed only once */
|
||||
BG(mt_rand_is_seeded) = 1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ php_mt_rand
|
||||
*/
|
||||
PHPAPI uint32_t php_mt_rand(void)
|
||||
{
|
||||
/* Pull a 32-bit integer from the generator state
|
||||
Every other access function simply transforms the numbers extracted here */
|
||||
|
||||
register uint32_t s1;
|
||||
|
||||
if (BG(left) == 0) {
|
||||
php_mt_reload();
|
||||
}
|
||||
--BG(left);
|
||||
|
||||
s1 = *BG(next)++;
|
||||
s1 ^= (s1 >> 11);
|
||||
s1 ^= (s1 << 7) & 0x9d2c5680U;
|
||||
s1 ^= (s1 << 15) & 0xefc60000U;
|
||||
return ( s1 ^ (s1 >> 18) );
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto void srand([int seed])
|
||||
Seeds random number generator */
|
||||
PHP_FUNCTION(srand)
|
||||
{
|
||||
zend_long seed = 0;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &seed) == FAILURE)
|
||||
return;
|
||||
|
||||
if (ZEND_NUM_ARGS() == 0)
|
||||
seed = GENERATE_SEED();
|
||||
|
||||
php_srand(seed);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto void mt_srand([int seed])
|
||||
Seeds Mersenne Twister random number generator */
|
||||
PHP_FUNCTION(mt_srand)
|
||||
{
|
||||
zend_long seed = 0;
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &seed) == FAILURE)
|
||||
return;
|
||||
|
||||
if (ZEND_NUM_ARGS() == 0)
|
||||
seed = GENERATE_SEED();
|
||||
|
||||
php_mt_srand(seed);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
* of random values over the range. Instead we map the range directly.
|
||||
*
|
||||
* We need to map the range from 0...M evenly to the range a...b
|
||||
* Let n = the random number and n' = the mapped random number
|
||||
*
|
||||
* Then we have: n' = a + n(b-a)/M
|
||||
*
|
||||
* We have a problem here in that only n==M will get mapped to b which
|
||||
# means the chances of getting b is much much less than getting any of
|
||||
# the other values in the range. We can fix this by increasing our range
|
||||
# artificially and using:
|
||||
#
|
||||
# n' = a + n(b-a+1)/M
|
||||
*
|
||||
# Now we only have a problem if n==M which would cause us to produce a
|
||||
# number of b+1 which would be bad. So we bump M up by one to make sure
|
||||
# this will never happen, and the final algorithm looks like this:
|
||||
#
|
||||
# n' = a + n(b-a+1)/(M+1)
|
||||
*
|
||||
* -RL
|
||||
*/
|
||||
|
||||
/* {{{ proto int rand([int min, int max])
|
||||
Returns a random number */
|
||||
PHP_FUNCTION(rand)
|
||||
{
|
||||
zend_long min;
|
||||
zend_long max;
|
||||
zend_long number;
|
||||
int argc = ZEND_NUM_ARGS();
|
||||
|
||||
if (argc != 0 && zend_parse_parameters(argc, "ll", &min, &max) == FAILURE)
|
||||
return;
|
||||
|
||||
number = php_rand();
|
||||
if (argc == 2) {
|
||||
RAND_RANGE(number, min, max, PHP_RAND_MAX);
|
||||
}
|
||||
|
||||
RETURN_LONG(number);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto int mt_rand([int min, int max])
|
||||
Returns a random number from Mersenne Twister */
|
||||
PHP_FUNCTION(mt_rand)
|
||||
{
|
||||
zend_long min;
|
||||
zend_long max;
|
||||
zend_long number;
|
||||
int argc = ZEND_NUM_ARGS();
|
||||
|
||||
if (argc != 0) {
|
||||
if (zend_parse_parameters(argc, "ll", &min, &max) == FAILURE) {
|
||||
return;
|
||||
} else if (max < min) {
|
||||
php_error_docref(NULL, E_WARNING, "max(" ZEND_LONG_FMT ") is smaller than min(" ZEND_LONG_FMT ")", max, min);
|
||||
RETURN_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!BG(mt_rand_is_seeded)) {
|
||||
php_mt_srand(GENERATE_SEED());
|
||||
}
|
||||
|
||||
/*
|
||||
* Melo: hmms.. randomMT() returns 32 random bits...
|
||||
* Yet, the previous php_rand only returns 31 at most.
|
||||
* So I put a right shift to loose the lsb. It *seems*
|
||||
* better than clearing the msb.
|
||||
* Update:
|
||||
* I talked with Cokus via email and it won't ruin the algorithm
|
||||
*/
|
||||
number = (zend_long) (php_mt_rand() >> 1);
|
||||
if (argc == 2) {
|
||||
RAND_RANGE(number, min, max, PHP_MT_RAND_MAX);
|
||||
}
|
||||
|
||||
RETURN_LONG(number);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto int getrandmax(void)
|
||||
Returns the maximum value a random number can have */
|
||||
PHP_FUNCTION(getrandmax)
|
||||
{
|
||||
if (zend_parse_parameters_none() == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
RETURN_LONG(PHP_RAND_MAX);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto int mt_getrandmax(void)
|
||||
Returns the maximum value a random number from Mersenne Twister can have */
|
||||
PHP_FUNCTION(mt_getrandmax)
|
||||
{
|
||||
if (zend_parse_parameters_none() == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Melo: it could be 2^^32 but we only use 2^^31 to maintain
|
||||
* compatibility with the previous php_rand
|
||||
*/
|
||||
RETURN_LONG(PHP_MT_RAND_MAX); /* 2^^31 */
|
||||
return php_mt_rand();
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -18,9 +18,11 @@ echo "Done\n";
|
||||
--EXPECTF--
|
||||
Warning: array_rand() expects at least 1 parameter, 0 given in %s on line %d
|
||||
NULL
|
||||
|
||||
Warning: array_rand(): Array is empty in %s on line %d
|
||||
NULL
|
||||
|
||||
Warning: array_rand(): Second argument has to be between 1 and the number of elements in the array in %s on line %d
|
||||
Warning: array_rand(): Array is empty in %s on line %d
|
||||
NULL
|
||||
|
||||
Warning: array_rand() expects parameter 1 to be array, integer given in %s on line %d
|
||||
|
@ -3,7 +3,21 @@ Test mt_rand() output
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
mt_srand(12345678);
|
||||
mt_srand(12345678, MT_RAND_PHP);
|
||||
|
||||
for ($i=0; $i<16; $i++) {
|
||||
echo mt_rand().PHP_EOL;
|
||||
}
|
||||
echo PHP_EOL;
|
||||
|
||||
$x = 0;
|
||||
for ($i=0; $i<1024; $i++) {
|
||||
$x ^= mt_rand();
|
||||
}
|
||||
echo $x.PHP_EOL;
|
||||
echo PHP_EOL;
|
||||
|
||||
mt_srand(12345678 /*, MT_RAND_MT19937 */);
|
||||
|
||||
for ($i=0; $i<16; $i++) {
|
||||
echo mt_rand().PHP_EOL;
|
||||
@ -42,3 +56,22 @@ echo $x.PHP_EOL;
|
||||
853386222
|
||||
|
||||
1571178311
|
||||
|
||||
527860569
|
||||
1711027313
|
||||
1280820687
|
||||
688176834
|
||||
770499160
|
||||
412773096
|
||||
813703253
|
||||
898651287
|
||||
52508912
|
||||
757323740
|
||||
511765911
|
||||
274407457
|
||||
833082629
|
||||
1923803667
|
||||
1461450755
|
||||
1301698200
|
||||
|
||||
1612214270
|
||||
|
@ -2,12 +2,12 @@
|
||||
Test mt_srand() - wrong params test mt_srand()
|
||||
--FILE--
|
||||
<?php
|
||||
var_dump(mt_srand(500, true));
|
||||
var_dump(mt_srand(500, 0, true));
|
||||
var_dump(mt_srand("fivehundred"));
|
||||
var_dump(mt_srand("500ABC"));
|
||||
?>
|
||||
--EXPECTF--
|
||||
Warning: mt_srand() expects at most 1 parameter, 2 given in %s on line 2
|
||||
Warning: mt_srand() expects at most 2 parameters, 3 given in %s on line 2
|
||||
NULL
|
||||
|
||||
Warning: mt_srand() expects parameter 1 to be integer, string given in %s on line 3
|
||||
|
@ -13,7 +13,7 @@ Test srand() function : error conditions - incorrect number of args
|
||||
|
||||
echo "*** Testing srand() : error conditions ***\n";
|
||||
|
||||
var_dump(srand(500, true));
|
||||
var_dump(srand(500, 0, true));
|
||||
var_dump(srand("fivehundred"));
|
||||
var_dump(srand("500ABC"));
|
||||
?>
|
||||
@ -21,7 +21,7 @@ var_dump(srand("500ABC"));
|
||||
--EXPECTF--
|
||||
*** Testing srand() : error conditions ***
|
||||
|
||||
Warning: srand() expects at most 1 parameter, 2 given in %s on line %d
|
||||
Warning: srand() expects at most 2 parameters, 3 given in %s on line %d
|
||||
NULL
|
||||
|
||||
Warning: srand() expects parameter 1 to be integer, string given in %s on line %d
|
||||
|
Loading…
Reference in New Issue
Block a user