From 06569bbd0401436aa3dfabb380520d60dfabb33d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 4 Mar 2024 19:51:01 +0100 Subject: [PATCH] random: Clean Up the Mt19937 state struct (#13577) * random: Make Mt19937's `mode` field an enum * random: Reorder the `php_random_status_state_mt19937` struct Empirical testing did not show any differences in performance, but it makes sense to me to put the `count` field (which is accessed for every invocation of Mt19937) at the beginning of the struct, keeping it near the values from the state array that are returned first, resulting in only a single cache line load if only a small amount of numbers are requested. It naturally follows to also put the `mode` field there and move the humongous state array to the end. * random: Remove the `MT_N` constant `MT_N` is an awfully generic name that bleeds into every file including `php_random.h`. As it's an implementation detail, remove it entirely to keep `php_random.h` clean. To prevent the state struct from diverging from the implementation, the size of the state vector is statically verified. Furthermore there are phpt tests verifying the Mt19937 output across a reload, revealing when the state vector is reloaded too early or too late. --- ext/random/engine_mt19937.c | 22 +++++++++++++--------- ext/random/php_random.h | 12 ++++++------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/ext/random/engine_mt19937.c b/ext/random/engine_mt19937.c index 1034fdf39a4..7723b5414d9 100644 --- a/ext/random/engine_mt19937.c +++ b/ext/random/engine_mt19937.c @@ -86,7 +86,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define N MT_N /* length of state vector */ +#define N 624 /* length of state vector */ +ZEND_STATIC_ASSERT( + N == sizeof(((php_random_status_state_mt19937*)0)->state) / sizeof(((php_random_status_state_mt19937*)0)->state[0]), + "Assumed length of Mt19937 state vector does not match actual size." +); #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 */ @@ -130,7 +134,7 @@ PHPAPI inline void php_random_mt19937_seed32(php_random_status_state_mt19937 *st In previous versions, most significant bits (MSBs) of the seed affect only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. */ state->state[0] = seed; - for (i = 1; i < MT_N; i++) { + for (i = 1; i < N; i++) { prev_state = state->state[i - 1]; state->state[i] = (1812433253U * (prev_state ^ (prev_state >> 30)) + i) & 0xffffffffU; } @@ -144,7 +148,7 @@ static php_random_result generate(void *state) php_random_status_state_mt19937 *s = state; uint32_t s1; - if (s->count >= MT_N) { + if (s->count >= N) { mt19937_reload(s); } @@ -172,7 +176,7 @@ static bool serialize(void *state, HashTable *data) php_random_status_state_mt19937 *s = state; zval t; - for (uint32_t i = 0; i < MT_N; i++) { + for (uint32_t i = 0; i < N; i++) { ZVAL_STR(&t, php_random_bin2hex_le(&s->state[i], sizeof(uint32_t))); zend_hash_next_index_insert(data, &t); } @@ -190,11 +194,11 @@ static bool unserialize(void *state, HashTable *data) zval *t; /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */ - if (zend_hash_num_elements(data) != (MT_N + 2)) { + if (zend_hash_num_elements(data) != (N + 2)) { return false; } - for (uint32_t i = 0; i < MT_N; i++) { + for (uint32_t i = 0; i < N; 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; @@ -203,16 +207,16 @@ static bool unserialize(void *state, HashTable *data) return false; } } - t = zend_hash_index_find(data, MT_N); + t = zend_hash_index_find(data, N); if (!t || Z_TYPE_P(t) != IS_LONG) { return false; } s->count = Z_LVAL_P(t); - if (s->count > MT_N) { + if (s->count > N) { return false; } - t = zend_hash_index_find(data, MT_N + 1); + t = zend_hash_index_find(data, N + 1); if (!t || Z_TYPE_P(t) != IS_LONG) { return false; } diff --git a/ext/random/php_random.h b/ext/random/php_random.h index 811d75cbfc5..08c1eb70cab 100644 --- a/ext/random/php_random.h +++ b/ext/random/php_random.h @@ -45,10 +45,10 @@ PHPAPI double php_combined_lcg(void); # define PHP_MT_RAND_MAX ((zend_long) (0x7FFFFFFF)) /* (1<<31) - 1 */ -# define MT_RAND_MT19937 0 -# define MT_RAND_PHP 1 - -# define MT_N (624) +enum php_random_mt19937_mode { + MT_RAND_MT19937 = 0, + MT_RAND_PHP = 1, +}; #define PHP_RANDOM_RANGE_ATTEMPTS (50) @@ -71,9 +71,9 @@ typedef struct _php_random_status_state_combinedlcg { } php_random_status_state_combinedlcg; typedef struct _php_random_status_state_mt19937 { - uint32_t state[MT_N]; uint32_t count; - uint8_t mode; + enum php_random_mt19937_mode mode; + uint32_t state[624]; } php_random_status_state_mt19937; typedef struct _php_random_status_state_pcgoneseq128xslrr64 {