php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
engine_mt19937.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Rasmus Lerdorf <rasmus@php.net> |
14 | Zeev Suraski <zeev@php.net> |
15 | Pedro Melo <melo@ip.pt> |
16 | Sterling Hughes <sterling@php.net> |
17 | Go Kudo <zeriyoshi@php.net> |
18 | |
19 | Based on code from: Richard J. Wagner <rjwagner@writeme.com> |
20 | Makoto Matsumoto <matumoto@math.keio.ac.jp> |
21 | Takuji Nishimura |
22 | Shawn Cokus <Cokus@math.washington.edu> |
23 +----------------------------------------------------------------------+
24*/
25
26#ifdef HAVE_CONFIG_H
27# include "config.h"
28#endif
29
30#include "php.h"
31#include "php_random.h"
32#include "php_random_csprng.h"
33
35
36/*
37 The following mt19937 algorithms are based on a C++ class MTRand by
38 Richard J. Wagner. For more information see the web page at
39 http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/C-LANG/MersenneTwister.h
40
41 Mersenne Twister random number generator -- a C++ class MTRand
42 Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
43 Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com
44
45 The Mersenne Twister is an algorithm for generating random numbers. It
46 was designed with consideration of the flaws in various other generators.
47 The period, 2^19937-1, and the order of equidistribution, 623 dimensions,
48 are far greater. The generator is also fast; it avoids multiplication and
49 division, and it benefits from caches and pipelines. For more information
50 see the inventors' web page at http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
51
52 Reference
53 M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
54 Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
55 Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
56
57 Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
58 Copyright (C) 2000 - 2003, Richard J. Wagner
59 All rights reserved.
60
61 Redistribution and use in source and binary forms, with or without
62 modification, are permitted provided that the following conditions
63 are met:
64
65 1. Redistributions of source code must retain the above copyright
66 notice, this list of conditions and the following disclaimer.
67
68 2. Redistributions in binary form must reproduce the above copyright
69 notice, this list of conditions and the following disclaimer in the
70 documentation and/or other materials provided with the distribution.
71
72 3. The names of its contributors may not be used to endorse or promote
73 products derived from this software without specific prior written
74 permission.
75
76 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
77 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
78 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
79 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
80 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
81 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
82 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
83 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
84 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
85 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
86 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
87*/
88
89#define N 624 /* length of state vector */
92 "Assumed length of Mt19937 state vector does not match actual size."
93);
94#define M (397) /* a period parameter */
95#define hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */
96#define loBit(u) ((u) & 0x00000001U) /* mask all but lowest bit of u */
97#define loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */
98#define mixBits(u, v) (hiBit(u) | loBits(v)) /* move hi bit of u to hi bit of v */
99
100#define twist(m,u,v) (m ^ (mixBits(u,v) >> 1) ^ ((uint32_t)(-(int32_t)(loBit(v))) & 0x9908b0dfU))
101#define twist_php(m,u,v) (m ^ (mixBits(u,v) >> 1) ^ ((uint32_t)(-(int32_t)(loBit(u))) & 0x9908b0dfU))
102
103static inline void mt19937_reload(php_random_status_state_mt19937 *state)
104{
105 uint32_t *p = state->state;
106
107 if (state->mode == MT_RAND_MT19937) {
108 for (uint32_t i = N - M; i--; ++p) {
109 *p = twist(p[M], p[0], p[1]);
110 }
111 for (uint32_t i = M; --i; ++p) {
112 *p = twist(p[M-N], p[0], p[1]);
113 }
114 *p = twist(p[M-N], p[0], state->state[0]);
115 } else {
116 for (uint32_t i = N - M; i--; ++p) {
117 *p = twist_php(p[M], p[0], p[1]);
118 }
119 for (uint32_t i = M; --i; ++p) {
120 *p = twist_php(p[M-N], p[0], p[1]);
121 }
122 *p = twist_php(p[M-N], p[0], state->state[0]);
123 }
124
125 state->count = 0;
126}
127
129{
130 uint32_t i, prev_state;
131
132 /* Initialize generator state with seed
133 See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
134 In previous versions, most significant bits (MSBs) of the seed affect
135 only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. */
136 state->state[0] = seed;
137 for (i = 1; i < N; i++) {
138 prev_state = state->state[i - 1];
139 state->state[i] = (1812433253U * (prev_state ^ (prev_state >> 30)) + i) & 0xffffffffU;
140 }
141 state->count = i;
142
143 mt19937_reload(state);
144}
145
146static php_random_result generate(void *state)
147{
149 uint32_t s1;
150
151 if (s->count >= N) {
152 mt19937_reload(s);
153 }
154
155 s1 = s->state[s->count++];
156 s1 ^= (s1 >> 11);
157 s1 ^= (s1 << 7) & 0x9d2c5680U;
158 s1 ^= (s1 << 15) & 0xefc60000U;
159
160 return (php_random_result){
161 .size = sizeof(uint32_t),
162 .result = (uint64_t) (s1 ^ (s1 >> 18)),
163 };
164}
165
166static zend_long range(void *state, zend_long min, zend_long max)
167{
170 .state = state,
171 }, min, max);
172}
173
174static bool serialize(void *state, HashTable *data)
175{
177 zval t;
178
179 for (uint32_t i = 0; i < N; i++) {
180 ZVAL_STR(&t, php_random_bin2hex_le(&s->state[i], sizeof(uint32_t)));
182 }
183 ZVAL_LONG(&t, s->count);
185 ZVAL_LONG(&t, s->mode);
187
188 return true;
189}
190
191static bool unserialize(void *state, HashTable *data)
192{
194 zval *t;
195
196 /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */
197 if (zend_hash_num_elements(data) != (N + 2)) {
198 return false;
199 }
200
201 for (uint32_t i = 0; i < N; i++) {
203 if (!t || Z_TYPE_P(t) != IS_STRING || Z_STRLEN_P(t) != (2 * sizeof(uint32_t))) {
204 return false;
205 }
206 if (!php_random_hex2bin_le(Z_STR_P(t), &s->state[i])) {
207 return false;
208 }
209 }
211 if (!t || Z_TYPE_P(t) != IS_LONG) {
212 return false;
213 }
214 s->count = Z_LVAL_P(t);
215 if (s->count > N) {
216 return false;
217 }
218
219 t = zend_hash_index_find(data, N + 1);
220 if (!t || Z_TYPE_P(t) != IS_LONG) {
221 return false;
222 }
223 s->mode = Z_LVAL_P(t);
224 if (s->mode != MT_RAND_MT19937 && s->mode != MT_RAND_PHP) {
225 return false;
226 }
227
228 return true;
229}
230
233 generate,
234 range,
235 serialize,
236 unserialize
237};
238
239/* {{{ php_random_mt19937_seed_default */
241{
242 uint32_t seed = 0;
243
244 if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
245 seed = (uint32_t)php_random_generate_fallback_seed();
246 }
247
249}
250/* }}} */
251
252/* {{{ Random\Engine\Mt19937::__construct() */
253PHP_METHOD(Random_Engine_Mt19937, __construct)
254{
258 bool seed_is_null = true;
259
262 Z_PARAM_LONG_OR_NULL(seed, seed_is_null);
265
266 switch (mode) {
267 case MT_RAND_MT19937:
268 state->mode = MT_RAND_MT19937;
269 break;
270 case MT_RAND_PHP:
271 zend_error(E_DEPRECATED, "The MT_RAND_PHP variant of Mt19937 is deprecated");
272 state->mode = MT_RAND_PHP;
273 break;
274 default:
275 zend_argument_value_error(2, "must be either MT_RAND_MT19937 or MT_RAND_PHP");
277 }
278
279 if (seed_is_null) {
280 /* MT19937 has a very large state, uses CSPRNG for seeding only */
281 if (php_random_bytes_throw(&seed, sizeof(seed)) == FAILURE) {
282 zend_throw_exception(random_ce_Random_RandomException, "Failed to generate a random seed", 0);
284 }
285 }
286
288}
289/* }}} */
290
291/* {{{ Random\Engine\Mt19937::generate() */
292PHP_METHOD(Random_Engine_Mt19937, generate)
293{
295 zend_string *bytes;
296
298
299 php_random_result generated = engine.algo->generate(engine.state);
300 if (EG(exception)) {
302 }
303
304 bytes = zend_string_alloc(generated.size, false);
305
306 /* Endianness safe copy */
307 for (size_t i = 0; i < generated.size; i++) {
308 ZSTR_VAL(bytes)[i] = (generated.result >> (i * 8)) & 0xff;
309 }
310 ZSTR_VAL(bytes)[generated.size] = '\0';
311
312 RETURN_STR(bytes);
313}
314/* }}} */
315
316/* {{{ Random\Engine\Mt19937::__serialize() */
317PHP_METHOD(Random_Engine_Mt19937, __serialize)
318{
320 zval t;
321
323
325
326 /* members */
327 ZVAL_ARR(&t, zend_std_get_properties(&engine->std));
328 Z_TRY_ADDREF(t);
330
331 /* state */
332 array_init(&t);
333 if (!engine->engine.algo->serialize(engine->engine.state, Z_ARRVAL(t))) {
334 zend_throw_exception(NULL, "Engine serialize failed", 0);
336 }
338}
339/* }}} */
340
341/* {{{ Random\Engine\Mt19937::__unserialize() */
342PHP_METHOD(Random_Engine_Mt19937, __unserialize)
343{
345 HashTable *d;
346 zval *t;
347
351
352 /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */
353 if (zend_hash_num_elements(d) != 2) {
354 zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name));
356 }
357
358 /* members */
359 t = zend_hash_index_find(d, 0);
360 if (!t || Z_TYPE_P(t) != IS_ARRAY) {
361 zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name));
363 }
364 object_properties_load(&engine->std, Z_ARRVAL_P(t));
365 if (EG(exception)) {
366 zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name));
368 }
369
370 /* state */
371 t = zend_hash_index_find(d, 1);
372 if (!t || Z_TYPE_P(t) != IS_ARRAY) {
373 zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name));
375 }
376 if (!engine->engine.algo->unserialize(engine->engine.state, Z_ARRVAL_P(t))) {
377 zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name));
379 }
380}
381/* }}} */
382
383/* {{{ Random\Engine\Mt19937::__debugInfo() */
384PHP_METHOD(Random_Engine_Mt19937, __debugInfo)
385{
387 zval t;
388
390
391 ZVAL_ARR(return_value, zend_array_dup(zend_std_get_properties_ex(&engine->std)));
392
393 if (engine->engine.algo->serialize) {
394 array_init(&t);
395 if (!engine->engine.algo->serialize(engine->engine.state, Z_ARRVAL(t))) {
396 zend_throw_exception(NULL, "Engine serialize failed", 0);
398 }
399 zend_hash_str_add(Z_ARR_P(return_value), "__states", strlen("__states"), &t);
400 }
401}
402/* }}} */
bool exception
Definition assert.c:30
char s[4]
Definition cdf.c:77
#define N
#define twist_php(m, u, v)
#define twist(m, u, v)
#define M
PHPAPI void php_random_mt19937_seed32(php_random_status_state_mt19937 *state, uint32_t seed)
PHPAPI const php_random_algo php_random_algo_mt19937
PHPAPI void php_random_mt19937_seed_default(php_random_status_state_mt19937 *state)
#define max(a, b)
Definition exif.c:60
char * mode
#define NULL
Definition gdcache.h:45
#define PHP_METHOD
Definition php.h:365
#define PHPAPI
Definition php.h:71
#define min(a, b)
PHPAPI zend_string * php_random_bin2hex_le(const void *ptr, const size_t len)
Definition random.c:332
PHPAPI bool php_random_hex2bin_le(zend_string *hexstr, void *dest)
Definition random.c:358
struct _php_random_algo_with_state php_random_algo_with_state
PHPAPI uint64_t php_random_generate_fallback_seed(void)
Definition random.c:716
PHPAPI zend_class_entry * random_ce_Random_RandomException
struct _php_random_status_state_mt19937 php_random_status_state_mt19937
#define Z_RANDOM_ENGINE_P(zval)
Definition php_random.h:147
struct _php_random_engine php_random_engine
PHPAPI zend_long php_random_range(php_random_algo_with_state engine, zend_long min, zend_long max)
Definition random.c:293
@ MT_RAND_PHP
Definition php_random.h:54
@ MT_RAND_MT19937
Definition php_random.h:53
struct _php_random_algo php_random_algo
struct _php_random_result php_random_result
#define zend_hash_str_add(...)
Definition phpdbg.h:77
zend_constant * data
p
Definition session.c:1105
ZEND_API ZEND_COLD void zend_error(int type, const char *format,...)
Definition zend.c:1666
ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:433
ZEND_API void object_properties_load(zend_object *object, HashTable *properties)
Definition zend_API.c:1728
#define ZEND_PARSE_PARAMETERS_END()
Definition zend_API.h:1641
#define ZEND_PARSE_PARAMETERS_NONE()
Definition zend_API.h:1623
#define Z_PARAM_OPTIONAL
Definition zend_API.h:1667
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
Definition zend_API.h:1620
#define Z_PARAM_LONG(dest)
Definition zend_API.h:1896
#define RETURN_THROWS()
Definition zend_API.h:1060
#define Z_PARAM_ARRAY_HT(dest)
Definition zend_API.h:1852
#define RETURN_STR(s)
Definition zend_API.h:1039
#define ZEND_THIS
Definition zend_API.h:523
#define Z_PARAM_LONG_OR_NULL(dest, is_null)
Definition zend_API.h:1899
#define array_init(arg)
Definition zend_API.h:537
struct _zval_struct zval
strlen(string $string)
#define E_DEPRECATED
Definition zend_errors.h:37
ZEND_API ZEND_COLD zend_object * zend_throw_exception(zend_class_entry *exception_ce, const char *message, zend_long code)
ZEND_API ZEND_COLD zend_object * zend_throw_exception_ex(zend_class_entry *exception_ce, zend_long code, const char *format,...)
#define EG(v)
ZEND_API zval *ZEND_FASTCALL zend_hash_next_index_insert(HashTable *ht, zval *pData)
Definition zend_hash.c:1224
ZEND_API HashTable *ZEND_FASTCALL zend_array_dup(HashTable *source)
Definition zend_hash.c:2438
ZEND_API zval *ZEND_FASTCALL zend_hash_index_find(const HashTable *ht, zend_ulong h)
Definition zend_hash.c:2701
int32_t zend_long
Definition zend_long.h:42
struct _zend_string zend_string
ZEND_API HashTable * zend_std_get_properties(zend_object *zobj)
#define ZEND_STATIC_ASSERT(c, m)
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define ZVAL_STR(z, s)
#define Z_ARRVAL_P(zval_p)
Definition zend_types.h:987
#define ZVAL_LONG(z, l)
#define IS_STRING
Definition zend_types.h:606
struct _zend_array HashTable
Definition zend_types.h:386
#define IS_ARRAY
Definition zend_types.h:607
#define Z_STR_P(zval_p)
Definition zend_types.h:972
#define Z_STRLEN_P(zval_p)
Definition zend_types.h:978
@ FAILURE
Definition zend_types.h:61
#define Z_TRY_ADDREF(z)
#define IS_LONG
Definition zend_types.h:604
#define ZVAL_ARR(z, a)
#define Z_ARRVAL(zval)
Definition zend_types.h:986
#define Z_ARR_P(zval_p)
Definition zend_types.h:984
#define Z_LVAL_P(zval_p)
Definition zend_types.h:966
zval * return_value
bool result