1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

random: Pass algorithm and state together as php_random_algo_with_state (#13350)

* random: Remove `php_random_status`

Since 162e1dce98, the `php_random_status` struct
contains just a single `void*`, resulting in needless indirection when
accessing the engine state and thus decreasing readability because of the
additional non-meaningful `->state` references / the local helper variables.

There is also a small, but measurable performance benefit:

    <?php
    $e = new Random\Engine\Xoshiro256StarStar(0);
    $r = new Random\Randomizer($e);

    for ($i = 0; $i < 15; $i++)
    	var_dump(strlen($r->getBytes(100000000)));

goes from roughly 3.85s down to 3.60s.

The names of the `status` variables have not yet been touched to keep the diff
small. They will be renamed to the more appropriate `state` in a follow-up
cleanup commit.

* Introduce `php_random_algo_with_state`
This commit is contained in:
Tim Düsterhus
2024-02-25 20:48:58 +01:00
committed by GitHub
parent d024dd3abd
commit 79133df156
15 changed files with 211 additions and 171 deletions

View File

@@ -109,6 +109,13 @@ PHP 8.4 INTERNALS UPGRADE NOTES
the new php_random_result struct, replacing the last_generated_size
member of the php_random_status struct and the generate_size member of
the php_random_algo struct.
- The php_random_status struct has been removed, since the previous change
reduced it to a single void* member containing the actual state, resulting
in needless indirection. Functions taking a php_random_algo struct pointer
and a php_random_status struct pointer as separate parameters now take a
single php_random_algo_with_state struct by value, making it easier to
pass around the state with its associated algorithm and thus reducing
the chance for mistakes.
- The CSPRNG API (php_random_(bytes|int)_*) is now provided by the new
and much smaller php_random_csprng.h header. The new header is included
in php_random.h for compatibility with existing users.

View File

@@ -32,17 +32,17 @@
*/
#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)
static void seed(void *status, uint64_t seed)
{
php_random_status_state_combinedlcg *s = status->state;
php_random_status_state_combinedlcg *s = status;
s->state[0] = seed & 0xffffffffU;
s->state[1] = seed >> 32;
}
static php_random_result generate(php_random_status *status)
static php_random_result generate(void *status)
{
php_random_status_state_combinedlcg *s = status->state;
php_random_status_state_combinedlcg *s = status;
int32_t q, z;
MODMULT(53668, 40014, 12211, 2147483563L, s->state[0]);
@@ -59,14 +59,17 @@ static php_random_result generate(php_random_status *status)
};
}
static zend_long range(php_random_status *status, zend_long min, zend_long max)
static zend_long range(void *status, zend_long min, zend_long max)
{
return php_random_range(&php_random_algo_combinedlcg, status, min, max);
return php_random_range((php_random_algo_with_state){
.algo = &php_random_algo_combinedlcg,
.status = status,
}, min, max);
}
static bool serialize(php_random_status *status, HashTable *data)
static bool serialize(void *status, HashTable *data)
{
php_random_status_state_combinedlcg *s = status->state;
php_random_status_state_combinedlcg *s = status;
zval t;
for (uint32_t i = 0; i < 2; i++) {
@@ -77,9 +80,9 @@ static bool serialize(php_random_status *status, HashTable *data)
return true;
}
static bool unserialize(php_random_status *status, HashTable *data)
static bool unserialize(void *status, HashTable *data)
{
php_random_status_state_combinedlcg *s = status->state;
php_random_status_state_combinedlcg *s = status;
zval *t;
for (uint32_t i = 0; i < 2; i++) {

View File

@@ -139,14 +139,14 @@ static inline void mt19937_seed_state(php_random_status_state_mt19937 *state, ui
mt19937_reload(state);
}
static void seed(php_random_status *status, uint64_t seed)
static void seed(void *status, uint64_t seed)
{
mt19937_seed_state(status->state, seed);
mt19937_seed_state(status, seed);
}
static php_random_result generate(php_random_status *status)
static php_random_result generate(void *status)
{
php_random_status_state_mt19937 *s = status->state;
php_random_status_state_mt19937 *s = status;
uint32_t s1;
if (s->count >= MT_N) {
@@ -164,14 +164,17 @@ static php_random_result generate(php_random_status *status)
};
}
static zend_long range(php_random_status *status, zend_long min, zend_long max)
static zend_long range(void *status, zend_long min, zend_long max)
{
return php_random_range(&php_random_algo_mt19937, status, min, max);
return php_random_range((php_random_algo_with_state){
.algo = &php_random_algo_mt19937,
.status = status,
}, min, max);
}
static bool serialize(php_random_status *status, HashTable *data)
static bool serialize(void *status, HashTable *data)
{
php_random_status_state_mt19937 *s = status->state;
php_random_status_state_mt19937 *s = status;
zval t;
for (uint32_t i = 0; i < MT_N; i++) {
@@ -186,9 +189,9 @@ static bool serialize(php_random_status *status, HashTable *data)
return true;
}
static bool unserialize(php_random_status *status, HashTable *data)
static bool unserialize(void *status, HashTable *data)
{
php_random_status_state_mt19937 *s = status->state;
php_random_status_state_mt19937 *s = status;
zval *t;
/* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */
@@ -251,8 +254,8 @@ PHPAPI void php_random_mt19937_seed_default(php_random_status_state_mt19937 *sta
/* {{{ Random\Engine\Mt19937::__construct() */
PHP_METHOD(Random_Engine_Mt19937, __construct)
{
php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
php_random_status_state_mt19937 *state = engine->status->state;
php_random_algo_with_state engine = Z_RANDOM_ENGINE_P(ZEND_THIS)->engine;
php_random_status_state_mt19937 *state = engine.status;
zend_long seed, mode = MT_RAND_MT19937;
bool seed_is_null = true;
@@ -290,12 +293,12 @@ PHP_METHOD(Random_Engine_Mt19937, __construct)
/* {{{ Random\Engine\Mt19937::generate() */
PHP_METHOD(Random_Engine_Mt19937, generate)
{
php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
php_random_algo_with_state engine = Z_RANDOM_ENGINE_P(ZEND_THIS)->engine;
zend_string *bytes;
ZEND_PARSE_PARAMETERS_NONE();
php_random_result generated = engine->algo->generate(engine->status);
php_random_result generated = engine.algo->generate(engine.status);
if (EG(exception)) {
RETURN_THROWS();
}
@@ -329,7 +332,7 @@ PHP_METHOD(Random_Engine_Mt19937, __serialize)
/* state */
array_init(&t);
if (!engine->algo->serialize(engine->status, Z_ARRVAL(t))) {
if (!engine->engine.algo->serialize(engine->engine.status, Z_ARRVAL(t))) {
zend_throw_exception(NULL, "Engine serialize failed", 0);
RETURN_THROWS();
}
@@ -372,7 +375,7 @@ PHP_METHOD(Random_Engine_Mt19937, __unserialize)
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name));
RETURN_THROWS();
}
if (!engine->algo->unserialize(engine->status, Z_ARRVAL_P(t))) {
if (!engine->engine.algo->unserialize(engine->engine.status, Z_ARRVAL_P(t))) {
zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name));
RETURN_THROWS();
}
@@ -392,9 +395,9 @@ PHP_METHOD(Random_Engine_Mt19937, __debugInfo)
}
ZVAL_ARR(return_value, zend_array_dup(engine->std.properties));
if (engine->algo->serialize) {
if (engine->engine.algo->serialize) {
array_init(&t);
if (!engine->algo->serialize(engine->status, Z_ARRVAL(t))) {
if (!engine->engine.algo->serialize(engine->engine.status, Z_ARRVAL(t))) {
zend_throw_exception(NULL, "Engine serialize failed", 0);
RETURN_THROWS();
}

View File

@@ -43,14 +43,14 @@ static inline void seed128(php_random_status_state_pcgoneseq128xslrr64 *s, php_r
step(s);
}
static void seed(php_random_status *status, uint64_t seed)
static void seed(void *status, uint64_t seed)
{
seed128(status->state, php_random_uint128_constant(0ULL, seed));
seed128(status, php_random_uint128_constant(0ULL, seed));
}
static php_random_result generate(php_random_status *status)
static php_random_result generate(void *status)
{
php_random_status_state_pcgoneseq128xslrr64 *s = status->state;
php_random_status_state_pcgoneseq128xslrr64 *s = status;
step(s);
@@ -60,14 +60,17 @@ static php_random_result generate(php_random_status *status)
};
}
static zend_long range(php_random_status *status, zend_long min, zend_long max)
static zend_long range(void *status, zend_long min, zend_long max)
{
return php_random_range(&php_random_algo_pcgoneseq128xslrr64, status, min, max);
return php_random_range((php_random_algo_with_state){
.algo = &php_random_algo_pcgoneseq128xslrr64,
.status = status,
}, min, max);
}
static bool serialize(php_random_status *status, HashTable *data)
static bool serialize(void *status, HashTable *data)
{
php_random_status_state_pcgoneseq128xslrr64 *s = status->state;
php_random_status_state_pcgoneseq128xslrr64 *s = status;
uint64_t u;
zval z;
@@ -82,9 +85,9 @@ static bool serialize(php_random_status *status, HashTable *data)
return true;
}
static bool unserialize(php_random_status *status, HashTable *data)
static bool unserialize(void *status, HashTable *data)
{
php_random_status_state_pcgoneseq128xslrr64 *s = status->state;
php_random_status_state_pcgoneseq128xslrr64 *s = status;
uint64_t u[2];
zval *t;
@@ -142,8 +145,8 @@ PHPAPI void php_random_pcgoneseq128xslrr64_advance(php_random_status_state_pcgon
/* {{{ Random\Engine\PcgOneseq128XslRr64::__construct */
PHP_METHOD(Random_Engine_PcgOneseq128XslRr64, __construct)
{
php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
php_random_status_state_pcgoneseq128xslrr64 *state = engine->status->state;
php_random_algo_with_state engine = Z_RANDOM_ENGINE_P(ZEND_THIS)->engine;
php_random_status_state_pcgoneseq128xslrr64 *state = engine.status;
zend_string *str_seed = NULL;
zend_long int_seed = 0;
bool seed_is_null = true;
@@ -191,8 +194,8 @@ PHP_METHOD(Random_Engine_PcgOneseq128XslRr64, __construct)
/* {{{ Random\Engine\PcgOneseq128XslRr64::jump() */
PHP_METHOD(Random_Engine_PcgOneseq128XslRr64, jump)
{
php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
php_random_status_state_pcgoneseq128xslrr64 *state = engine->status->state;
php_random_algo_with_state engine = Z_RANDOM_ENGINE_P(ZEND_THIS)->engine;
php_random_status_state_pcgoneseq128xslrr64 *state = engine.status;
zend_long advance = 0;
ZEND_PARSE_PARAMETERS_START(1, 1)

View File

@@ -25,7 +25,7 @@
#include "Zend/zend_exceptions.h"
static php_random_result generate(php_random_status *status)
static php_random_result generate(void *status)
{
zend_ulong r = 0;
@@ -37,7 +37,7 @@ static php_random_result generate(php_random_status *status)
};
}
static zend_long range(php_random_status *status, zend_long min, zend_long max)
static zend_long range(void *status, zend_long min, zend_long max)
{
zend_long result = 0;

View File

@@ -21,9 +21,9 @@
#include "php.h"
#include "php_random.h"
static php_random_result generate(php_random_status *status)
static php_random_result generate(void *status)
{
php_random_status_state_user *s = status->state;
php_random_status_state_user *s = status;
uint64_t result = 0;
size_t size;
zval retval;
@@ -65,9 +65,12 @@ static php_random_result generate(php_random_status *status)
};
}
static zend_long range(php_random_status *status, zend_long min, zend_long max)
static zend_long range(void *status, zend_long min, zend_long max)
{
return php_random_range(&php_random_algo_user, status, min, max);
return php_random_range((php_random_algo_with_state){
.algo = &php_random_algo_user,
.status = status,
}, min, max);
}
const php_random_algo php_random_algo_user = {

View File

@@ -102,27 +102,30 @@ static inline void seed64(php_random_status_state_xoshiro256starstar *state, uin
seed256(state, s[0], s[1], s[2], s[3]);
}
static void seed(php_random_status *status, uint64_t seed)
static void seed(void *status, uint64_t seed)
{
seed64(status->state, seed);
seed64(status, seed);
}
static php_random_result generate(php_random_status *status)
static php_random_result generate(void *status)
{
return (php_random_result){
.size = sizeof(uint64_t),
.result = generate_state(status->state),
.result = generate_state(status),
};
}
static zend_long range(php_random_status *status, zend_long min, zend_long max)
static zend_long range(void *status, zend_long min, zend_long max)
{
return php_random_range(&php_random_algo_xoshiro256starstar, status, min, max);
return php_random_range((php_random_algo_with_state){
.algo = &php_random_algo_xoshiro256starstar,
.status = status,
}, min, max);
}
static bool serialize(php_random_status *status, HashTable *data)
static bool serialize(void *status, HashTable *data)
{
php_random_status_state_xoshiro256starstar *s = status->state;
php_random_status_state_xoshiro256starstar *s = status;
zval t;
for (uint32_t i = 0; i < 4; i++) {
@@ -133,9 +136,9 @@ static bool serialize(php_random_status *status, HashTable *data)
return true;
}
static bool unserialize(php_random_status *status, HashTable *data)
static bool unserialize(void *status, HashTable *data)
{
php_random_status_state_xoshiro256starstar *s = status->state;
php_random_status_state_xoshiro256starstar *s = status;
zval *t;
/* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */
@@ -180,8 +183,8 @@ PHPAPI void php_random_xoshiro256starstar_jump_long(php_random_status_state_xosh
/* {{{ Random\Engine\Xoshiro256StarStar::jump() */
PHP_METHOD(Random_Engine_Xoshiro256StarStar, jump)
{
php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
php_random_status_state_xoshiro256starstar *state = engine->status->state;
php_random_algo_with_state engine = Z_RANDOM_ENGINE_P(ZEND_THIS)->engine;
php_random_status_state_xoshiro256starstar *state = engine.status;
ZEND_PARSE_PARAMETERS_NONE();
@@ -192,8 +195,8 @@ PHP_METHOD(Random_Engine_Xoshiro256StarStar, jump)
/* {{{ Random\Engine\Xoshiro256StarStar::jumpLong() */
PHP_METHOD(Random_Engine_Xoshiro256StarStar, jumpLong)
{
php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
php_random_status_state_xoshiro256starstar *state = engine->status->state;
php_random_algo_with_state engine = Z_RANDOM_ENGINE_P(ZEND_THIS)->engine;
php_random_status_state_xoshiro256starstar *state = engine.status;
ZEND_PARSE_PARAMETERS_NONE();
@@ -204,8 +207,8 @@ PHP_METHOD(Random_Engine_Xoshiro256StarStar, jumpLong)
/* {{{ Random\Engine\Xoshiro256StarStar::__construct */
PHP_METHOD(Random_Engine_Xoshiro256StarStar, __construct)
{
php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
php_random_status_state_xoshiro256starstar *state = engine->status->state;
php_random_algo_with_state engine = Z_RANDOM_ENGINE_P(ZEND_THIS)->engine;
php_random_status_state_xoshiro256starstar *state = engine.status;
zend_string *str_seed = NULL;
zend_long int_seed = 0;
bool seed_is_null = true;

View File

@@ -71,7 +71,7 @@ static uint64_t ceilint(double a, double b, double g)
return (s != si) ? (uint64_t)si : (uint64_t)si + (e > 0);
}
PHPAPI double php_random_gammasection_closed_open(const php_random_algo *algo, php_random_status *status, double min, double max)
PHPAPI double php_random_gammasection_closed_open(php_random_algo_with_state engine, double min, double max)
{
double g = gamma_max(min, max);
uint64_t hi = ceilint(min, max, g);
@@ -80,7 +80,7 @@ PHPAPI double php_random_gammasection_closed_open(const php_random_algo *algo, p
return NAN;
}
uint64_t k = 1 + php_random_range64(algo, status, hi - 1); /* [1, hi] */
uint64_t k = 1 + php_random_range64(engine, hi - 1); /* [1, hi] */
if (fabs(min) <= fabs(max)) {
if (k == hi) {
@@ -99,7 +99,7 @@ PHPAPI double php_random_gammasection_closed_open(const php_random_algo *algo, p
}
}
PHPAPI double php_random_gammasection_closed_closed(const php_random_algo *algo, php_random_status *status, double min, double max)
PHPAPI double php_random_gammasection_closed_closed(php_random_algo_with_state engine, double min, double max)
{
double g = gamma_max(min, max);
uint64_t hi = ceilint(min, max, g);
@@ -108,7 +108,7 @@ PHPAPI double php_random_gammasection_closed_closed(const php_random_algo *algo,
return NAN;
}
uint64_t k = php_random_range64(algo, status, hi); /* [0, hi] */
uint64_t k = php_random_range64(engine, hi); /* [0, hi] */
if (fabs(min) <= fabs(max)) {
if (k == hi) {
@@ -131,7 +131,7 @@ PHPAPI double php_random_gammasection_closed_closed(const php_random_algo *algo,
}
}
PHPAPI double php_random_gammasection_open_closed(const php_random_algo *algo, php_random_status *status, double min, double max)
PHPAPI double php_random_gammasection_open_closed(php_random_algo_with_state engine, double min, double max)
{
double g = gamma_max(min, max);
uint64_t hi = ceilint(min, max, g);
@@ -140,7 +140,7 @@ PHPAPI double php_random_gammasection_open_closed(const php_random_algo *algo, p
return NAN;
}
uint64_t k = php_random_range64(algo, status, hi - 1); /* [0, hi - 1] */
uint64_t k = php_random_range64(engine, hi - 1); /* [0, hi - 1] */
if (fabs(min) <= fabs(max)) {
double k_hi, k_lo;
@@ -159,7 +159,7 @@ PHPAPI double php_random_gammasection_open_closed(const php_random_algo *algo, p
}
}
PHPAPI double php_random_gammasection_open_open(const php_random_algo *algo, php_random_status *status, double min, double max)
PHPAPI double php_random_gammasection_open_open(php_random_algo_with_state engine, double min, double max)
{
double g = gamma_max(min, max);
uint64_t hi = ceilint(min, max, g);
@@ -168,7 +168,7 @@ PHPAPI double php_random_gammasection_open_open(const php_random_algo *algo, php
return NAN;
}
uint64_t k = 1 + php_random_range64(algo, status, hi - 2); /* [1, hi - 1] */
uint64_t k = 1 + php_random_range64(engine, hi - 2); /* [1, hi - 1] */
if (fabs(min) <= fabs(max)) {
double k_hi, k_lo;

View File

@@ -66,10 +66,6 @@ PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max);
PHPAPI void php_srand(zend_long seed);
PHPAPI zend_long php_rand(void);
typedef struct _php_random_status_ {
void *state;
} php_random_status;
typedef struct _php_random_status_state_combinedlcg {
int32_t state[2];
} php_random_status_state_combinedlcg;
@@ -100,13 +96,18 @@ typedef struct _php_random_result {
typedef struct _php_random_algo {
const size_t state_size;
void (*seed)(php_random_status *status, uint64_t seed);
php_random_result (*generate)(php_random_status *status);
zend_long (*range)(php_random_status *status, zend_long min, zend_long max);
bool (*serialize)(php_random_status *status, HashTable *data);
bool (*unserialize)(php_random_status *status, HashTable *data);
void (*seed)(void *status, uint64_t seed);
php_random_result (*generate)(void *status);
zend_long (*range)(void *status, zend_long min, zend_long max);
bool (*serialize)(void *status, HashTable *data);
bool (*unserialize)(void *status, HashTable *data);
} php_random_algo;
typedef struct _php_random_algo_with_state {
const php_random_algo *algo;
void *status;
} php_random_algo_with_state;
extern PHPAPI const php_random_algo php_random_algo_combinedlcg;
extern PHPAPI const php_random_algo php_random_algo_mt19937;
extern PHPAPI const php_random_algo php_random_algo_pcgoneseq128xslrr64;
@@ -115,14 +116,12 @@ extern PHPAPI const php_random_algo php_random_algo_secure;
extern PHPAPI const php_random_algo php_random_algo_user;
typedef struct _php_random_engine {
const php_random_algo *algo;
php_random_status *status;
php_random_algo_with_state engine;
zend_object std;
} php_random_engine;
typedef struct _php_random_randomizer {
const php_random_algo *algo;
php_random_status *status;
php_random_algo_with_state engine;
bool is_userland_algo;
zend_object std;
} php_random_randomizer;
@@ -155,17 +154,25 @@ static inline php_random_randomizer *php_random_randomizer_from_obj(zend_object
# define Z_RANDOM_RANDOMIZER_P(zval) php_random_randomizer_from_obj(Z_OBJ_P(zval));
PHPAPI php_random_status *php_random_status_alloc(const php_random_algo *algo, const bool persistent);
PHPAPI php_random_status *php_random_status_copy(const php_random_algo *algo, php_random_status *old_status, php_random_status *new_status);
PHPAPI void php_random_status_free(php_random_status *status, const bool persistent);
PHPAPI void *php_random_status_alloc(const php_random_algo *algo, const bool persistent);
PHPAPI void *php_random_status_copy(const php_random_algo *algo, void *old_status, void *new_status);
PHPAPI void php_random_status_free(void *status, const bool persistent);
PHPAPI php_random_engine *php_random_engine_common_init(zend_class_entry *ce, zend_object_handlers *handlers, const php_random_algo *algo);
PHPAPI void php_random_engine_common_free_object(zend_object *object);
PHPAPI zend_object *php_random_engine_common_clone_object(zend_object *object);
PHPAPI uint32_t php_random_range32(const php_random_algo *algo, php_random_status *status, uint32_t umax);
PHPAPI uint64_t php_random_range64(const php_random_algo *algo, php_random_status *status, uint64_t umax);
PHPAPI zend_long php_random_range(const php_random_algo *algo, php_random_status *status, zend_long min, zend_long max);
PHPAPI uint32_t php_random_range32(php_random_algo_with_state engine, uint32_t umax);
PHPAPI uint64_t php_random_range64(php_random_algo_with_state engine, uint64_t umax);
PHPAPI zend_long php_random_range(php_random_algo_with_state engine, zend_long min, zend_long max);
PHPAPI const php_random_algo *php_random_default_algo(void);
PHPAPI php_random_status *php_random_default_status(void);
PHPAPI void *php_random_default_status(void);
static inline php_random_algo_with_state php_random_default_engine(void)
{
return (php_random_algo_with_state){
.algo = php_random_default_algo(),
.status = php_random_default_status(),
};
}
PHPAPI zend_string *php_random_bin2hex_le(const void *ptr, const size_t len);
PHPAPI bool php_random_hex2bin_le(zend_string *hexstr, void *dest);
@@ -179,10 +186,10 @@ PHPAPI void php_random_pcgoneseq128xslrr64_advance(php_random_status_state_pcgon
PHPAPI void php_random_xoshiro256starstar_jump(php_random_status_state_xoshiro256starstar *state);
PHPAPI void php_random_xoshiro256starstar_jump_long(php_random_status_state_xoshiro256starstar *state);
PHPAPI double php_random_gammasection_closed_open(const php_random_algo *algo, php_random_status *status, double min, double max);
PHPAPI double php_random_gammasection_closed_closed(const php_random_algo *algo, php_random_status *status, double min, double max);
PHPAPI double php_random_gammasection_open_closed(const php_random_algo *algo, php_random_status *status, double min, double max);
PHPAPI double php_random_gammasection_open_open(const php_random_algo *algo, php_random_status *status, double min, double max);
PHPAPI double php_random_gammasection_closed_open(php_random_algo_with_state engine, double min, double max);
PHPAPI double php_random_gammasection_closed_closed(php_random_algo_with_state engine, double min, double max);
PHPAPI double php_random_gammasection_open_closed(php_random_algo_with_state engine, double min, double max);
PHPAPI double php_random_gammasection_open_open(php_random_algo_with_state engine, double min, double max);
extern zend_module_entry random_module_entry;
# define phpext_random_ptr &random_module_entry
@@ -192,9 +199,9 @@ PHP_MSHUTDOWN_FUNCTION(random);
PHP_RINIT_FUNCTION(random);
ZEND_BEGIN_MODULE_GLOBALS(random)
php_random_status *combined_lcg;
php_random_status_state_combinedlcg *combined_lcg;
bool combined_lcg_seeded;
php_random_status *mt19937;
php_random_status_state_mt19937 *mt19937;
bool mt19937_seeded;
int random_fd;
ZEND_END_MODULE_GLOBALS(random)

View File

@@ -74,8 +74,11 @@ static zend_object_handlers random_engine_xoshiro256starstar_object_handlers;
static zend_object_handlers random_engine_secure_object_handlers;
static zend_object_handlers random_randomizer_object_handlers;
PHPAPI uint32_t php_random_range32(const php_random_algo *algo, php_random_status *status, uint32_t umax)
PHPAPI uint32_t php_random_range32(php_random_algo_with_state engine, uint32_t umax)
{
const php_random_algo *algo = engine.algo;
void *status = engine.status;
uint32_t result, limit;
size_t total_size = 0;
uint32_t count = 0;
@@ -130,8 +133,11 @@ PHPAPI uint32_t php_random_range32(const php_random_algo *algo, php_random_statu
return result % umax;
}
PHPAPI uint64_t php_random_range64(const php_random_algo *algo, php_random_status *status, uint64_t umax)
PHPAPI uint64_t php_random_range64(php_random_algo_with_state engine, uint64_t umax)
{
const php_random_algo *algo = engine.algo;
void *status = engine.status;
uint64_t result, limit;
size_t total_size = 0;
uint32_t count = 0;
@@ -220,34 +226,24 @@ static void randomizer_free_obj(zend_object *object) {
php_random_randomizer *randomizer = php_random_randomizer_from_obj(object);
if (randomizer->is_userland_algo) {
php_random_status_free(randomizer->status, false);
php_random_status_free(randomizer->engine.status, false);
}
zend_object_std_dtor(&randomizer->std);
}
PHPAPI php_random_status *php_random_status_alloc(const php_random_algo *algo, const bool persistent)
PHPAPI void *php_random_status_alloc(const php_random_algo *algo, const bool persistent)
{
php_random_status *status = pecalloc(1, sizeof(php_random_status), persistent);
status->state = algo->state_size > 0 ? pecalloc(1, algo->state_size, persistent) : NULL;
return status;
return algo->state_size > 0 ? pecalloc(1, algo->state_size, persistent) : NULL;
}
PHPAPI php_random_status *php_random_status_copy(const php_random_algo *algo, php_random_status *old_status, php_random_status *new_status)
PHPAPI void *php_random_status_copy(const php_random_algo *algo, void *old_status, void *new_status)
{
new_status->state = memcpy(new_status->state, old_status->state, algo->state_size);
return new_status;
return memcpy(new_status, old_status, algo->state_size);
}
PHPAPI void php_random_status_free(php_random_status *status, const bool persistent)
PHPAPI void php_random_status_free(void *status, const bool persistent)
{
if (status != NULL) {
pefree(status->state, persistent);
}
pefree(status, persistent);
}
@@ -258,8 +254,10 @@ PHPAPI php_random_engine *php_random_engine_common_init(zend_class_entry *ce, ze
zend_object_std_init(&engine->std, ce);
object_properties_init(&engine->std, ce);
engine->algo = algo;
engine->status = php_random_status_alloc(engine->algo, false);
engine->engine = (php_random_algo_with_state){
.algo = algo,
.status = php_random_status_alloc(algo, false)
};
engine->std.handlers = handlers;
return engine;
@@ -269,7 +267,7 @@ PHPAPI void php_random_engine_common_free_object(zend_object *object)
{
php_random_engine *engine = php_random_engine_from_obj(object);
php_random_status_free(engine->status, false);
php_random_status_free(engine->engine.status, false);
zend_object_std_dtor(object);
}
@@ -278,9 +276,9 @@ PHPAPI zend_object *php_random_engine_common_clone_object(zend_object *object)
php_random_engine *old_engine = php_random_engine_from_obj(object);
php_random_engine *new_engine = php_random_engine_from_obj(old_engine->std.ce->create_object(old_engine->std.ce));
new_engine->algo = old_engine->algo;
if (old_engine->status) {
new_engine->status = php_random_status_copy(old_engine->algo, old_engine->status, new_engine->status);
new_engine->engine.algo = old_engine->engine.algo;
if (old_engine->engine.status) {
new_engine->engine.status = php_random_status_copy(old_engine->engine.algo, old_engine->engine.status, new_engine->engine.status);
}
zend_objects_clone_members(&new_engine->std, &old_engine->std);
@@ -289,15 +287,15 @@ PHPAPI zend_object *php_random_engine_common_clone_object(zend_object *object)
}
/* {{{ php_random_range */
PHPAPI zend_long php_random_range(const php_random_algo *algo, php_random_status *status, zend_long min, zend_long max)
PHPAPI zend_long php_random_range(php_random_algo_with_state engine, zend_long min, zend_long max)
{
zend_ulong umax = (zend_ulong) max - (zend_ulong) min;
if (umax > UINT32_MAX) {
return (zend_long) (php_random_range64(algo, status, umax) + min);
return (zend_long) (php_random_range64(engine, umax) + min);
}
return (zend_long) (php_random_range32(algo, status, umax) + min);
return (zend_long) (php_random_range32(engine, umax) + min);
}
/* }}} */
@@ -309,12 +307,12 @@ PHPAPI const php_random_algo *php_random_default_algo(void)
/* }}} */
/* {{{ php_random_default_status */
PHPAPI php_random_status *php_random_default_status(void)
PHPAPI void *php_random_default_status(void)
{
php_random_status *status = RANDOM_G(mt19937);
php_random_status_state_mt19937 *status = RANDOM_G(mt19937);
if (!RANDOM_G(mt19937_seeded)) {
php_random_mt19937_seed_default(status->state);
php_random_mt19937_seed_default(status);
RANDOM_G(mt19937_seeded) = true;
}
@@ -392,10 +390,10 @@ PHPAPI bool php_random_hex2bin_le(zend_string *hexstr, void *dest)
/* {{{ php_combined_lcg */
PHPAPI double php_combined_lcg(void)
{
php_random_status *status = RANDOM_G(combined_lcg);
php_random_status_state_combinedlcg *status = RANDOM_G(combined_lcg);
if (!RANDOM_G(combined_lcg_seeded)) {
php_random_combinedlcg_seed_default(status->state);
php_random_combinedlcg_seed_default(status);
RANDOM_G(combined_lcg_seeded) = true;
}
@@ -429,8 +427,7 @@ PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max)
* rand() allows min > max, mt_rand does not */
PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max)
{
php_random_status *status = php_random_default_status();
php_random_status_state_mt19937 *s = status->state;
php_random_status_state_mt19937 *s = php_random_default_status();
if (s->mode == MT_RAND_MT19937) {
return php_mt_rand_range(min, max);
@@ -476,8 +473,7 @@ PHP_FUNCTION(mt_srand)
zend_long seed = 0;
bool seed_is_null = true;
zend_long mode = MT_RAND_MT19937;
php_random_status *status = RANDOM_G(mt19937);
php_random_status_state_mt19937 *state = status->state;
php_random_status_state_mt19937 *state = RANDOM_G(mt19937);
ZEND_PARSE_PARAMETERS_START(0, 2)
Z_PARAM_OPTIONAL
@@ -493,9 +489,9 @@ PHP_FUNCTION(mt_srand)
}
if (seed_is_null) {
php_random_mt19937_seed_default(status->state);
php_random_mt19937_seed_default(state);
} else {
php_random_algo_mt19937.seed(status, (uint64_t) seed);
php_random_algo_mt19937.seed(state, (uint64_t) seed);
}
RANDOM_G(mt19937_seeded) = true;
}

View File

@@ -33,12 +33,15 @@ static inline void randomizer_common_init(php_random_randomizer *randomizer, zen
php_random_engine *engine = php_random_engine_from_obj(engine_object);
/* Copy engine pointers */
randomizer->algo = engine->algo;
randomizer->status = engine->status;
randomizer->engine = engine->engine;
} else {
/* Self allocation */
randomizer->status = php_random_status_alloc(&php_random_algo_user, false);
php_random_status_state_user *state = randomizer->status->state;
php_random_status_state_user *state = php_random_status_alloc(&php_random_algo_user, false);
randomizer->engine = (php_random_algo_with_state){
.algo = &php_random_algo_user,
.status = state,
};
zend_string *mname;
zend_function *generate_method;
@@ -50,9 +53,6 @@ static inline void randomizer_common_init(php_random_randomizer *randomizer, zen
state->object = engine_object;
state->generate_method = generate_method;
/* Copy common pointers */
randomizer->algo = &php_random_algo_user;
/* Mark self-allocated for memory management */
randomizer->is_userland_algo = true;
}
@@ -93,6 +93,8 @@ PHP_METHOD(Random_Randomizer, __construct)
PHP_METHOD(Random_Randomizer, nextFloat)
{
php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS);
php_random_algo_with_state engine = randomizer->engine;
uint64_t result;
size_t total_size;
@@ -101,7 +103,7 @@ PHP_METHOD(Random_Randomizer, nextFloat)
result = 0;
total_size = 0;
do {
php_random_result r = randomizer->algo->generate(randomizer->status);
php_random_result r = engine.algo->generate(engine.status);
result = result | (r.result << (total_size * 8));
total_size += r.size;
if (EG(exception)) {
@@ -169,28 +171,28 @@ PHP_METHOD(Random_Randomizer, getFloat)
RETURN_THROWS();
}
RETURN_DOUBLE(php_random_gammasection_closed_open(randomizer->algo, randomizer->status, min, max));
RETURN_DOUBLE(php_random_gammasection_closed_open(randomizer->engine, min, max));
case 'C' + sizeof("ClosedClosed") - 1:
if (UNEXPECTED(max < min)) {
zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)");
RETURN_THROWS();
}
RETURN_DOUBLE(php_random_gammasection_closed_closed(randomizer->algo, randomizer->status, min, max));
RETURN_DOUBLE(php_random_gammasection_closed_closed(randomizer->engine, min, max));
case 'O' + sizeof("OpenClosed") - 1:
if (UNEXPECTED(max <= min)) {
zend_argument_value_error(2, "must be greater than argument #1 ($min)");
RETURN_THROWS();
}
RETURN_DOUBLE(php_random_gammasection_open_closed(randomizer->algo, randomizer->status, min, max));
RETURN_DOUBLE(php_random_gammasection_open_closed(randomizer->engine, min, max));
case 'O' + sizeof("OpenOpen") - 1:
if (UNEXPECTED(max <= min)) {
zend_argument_value_error(2, "must be greater than argument #1 ($min)");
RETURN_THROWS();
}
RETVAL_DOUBLE(php_random_gammasection_open_open(randomizer->algo, randomizer->status, min, max));
RETVAL_DOUBLE(php_random_gammasection_open_open(randomizer->engine, min, max));
if (UNEXPECTED(isnan(Z_DVAL_P(return_value)))) {
zend_value_error("The given interval is empty, there are no floats between argument #1 ($min) and argument #2 ($max).");
@@ -208,10 +210,11 @@ PHP_METHOD(Random_Randomizer, getFloat)
PHP_METHOD(Random_Randomizer, nextInt)
{
php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS);
php_random_algo_with_state engine = randomizer->engine;
ZEND_PARSE_PARAMETERS_NONE();
php_random_result result = randomizer->algo->generate(randomizer->status);
php_random_result result = engine.algo->generate(engine.status);
if (EG(exception)) {
RETURN_THROWS();
}
@@ -228,6 +231,8 @@ PHP_METHOD(Random_Randomizer, nextInt)
PHP_METHOD(Random_Randomizer, getInt)
{
php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS);
php_random_algo_with_state engine = randomizer->engine;
uint64_t result;
zend_long min, max;
@@ -242,10 +247,10 @@ PHP_METHOD(Random_Randomizer, getInt)
}
if (UNEXPECTED(
randomizer->algo->range == php_random_algo_mt19937.range
&& ((php_random_status_state_mt19937 *) randomizer->status->state)->mode != MT_RAND_MT19937
engine.algo->range == php_random_algo_mt19937.range
&& ((php_random_status_state_mt19937 *) engine.status)->mode != MT_RAND_MT19937
)) {
uint64_t r = php_random_algo_mt19937.generate(randomizer->status).result >> 1;
uint64_t r = php_random_algo_mt19937.generate(engine.status).result >> 1;
/* This is an inlined version of the RAND_RANGE_BADSCALING macro that does not invoke UB when encountering
* (max - min) > ZEND_LONG_MAX.
@@ -254,7 +259,7 @@ PHP_METHOD(Random_Randomizer, getInt)
result = (zend_long) (offset + min);
} else {
result = randomizer->algo->range(randomizer->status, min, max);
result = engine.algo->range(engine.status, min, max);
}
if (EG(exception)) {
@@ -269,6 +274,8 @@ PHP_METHOD(Random_Randomizer, getInt)
PHP_METHOD(Random_Randomizer, getBytes)
{
php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS);
php_random_algo_with_state engine = randomizer->engine;
zend_string *retval;
zend_long length;
size_t total_size = 0;
@@ -285,7 +292,7 @@ PHP_METHOD(Random_Randomizer, getBytes)
retval = zend_string_alloc(length, 0);
while (total_size < length) {
php_random_result result = randomizer->algo->generate(randomizer->status);
php_random_result result = engine.algo->generate(engine.status);
if (EG(exception)) {
zend_string_free(retval);
RETURN_THROWS();
@@ -314,7 +321,7 @@ PHP_METHOD(Random_Randomizer, shuffleArray)
ZEND_PARSE_PARAMETERS_END();
ZVAL_DUP(return_value, array);
if (!php_array_data_shuffle(randomizer->algo, randomizer->status, return_value)) {
if (!php_array_data_shuffle(randomizer->engine, return_value)) {
RETURN_THROWS();
}
}
@@ -335,7 +342,7 @@ PHP_METHOD(Random_Randomizer, shuffleBytes)
}
RETVAL_STRINGL(ZSTR_VAL(bytes), ZSTR_LEN(bytes));
if (!php_binary_string_shuffle(randomizer->algo, randomizer->status, Z_STRVAL_P(return_value), (zend_long) Z_STRLEN_P(return_value))) {
if (!php_binary_string_shuffle(randomizer->engine, Z_STRVAL_P(return_value), (zend_long) Z_STRLEN_P(return_value))) {
RETURN_THROWS();
}
}
@@ -354,8 +361,7 @@ PHP_METHOD(Random_Randomizer, pickArrayKeys)
ZEND_PARSE_PARAMETERS_END();
if (!php_array_pick_keys(
randomizer->algo,
randomizer->status,
randomizer->engine,
input,
num_req,
return_value,
@@ -377,6 +383,8 @@ PHP_METHOD(Random_Randomizer, pickArrayKeys)
PHP_METHOD(Random_Randomizer, getBytesFromString)
{
php_random_randomizer *randomizer = Z_RANDOM_RANDOMIZER_P(ZEND_THIS);
php_random_algo_with_state engine = randomizer->engine;
zend_long length;
zend_string *source, *retval;
size_t total_size = 0;
@@ -403,7 +411,7 @@ PHP_METHOD(Random_Randomizer, getBytesFromString)
if (max_offset > 0xff) {
while (total_size < length) {
uint64_t offset = randomizer->algo->range(randomizer->status, 0, max_offset);
uint64_t offset = engine.algo->range(engine.status, 0, max_offset);
if (EG(exception)) {
zend_string_free(retval);
@@ -424,7 +432,7 @@ PHP_METHOD(Random_Randomizer, getBytesFromString)
int failures = 0;
while (total_size < length) {
php_random_result result = randomizer->algo->generate(randomizer->status);
php_random_result result = engine.algo->generate(engine.status);
if (EG(exception)) {
zend_string_free(retval);
RETURN_THROWS();

View File

@@ -3211,8 +3211,11 @@ boundary_error:
#undef RANGE_CHECK_LONG_INIT_ARRAY
/* {{{ php_array_data_shuffle */
PHPAPI bool php_array_data_shuffle(const php_random_algo *algo, php_random_status *status, zval *array) /* {{{ */
PHPAPI bool php_array_data_shuffle(php_random_algo_with_state engine, zval *array) /* {{{ */
{
const php_random_algo *algo = engine.algo;
void *status = engine.status;
int64_t idx, j, n_elems, rnd_idx, n_left;
zval *zv, temp;
HashTable *hash;
@@ -3310,7 +3313,7 @@ PHP_FUNCTION(shuffle)
Z_PARAM_ARRAY_EX(array, 0, 1)
ZEND_PARSE_PARAMETERS_END();
php_array_data_shuffle(php_random_default_algo(), php_random_default_status(), array);
php_array_data_shuffle(php_random_default_engine(), array);
RETURN_TRUE;
}
@@ -6191,8 +6194,11 @@ clean_up:
/* }}} */
/* {{{ php_array_pick_keys */
PHPAPI bool php_array_pick_keys(const php_random_algo *algo, php_random_status *status, zval *input, zend_long num_req, zval *retval, bool silent)
PHPAPI bool php_array_pick_keys(php_random_algo_with_state engine, zval *input, zend_long num_req, zval *retval, bool silent)
{
const php_random_algo *algo = engine.algo;
void *status = engine.status;
HashTable *ht = Z_ARRVAL_P(input);
uint32_t num_avail = zend_hash_num_elements(ht);
zend_long i, randval;
@@ -6350,8 +6356,7 @@ PHP_FUNCTION(array_rand)
ZEND_PARSE_PARAMETERS_END();
if (!php_array_pick_keys(
php_random_default_algo(),
php_random_default_status(),
php_random_default_engine(),
input,
num_req,
return_value,

View File

@@ -31,8 +31,8 @@ PHPAPI int php_array_replace_recursive(HashTable *dest, HashTable *src);
PHPAPI int php_multisort_compare(const void *a, const void *b);
PHPAPI zend_long php_count_recursive(HashTable *ht);
PHPAPI bool php_array_data_shuffle(const php_random_algo *algo, php_random_status *status, zval *array);
PHPAPI bool php_array_pick_keys(const php_random_algo *algo, php_random_status *status, zval *input, zend_long num_req, zval *retval, bool silent);
PHPAPI bool php_array_data_shuffle(php_random_algo_with_state engine, zval *array);
PHPAPI bool php_array_pick_keys(php_random_algo_with_state engine, zval *input, zend_long num_req, zval *retval, bool silent);
#define PHP_EXTR_OVERWRITE 0
#define PHP_EXTR_SKIP 1

View File

@@ -62,7 +62,7 @@ PHPAPI int string_natural_compare_function_ex(zval *result, zval *op1, zval *op2
PHPAPI int string_natural_compare_function(zval *result, zval *op1, zval *op2);
PHPAPI int string_natural_case_compare_function(zval *result, zval *op1, zval *op2);
PHPAPI bool php_binary_string_shuffle(const php_random_algo *algo, php_random_status *status, char *str, zend_long len);
PHPAPI bool php_binary_string_shuffle(php_random_algo_with_state engine, char *str, zend_long len);
#ifdef _REENTRANT
# ifdef PHP_WIN32

View File

@@ -5953,8 +5953,11 @@ PHP_FUNCTION(str_rot13)
/* }}} */
/* {{{ php_binary_string_shuffle */
PHPAPI bool php_binary_string_shuffle(const php_random_algo *algo, php_random_status *status, char *str, zend_long len) /* {{{ */
PHPAPI bool php_binary_string_shuffle(php_random_algo_with_state engine, char *str, zend_long len) /* {{{ */
{
const php_random_algo *algo = engine.algo;
void *status = engine.status;
int64_t n_elems, rnd_idx, n_left;
char temp;
@@ -5996,8 +5999,7 @@ PHP_FUNCTION(str_shuffle)
RETVAL_STRINGL(ZSTR_VAL(arg), ZSTR_LEN(arg));
if (Z_STRLEN_P(return_value) > 1) {
php_binary_string_shuffle(
php_random_default_algo(),
php_random_default_status(),
php_random_default_engine(),
Z_STRVAL_P(return_value),
Z_STRLEN_P(return_value)
);