php-src/ext/random/engine_combinedlcg.c
Tim Düsterhus 99e7cf074b
random: Clean up seeding API (#13540)
* random: Expose xoshiro256**'s seeding functions

* random: Expose pcgoneseq128xslrr64's seeding functions

* random: Expose Mt19937's seeding functions

* random: Expose CombinedLCG's seeding functions

* random: Call php_random_mt19937_seed32 to seed the global Mt19937

This avoids the function pointer indirection and improves type safety.

* random: NULL the generic seeding function

Different engines work quite differently, it is not useful to attempt to seed
them in a generic way using a 64 bit integer. As an example Mt19937 completely
ignores the upper 32 bits.

* random: Remove the `seed` member from `php_random_algo`

See the explanation in the previous commit for the reasoning. This member is
unused since the previous commit and was not consistently available even before
that (specifically for the Secure engine).

* UPGRADING.INTERNALS

* random: Remove useless cast in `php_mt_srand()`
2024-02-29 08:03:35 +01:00

129 lines
3.6 KiB
C

/*
+----------------------------------------------------------------------+
| Copyright (c) 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: |
| https://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: Sascha Schumann <sascha@schumann.cx> |
| Go Kudo <zeriyoshi@php.net> |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "php.h"
#include "php_random.h"
#include "Zend/zend_exceptions.h"
/*
* combinedLCG() returns a pseudo random number in the range of (0, 1).
* The function combines two CGs with periods of
* 2^31 - 85 and 2^31 - 249. The period of this function
* is equal to the product of both primes.
*/
#define MODMULT(a, b, c, m, s) q = s / a; s = b * (s - a * q) - c * q; if (s < 0) s += m
PHPAPI void php_random_combinedlcg_seed64(php_random_status_state_combinedlcg *state, uint64_t seed)
{
state->state[0] = seed & 0xffffffffU;
state->state[1] = seed >> 32;
}
static php_random_result generate(void *state)
{
php_random_status_state_combinedlcg *s = state;
int32_t q, z;
MODMULT(53668, 40014, 12211, 2147483563L, s->state[0]);
MODMULT(52774, 40692, 3791, 2147483399L, s->state[1]);
z = s->state[0] - s->state[1];
if (z < 1) {
z += 2147483562;
}
return (php_random_result){
.size = sizeof(uint32_t),
.result = (uint64_t) z,
};
}
static zend_long range(void *state, zend_long min, zend_long max)
{
return php_random_range((php_random_algo_with_state){
.algo = &php_random_algo_combinedlcg,
.state = state,
}, min, max);
}
static bool serialize(void *state, HashTable *data)
{
php_random_status_state_combinedlcg *s = state;
zval t;
for (uint32_t i = 0; i < 2; i++) {
ZVAL_STR(&t, php_random_bin2hex_le(&s->state[i], sizeof(uint32_t)));
zend_hash_next_index_insert(data, &t);
}
return true;
}
static bool unserialize(void *state, HashTable *data)
{
php_random_status_state_combinedlcg *s = state;
zval *t;
for (uint32_t i = 0; i < 2; i++) {
t = zend_hash_index_find(data, i);
if (!t || Z_TYPE_P(t) != IS_STRING || Z_STRLEN_P(t) != (2 * sizeof(uint32_t))) {
return false;
}
if (!php_random_hex2bin_le(Z_STR_P(t), &s->state[i])) {
return false;
}
}
return true;
}
const php_random_algo php_random_algo_combinedlcg = {
sizeof(php_random_status_state_combinedlcg),
generate,
range,
serialize,
unserialize
};
/* {{{ php_random_combinedlcg_seed_default */
PHPAPI void php_random_combinedlcg_seed_default(php_random_status_state_combinedlcg *state)
{
struct timeval tv;
if (gettimeofday(&tv, NULL) == 0) {
state->state[0] = tv.tv_usec ^ (tv.tv_usec << 11);
} else {
state->state[0] = 1;
}
#ifdef ZTS
state->state[1] = (zend_long) tsrm_thread_id();
#else
state->state[1] = (zend_long) getpid();
#endif
/* Add entropy to s2 by calling gettimeofday() again */
if (gettimeofday(&tv, NULL) == 0) {
state->state[1] ^= (tv.tv_usec << 11);
}
}
/* }}} */