mirror of
https://github.com/php/php-src.git
synced 2026-04-24 16:38:25 +02:00
162e1dce98
Instead of returning the generated `uint64_t` and providing the size (i.e. the number of bytes of the generated value) out-of-band via the `last_generated_size` member of the `php_random_status` struct, the `generate` function is now expected to return a new `php_random_result` struct containing both the `size` and the `result`. This has two benefits, one for the developer: It's no longer possible to forget setting `last_generated_size` to the correct value, because it now happens at the time of returning from the function. and the other benefit is for performance: The `php_random_result` struct will be returned as a register pair, thus the `size` will be directly available without reloading it from main memory. Checking a simplified version of `php_random_range64()` on Compiler Explorer (“Godbolt”) with clang 17 shows a single change in the resulting assembly showcasing the improvement (https://godbolt.org/z/G4WjdYxqx): - add rbp, qword ptr [r14] + add rbp, rdx Empirical testing confirms a measurable performance increase for the `Randomizer::getBytes()` method: <?php $e = new Random\Engine\Xoshiro256StarStar(0); $r = new Random\Randomizer($e); var_dump(strlen($r->getBytes(100000000))); goes from 250ms (before the change) to 220ms (after the change). While generating 100 MB of random data certainly is not the most common use case, it confirms the theoretical improvement in practice.
130 lines
3.6 KiB
C
130 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
|
|
|
|
static void seed(php_random_status *status, uint64_t seed)
|
|
{
|
|
php_random_status_state_combinedlcg *s = status->state;
|
|
|
|
s->state[0] = seed & 0xffffffffU;
|
|
s->state[1] = seed >> 32;
|
|
}
|
|
|
|
static php_random_result generate(php_random_status *status)
|
|
{
|
|
php_random_status_state_combinedlcg *s = status->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(php_random_status *status, zend_long min, zend_long max)
|
|
{
|
|
return php_random_range(&php_random_algo_combinedlcg, status, min, max);
|
|
}
|
|
|
|
static bool serialize(php_random_status *status, HashTable *data)
|
|
{
|
|
php_random_status_state_combinedlcg *s = status->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(php_random_status *status, HashTable *data)
|
|
{
|
|
php_random_status_state_combinedlcg *s = status->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),
|
|
seed,
|
|
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);
|
|
}
|
|
}
|
|
/* }}} */
|