Line data Source code
1 : /*
2 : +----------------------------------------------------------------------+
3 : | COLOPL PHP Backwards Compatibility Extension. |
4 : +----------------------------------------------------------------------+
5 : | Copyright (c) COLOPL, Inc. |
6 : | Copyright (c) The PHP Group |
7 : +----------------------------------------------------------------------+
8 : | This source file is subject to version 3.01 of the PHP license, |
9 : | that is bundled with this package in the file LICENSE, and is |
10 : | available through the world-wide-web at the following url: |
11 : | http://www.php.net/license/3_01.txt |
12 : | If you did not receive a copy of the PHP license and are unable to |
13 : | obtain it through the world-wide-web, please send a note to |
14 : | license@php.net so we can mail you a copy immediately. |
15 : +----------------------------------------------------------------------+
16 : | Author: Go Kudo <g-kudo@colopl.co.jp> |
17 : +----------------------------------------------------------------------+
18 : */
19 :
20 : #ifdef HAVE_CONFIG_H
21 : # include "config.h"
22 : #endif
23 :
24 : #include "php_colopl_bc.h"
25 :
26 : #include "ext/date/php_date.h"
27 : #include "zend_bitset.h"
28 :
29 : /* rand.c */
30 :
31 : #if defined(__clang__)
32 : __attribute__((no_sanitize("signed-integer-overflow")))
33 : #endif
34 52 : void php_colopl_bc_srand(zend_long seed)
35 : {
36 : #ifdef ZTS
37 : COLOPL_BC_G(rand_seed) = (uint32_t) seed;
38 : #else
39 52 : uint32_t s = (uint32_t) seed;
40 52 : int i = 0;
41 :
42 52 : if (s == 0) {
43 12 : s = 1;
44 : }
45 :
46 52 : COLOPL_BC_G(gnurandom_r)[0] = s;
47 1612 : for (i = 1; i < 31; i++) {
48 1560 : COLOPL_BC_G(gnurandom_r)[i] = (16807LL * COLOPL_BC_G(gnurandom_r)[i - 1]) % 2147483647;
49 1560 : if (COLOPL_BC_G(gnurandom_r)[i] < 0) {
50 4 : COLOPL_BC_G(gnurandom_r)[i] += 2147483647;
51 : }
52 : }
53 208 : for (i = 31; i < 34; i++) {
54 156 : COLOPL_BC_G(gnurandom_r)[i] = COLOPL_BC_G(gnurandom_r)[i - 31];
55 : }
56 16172 : for (i = 34; i < 344; i++) {
57 16120 : COLOPL_BC_G(gnurandom_r)[i] = COLOPL_BC_G(gnurandom_r)[i - 31] + COLOPL_BC_G(gnurandom_r)[i - 3];
58 : }
59 :
60 52 : COLOPL_BC_G(gnurandom_next) = 0;
61 : #endif
62 52 : COLOPL_BC_G(rand_is_seeded) = 1;
63 52 : }
64 :
65 : #if defined(__clang__)
66 : __attribute__((no_sanitize("signed-integer-overflow")))
67 : #endif
68 3206276 : zend_long php_colopl_bc_rand(void)
69 : {
70 : zend_long ret;
71 :
72 3206276 : if (!COLOPL_BC_G(rand_is_seeded)) {
73 0 : php_colopl_bc_srand(COLOPL_BC_GENERATE_SEED());
74 : }
75 :
76 : #ifdef ZTS
77 : uint32_t next = COLOPL_BC_G(rand_seed);
78 :
79 : next = (next * 1103515245) + 12345;
80 : ret = (uint32_t) (next / 65536) % 2048;
81 :
82 : next = (next * 1103515245) + 12345;
83 : ret <<= 10;
84 : ret ^= (uint32_t) (next / 65536) % 1024;
85 :
86 : next = (next * 1103515245) + 12345;
87 : ret <<= 10;
88 : ret ^= (uint32_t) (next / 65536) % 1024;
89 :
90 : COLOPL_BC_G(rand_seed) = next;
91 : #else
92 3206276 : COLOPL_BC_G(gnurandom_r)[COLOPL_BC_G(gnurandom_next) % 344] = COLOPL_BC_G(gnurandom_r)[(COLOPL_BC_G(gnurandom_next) + 313) % 344] + COLOPL_BC_G(gnurandom_r)[(COLOPL_BC_G(gnurandom_next) + 341) % 344];
93 3206276 : ret = ((uint32_t) COLOPL_BC_G(gnurandom_r)[COLOPL_BC_G(gnurandom_next) % 344] >> 1);
94 3206276 : COLOPL_BC_G(gnurandom_next) = (COLOPL_BC_G(gnurandom_next) + 1) % 344;
95 : #endif
96 3206276 : return (zend_long) ret;
97 : }
98 :
99 52 : PHP_FUNCTION(Colopl_ColoplBc_Php70_srand)
100 : {
101 52 : zend_long seed = 0;
102 :
103 52 : ZEND_PARSE_PARAMETERS_START(0, 1)
104 52 : Z_PARAM_OPTIONAL
105 52 : Z_PARAM_LONG(seed)
106 52 : ZEND_PARSE_PARAMETERS_END();
107 :
108 52 : if (ZEND_NUM_ARGS() == 0) {
109 0 : seed = COLOPL_BC_GENERATE_SEED();
110 : }
111 :
112 52 : php_colopl_bc_srand(seed);
113 : }
114 :
115 3204088 : PHP_FUNCTION(Colopl_ColoplBc_Php70_rand)
116 : {
117 : zend_long min, max, number;
118 : int argc;
119 :
120 3204088 : number = php_colopl_bc_rand();
121 3204088 : argc = ZEND_NUM_ARGS();
122 :
123 3204088 : if (argc == 0) {
124 0 : RETURN_LONG(number);
125 : }
126 :
127 3204088 : ZEND_PARSE_PARAMETERS_START(2, 2)
128 3204088 : Z_PARAM_LONG(min)
129 3204088 : Z_PARAM_LONG(max)
130 3204088 : ZEND_PARSE_PARAMETERS_END();
131 :
132 3204088 : PHP_COLOPL_BC_RAND_RANGE(number, min, max, PHP_COLOPL_BC_RAND_MAX);
133 3204088 : RETURN_LONG(number);
134 : }
135 :
136 0 : PHP_FUNCTION(Colopl_ColoplBc_Php70_getrandmax)
137 : {
138 0 : ZEND_PARSE_PARAMETERS_NONE();
139 :
140 0 : RETURN_LONG(PHP_COLOPL_BC_RAND_MAX);
141 : }
142 :
143 : /* mt_rand.c */
144 :
145 40 : static inline void mt_initialize(uint32_t seed, uint32_t *state)
146 : {
147 40 : register uint32_t *s = state;
148 40 : register uint32_t *r = state;
149 40 : register int i = 1;
150 :
151 40 : *s++ = seed & 0xffffffffU;
152 24960 : for (; i < N; ++i) {
153 24920 : *s++ = ( 1812433253U * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffU;
154 24920 : r++;
155 : }
156 40 : }
157 :
158 5160 : static inline void mt_reload(void)
159 : {
160 5160 : register uint32_t *state = COLOPL_BC_G(mt_state);
161 5160 : register uint32_t *p = state;
162 : register int i;
163 :
164 1176480 : for (i = N - M; i--; ++p) {
165 1171320 : *p = twist(p[M], p[0], p[1]);
166 : }
167 2048520 : for (i = M; --i; ++p) {
168 2043360 : *p = twist(p[M-N], p[0], p[1]);
169 : }
170 5160 : *p = twist(p[M-N], p[0], state[0]);
171 5160 : COLOPL_BC_G(mt_left) = N;
172 5160 : COLOPL_BC_G(mt_next) = state;
173 5160 : }
174 :
175 40 : void php_colopl_bc_mt_srand(uint32_t seed)
176 : {
177 40 : mt_initialize(seed, COLOPL_BC_G(mt_state));
178 40 : mt_reload();
179 :
180 40 : COLOPL_BC_G(mt_rand_is_seeded) = 1;
181 40 : }
182 :
183 3204088 : uint32_t php_colopl_bc_mt_rand(void)
184 : {
185 : register uint32_t s1;
186 :
187 3204088 : if (COLOPL_BC_G(mt_left) == 0) {
188 5120 : mt_reload();
189 : }
190 3204088 : --COLOPL_BC_G(mt_left);
191 :
192 3204088 : s1 = *COLOPL_BC_G(mt_next)++;
193 3204088 : s1 ^= (s1 >> 11);
194 3204088 : s1 ^= (s1 << 7) & 0x9d2c5680U;
195 3204088 : s1 ^= (s1 << 15) & 0xefc60000U;
196 3204088 : return ( s1 ^ (s1 >> 18) );
197 : }
198 :
199 40 : PHP_FUNCTION(Colopl_ColoplBc_Php70_mt_srand)
200 : {
201 40 : zend_long seed = 0;
202 :
203 40 : ZEND_PARSE_PARAMETERS_START(0, 1)
204 40 : Z_PARAM_OPTIONAL
205 40 : Z_PARAM_LONG(seed)
206 40 : ZEND_PARSE_PARAMETERS_END();
207 :
208 40 : if (ZEND_NUM_ARGS() == 0) {
209 0 : seed = COLOPL_BC_GENERATE_SEED();
210 : }
211 :
212 40 : php_colopl_bc_mt_srand((uint32_t) seed);
213 : }
214 :
215 3204088 : PHP_FUNCTION(Colopl_ColoplBc_Php70_mt_rand)
216 : {
217 : zend_long min, max, number;
218 3204088 : int argc = ZEND_NUM_ARGS();
219 :
220 3204088 : if (argc == 0) {
221 0 : if (!COLOPL_BC_G(mt_rand_is_seeded)) {
222 0 : php_colopl_bc_mt_srand(COLOPL_BC_GENERATE_SEED());
223 : }
224 0 : RETURN_LONG(php_colopl_bc_mt_rand() >> 1);
225 : }
226 :
227 3204088 : ZEND_PARSE_PARAMETERS_START(2, 2)
228 3204088 : Z_PARAM_LONG(min)
229 3204088 : Z_PARAM_LONG(max)
230 3204088 : ZEND_PARSE_PARAMETERS_END();
231 :
232 3204088 : if (UNEXPECTED(max < min)) {
233 0 : zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)");
234 0 : RETURN_THROWS();
235 : }
236 :
237 3204088 : if (!COLOPL_BC_G(mt_rand_is_seeded)) {
238 0 : php_colopl_bc_mt_srand(COLOPL_BC_GENERATE_SEED());
239 : }
240 :
241 3204088 : number = (zend_long) (php_colopl_bc_mt_rand() >> 1);
242 3204088 : PHP_COLOPL_BC_RAND_RANGE(number, min, max, PHP_COLOPL_BC_MT_RAND_MAX);
243 3204088 : RETURN_LONG(number);
244 : }
245 :
246 0 : PHP_FUNCTION(Colopl_ColoplBc_Php70_mt_getrandmax)
247 : {
248 0 : ZEND_PARSE_PARAMETERS_NONE();
249 :
250 0 : RETURN_LONG(PHP_COLOPL_BC_MT_RAND_MAX);
251 : }
252 :
253 : /* array.c */
254 :
255 : #if PHP_VERSION_ID < 80200
256 : void php_colopl_bc_array_data_shuffle(zval *array)
257 : {
258 : uint32_t idx, j, n_elems;
259 : Bucket *p, temp;
260 : HashTable *hash;
261 : zend_long rnd_idx;
262 : uint32_t n_left;
263 :
264 : n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
265 :
266 : if (n_elems < 1) {
267 : return;
268 : }
269 :
270 : hash = Z_ARRVAL_P(array);
271 : n_left = n_elems;
272 :
273 : if (EXPECTED(hash->u.v.nIteratorsCount == 0)) {
274 : if (hash->nNumUsed != hash->nNumOfElements) {
275 : for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
276 : p = hash->arData + idx;
277 : if (Z_TYPE(p->val) == IS_UNDEF) continue;
278 : if (j != idx) {
279 : hash->arData[j] = *p;
280 : }
281 : j++;
282 : }
283 : }
284 : while (--n_left) {
285 : rnd_idx = php_colopl_bc_rand();
286 : PHP_COLOPL_BC_RAND_RANGE(rnd_idx, 0, n_left, PHP_COLOPL_BC_RAND_MAX);
287 : if (rnd_idx != n_left) {
288 : temp = hash->arData[n_left];
289 : hash->arData[n_left] = hash->arData[rnd_idx];
290 : hash->arData[rnd_idx] = temp;
291 : }
292 : }
293 : } else {
294 : uint32_t iter_pos = zend_hash_iterators_lower_pos(hash, 0);
295 :
296 : if (hash->nNumUsed != hash->nNumOfElements) {
297 : for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
298 : p = hash->arData + idx;
299 : if (Z_TYPE(p->val) == IS_UNDEF) continue;
300 : if (j != idx) {
301 : hash->arData[j] = *p;
302 : if (idx == iter_pos) {
303 : zend_hash_iterators_update(hash, idx, j);
304 : iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
305 : }
306 : }
307 : j++;
308 : }
309 : }
310 : while (--n_left) {
311 : rnd_idx = php_colopl_bc_rand();
312 : PHP_COLOPL_BC_RAND_RANGE(rnd_idx, 0, n_left, PHP_COLOPL_BC_RAND_MAX);
313 : if (rnd_idx != n_left) {
314 : temp = hash->arData[n_left];
315 : hash->arData[n_left] = hash->arData[rnd_idx];
316 : hash->arData[rnd_idx] = temp;
317 : zend_hash_iterators_update(hash, (uint32_t)rnd_idx, n_left);
318 : }
319 : }
320 : }
321 : HANDLE_BLOCK_INTERRUPTIONS();
322 : hash->nNumUsed = n_elems;
323 : hash->nInternalPointer = 0;
324 :
325 : for (j = 0; j < n_elems; j++) {
326 : p = hash->arData + j;
327 : if (p->key) {
328 : zend_string_release(p->key);
329 : }
330 : p->h = j;
331 : p->key = NULL;
332 : }
333 : hash->nNextFreeElement = n_elems;
334 : if (!(hash->u.flags & HASH_FLAG_PACKED)) {
335 : zend_hash_to_packed(hash);
336 : }
337 : HANDLE_UNBLOCK_INTERRUPTIONS();
338 : }
339 : #else
340 4 : void php_colopl_bc_array_data_shuffle(zval *array)
341 : {
342 : int64_t idx, j, n_elems, rnd_idx, n_left;
343 : zval *zv, temp;
344 : HashTable *hash;
345 :
346 4 : n_elems = zend_hash_num_elements(Z_ARRVAL_P(array));
347 :
348 4 : if (n_elems < 1) {
349 0 : return;
350 : }
351 :
352 4 : hash = Z_ARRVAL_P(array);
353 4 : n_left = n_elems;
354 :
355 4 : if (!HT_IS_PACKED(hash)) {
356 0 : if (!HT_HAS_STATIC_KEYS_ONLY(hash)) {
357 0 : Bucket *p = hash->arData;
358 0 : zend_long i = hash->nNumUsed;
359 :
360 0 : for (; i > 0; p++, i--) {
361 0 : if (p->key) {
362 0 : zend_string_release(p->key);
363 0 : p->key = NULL;
364 : }
365 : }
366 : }
367 0 : zend_hash_to_packed(hash);
368 : }
369 :
370 4 : if (EXPECTED(!HT_HAS_ITERATORS(hash))) {
371 4 : if (hash->nNumUsed != hash->nNumOfElements) {
372 0 : for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
373 0 : zv = hash->arPacked + idx;
374 0 : if (Z_TYPE_P(zv) == IS_UNDEF) continue;
375 0 : if (j != idx) {
376 0 : ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
377 : }
378 0 : j++;
379 : }
380 : }
381 400 : while (--n_left) {
382 396 : rnd_idx = php_colopl_bc_rand();
383 396 : PHP_COLOPL_BC_RAND_RANGE(rnd_idx, 0, n_left, PHP_COLOPL_BC_RAND_MAX);
384 396 : if (rnd_idx != n_left) {
385 380 : ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
386 380 : ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
387 380 : ZVAL_COPY_VALUE(&hash->arPacked[rnd_idx], &temp);
388 : }
389 : }
390 : } else {
391 0 : zend_long iter_pos = zend_hash_iterators_lower_pos(hash, 0);
392 :
393 0 : if (hash->nNumUsed != hash->nNumOfElements) {
394 0 : for (j = 0, idx = 0; idx < hash->nNumUsed; idx++) {
395 0 : zv = hash->arPacked + idx;
396 0 : if (Z_TYPE_P(zv) == IS_UNDEF) continue;
397 0 : if (j != idx) {
398 0 : ZVAL_COPY_VALUE(&hash->arPacked[j], zv);
399 0 : if (idx == iter_pos) {
400 0 : zend_hash_iterators_update(hash, idx, j);
401 0 : iter_pos = zend_hash_iterators_lower_pos(hash, iter_pos + 1);
402 : }
403 : }
404 0 : j++;
405 : }
406 : }
407 0 : while (--n_left) {
408 0 : rnd_idx = php_colopl_bc_rand();
409 0 : PHP_COLOPL_BC_RAND_RANGE(rnd_idx, 0, n_left, PHP_COLOPL_BC_RAND_MAX);
410 0 : if (rnd_idx != n_left) {
411 0 : ZVAL_COPY_VALUE(&temp, &hash->arPacked[n_left]);
412 0 : ZVAL_COPY_VALUE(&hash->arPacked[n_left], &hash->arPacked[rnd_idx]);
413 0 : ZVAL_COPY_VALUE(&hash->arPacked[rnd_idx], &temp);
414 0 : zend_hash_iterators_update(hash, (uint32_t)rnd_idx, n_left);
415 : }
416 : }
417 : }
418 4 : hash->nNumUsed = n_elems;
419 4 : hash->nInternalPointer = 0;
420 4 : hash->nNextFreeElement = n_elems;
421 : }
422 : #endif
423 :
424 4 : PHP_FUNCTION(Colopl_ColoplBc_Php70_shuffle)
425 : {
426 : zval *array;
427 :
428 4 : ZEND_PARSE_PARAMETERS_START(1, 1)
429 4 : Z_PARAM_ARRAY_EX(array, 0, 1)
430 4 : ZEND_PARSE_PARAMETERS_END();
431 :
432 4 : php_colopl_bc_array_data_shuffle(array);
433 :
434 4 : RETURN_TRUE;
435 : }
436 :
437 4 : PHP_FUNCTION(Colopl_ColoplBc_Php70_array_rand)
438 : {
439 : zval *input;
440 4 : zend_long randval, num_req = 1;
441 : int num_avail;
442 : zend_string *string_key;
443 : zend_ulong num_key;
444 :
445 4 : ZEND_PARSE_PARAMETERS_START(1, 2)
446 4 : Z_PARAM_ARRAY(input)
447 4 : Z_PARAM_OPTIONAL
448 4 : Z_PARAM_LONG(num_req)
449 4 : ZEND_PARSE_PARAMETERS_END();
450 :
451 4 : num_avail = zend_hash_num_elements(Z_ARRVAL_P(input));
452 :
453 4 : if (ZEND_NUM_ARGS() > 1) {
454 4 : if (num_req <= 0 || num_req > num_avail) {
455 0 : zend_argument_value_error(2, "must be between 1 and the number of elements in argument #1 ($array)");
456 0 : RETURN_THROWS();
457 : }
458 : }
459 :
460 : /* Make the return value an array only if we need to pass back more than one result. */
461 4 : if (num_req > 1) {
462 4 : array_init_size(return_value, (uint32_t)num_req);
463 : }
464 :
465 : /* We can't use zend_hash_index_find() because the array may have string keys or gaps. */
466 20 : ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) {
467 20 : if (!num_req) {
468 4 : break;
469 : }
470 :
471 16 : randval = php_colopl_bc_rand();
472 :
473 16 : if ((double) (randval / (PHP_COLOPL_BC_RAND_MAX + 1.0)) < (double) num_req / (double) num_avail) {
474 : /* If we are returning a single result, just do it. */
475 12 : if (Z_TYPE_P(return_value) != IS_ARRAY) {
476 0 : if (string_key) {
477 0 : RETURN_STR_COPY(string_key);
478 : } else {
479 0 : RETURN_LONG(num_key);
480 : }
481 : } else {
482 : /* Append the result to the return value. */
483 12 : if (string_key) {
484 0 : add_next_index_str(return_value, zend_string_copy(string_key));
485 : } else {
486 12 : add_next_index_long(return_value, num_key);
487 : }
488 : }
489 12 : num_req--;
490 : }
491 16 : num_avail--;
492 : } ZEND_HASH_FOREACH_END();
493 :
494 : }
495 :
496 : /* string.c */
497 4 : void php_colopl_bc_string_shuffle(char *str, zend_long len)
498 : {
499 : zend_long n_elems, rnd_idx, n_left;
500 : char temp;
501 :
502 4 : n_elems = len;
503 :
504 4 : if (n_elems <= 1) {
505 0 : return;
506 : }
507 :
508 4 : n_left = n_elems;
509 :
510 1780 : while (--n_left) {
511 1776 : rnd_idx = php_colopl_bc_rand();
512 1776 : PHP_COLOPL_BC_RAND_RANGE(rnd_idx, 0, n_left, PHP_COLOPL_BC_RAND_MAX);
513 1776 : if (rnd_idx != n_left) {
514 1756 : temp = str[n_left];
515 1756 : str[n_left] = str[rnd_idx];
516 1756 : str[rnd_idx] = temp;
517 : }
518 : }
519 : }
520 :
521 4 : PHP_FUNCTION(Colopl_ColoplBc_Php70_str_shuffle)
522 : {
523 : zend_string *arg;
524 :
525 4 : ZEND_PARSE_PARAMETERS_START(1, 1)
526 4 : Z_PARAM_STR(arg)
527 4 : ZEND_PARSE_PARAMETERS_END();
528 :
529 4 : RETVAL_STRINGL(ZSTR_VAL(arg), ZSTR_LEN(arg));
530 4 : if (Z_STRLEN_P(return_value) > 1) {
531 4 : php_colopl_bc_string_shuffle(Z_STRVAL_P(return_value), (zend_long) Z_STRLEN_P(return_value));
532 : }
533 : }
534 :
535 4004 : PHP_FUNCTION(Colopl_ColoplBc_Php70_date_create)
536 : {
537 : zend_function *fn;
538 : php_date_obj *date;
539 :
540 4004 : fn = zend_hash_str_find_ptr(CG(function_table), "date_create", strlen("date_create"));
541 4004 : ZEND_ASSERT(fn);
542 4004 : fn->internal_function.handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
543 :
544 4004 : if (Z_TYPE_P(return_value) != IS_FALSE) {
545 4000 : date = Z_PHPDATE_P(return_value);
546 4000 : date->time->us = 0;
547 : }
548 4004 : }
549 :
550 4004 : PHP_FUNCTION(Colopl_ColoplBc_Php70_date_create_immutable)
551 : {
552 : zend_function *fn;
553 : php_date_obj *date;
554 :
555 4004 : fn = zend_hash_str_find_ptr(CG(function_table), "date_create_immutable", strlen("date_create_immutable"));
556 4004 : ZEND_ASSERT(fn);
557 4004 : fn->internal_function.handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
558 :
559 4004 : if (Z_TYPE_P(return_value) != IS_FALSE) {
560 4000 : date = Z_PHPDATE_P(return_value);
561 4000 : date->time->us = 0;
562 : }
563 4004 : }
|