Skip to content

Commit 06569bb

Browse files
authored
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.
1 parent 650a8fb commit 06569bb

File tree

2 files changed

+19
-15
lines changed

2 files changed

+19
-15
lines changed

ext/random/engine_mt19937.c

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,11 @@
8686
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
8787
*/
8888

89-
#define N MT_N /* length of state vector */
89+
#define N 624 /* length of state vector */
90+
ZEND_STATIC_ASSERT(
91+
N == sizeof(((php_random_status_state_mt19937*)0)->state) / sizeof(((php_random_status_state_mt19937*)0)->state[0]),
92+
"Assumed length of Mt19937 state vector does not match actual size."
93+
);
9094
#define M (397) /* a period parameter */
9195
#define hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */
9296
#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
130134
In previous versions, most significant bits (MSBs) of the seed affect
131135
only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. */
132136
state->state[0] = seed;
133-
for (i = 1; i < MT_N; i++) {
137+
for (i = 1; i < N; i++) {
134138
prev_state = state->state[i - 1];
135139
state->state[i] = (1812433253U * (prev_state ^ (prev_state >> 30)) + i) & 0xffffffffU;
136140
}
@@ -144,7 +148,7 @@ static php_random_result generate(void *state)
144148
php_random_status_state_mt19937 *s = state;
145149
uint32_t s1;
146150

147-
if (s->count >= MT_N) {
151+
if (s->count >= N) {
148152
mt19937_reload(s);
149153
}
150154

@@ -172,7 +176,7 @@ static bool serialize(void *state, HashTable *data)
172176
php_random_status_state_mt19937 *s = state;
173177
zval t;
174178

175-
for (uint32_t i = 0; i < MT_N; i++) {
179+
for (uint32_t i = 0; i < N; i++) {
176180
ZVAL_STR(&t, php_random_bin2hex_le(&s->state[i], sizeof(uint32_t)));
177181
zend_hash_next_index_insert(data, &t);
178182
}
@@ -190,11 +194,11 @@ static bool unserialize(void *state, HashTable *data)
190194
zval *t;
191195

192196
/* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */
193-
if (zend_hash_num_elements(data) != (MT_N + 2)) {
197+
if (zend_hash_num_elements(data) != (N + 2)) {
194198
return false;
195199
}
196200

197-
for (uint32_t i = 0; i < MT_N; i++) {
201+
for (uint32_t i = 0; i < N; i++) {
198202
t = zend_hash_index_find(data, i);
199203
if (!t || Z_TYPE_P(t) != IS_STRING || Z_STRLEN_P(t) != (2 * sizeof(uint32_t))) {
200204
return false;
@@ -203,16 +207,16 @@ static bool unserialize(void *state, HashTable *data)
203207
return false;
204208
}
205209
}
206-
t = zend_hash_index_find(data, MT_N);
210+
t = zend_hash_index_find(data, N);
207211
if (!t || Z_TYPE_P(t) != IS_LONG) {
208212
return false;
209213
}
210214
s->count = Z_LVAL_P(t);
211-
if (s->count > MT_N) {
215+
if (s->count > N) {
212216
return false;
213217
}
214218

215-
t = zend_hash_index_find(data, MT_N + 1);
219+
t = zend_hash_index_find(data, N + 1);
216220
if (!t || Z_TYPE_P(t) != IS_LONG) {
217221
return false;
218222
}

ext/random/php_random.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ PHPAPI double php_combined_lcg(void);
4545

4646
# define PHP_MT_RAND_MAX ((zend_long) (0x7FFFFFFF)) /* (1<<31) - 1 */
4747

48-
# define MT_RAND_MT19937 0
49-
# define MT_RAND_PHP 1
50-
51-
# define MT_N (624)
48+
enum php_random_mt19937_mode {
49+
MT_RAND_MT19937 = 0,
50+
MT_RAND_PHP = 1,
51+
};
5252

5353
#define PHP_RANDOM_RANGE_ATTEMPTS (50)
5454

@@ -71,9 +71,9 @@ typedef struct _php_random_status_state_combinedlcg {
7171
} php_random_status_state_combinedlcg;
7272

7373
typedef struct _php_random_status_state_mt19937 {
74-
uint32_t state[MT_N];
7574
uint32_t count;
76-
uint8_t mode;
75+
enum php_random_mt19937_mode mode;
76+
uint32_t state[624];
7777
} php_random_status_state_mt19937;
7878

7979
typedef struct _php_random_status_state_pcgoneseq128xslrr64 {

0 commit comments

Comments
 (0)