Coverage Report

Created: 2025-06-13 06:43

/src/php-src/ext/random/engine_pcgoneseq128xslrr64.c
Line
Count
Source (jump to first uncovered line)
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
   | Author: Go Kudo <zeriyoshi@php.net>                                  |
14
   |                                                                      |
15
   | Based on code from: Melissa O'Neill <oneill@pcg-random.org>          |
16
   +----------------------------------------------------------------------+
17
*/
18
19
#ifdef HAVE_CONFIG_H
20
# include "config.h"
21
#endif
22
23
#include "php.h"
24
#include "php_random.h"
25
#include "php_random_csprng.h"
26
#include "php_random_uint128.h"
27
28
#include "Zend/zend_exceptions.h"
29
30
static inline void step(php_random_status_state_pcgoneseq128xslrr64 *s)
31
10
{
32
10
  s->state = php_random_uint128_add(
33
10
    php_random_uint128_multiply(s->state, php_random_uint128_constant(2549297995355413924ULL,4865540595714422341ULL)),
34
10
    php_random_uint128_constant(6364136223846793005ULL,1442695040888963407ULL)
35
10
  );
36
10
}
37
38
PHPAPI inline void php_random_pcgoneseq128xslrr64_seed128(php_random_status_state_pcgoneseq128xslrr64 *s, php_random_uint128_t seed)
39
5
{
40
5
  s->state = php_random_uint128_constant(0ULL, 0ULL);
41
5
  step(s);
42
5
  s->state = php_random_uint128_add(s->state, seed);
43
5
  step(s);
44
5
}
45
46
static php_random_result generate(void *state)
47
0
{
48
0
  php_random_status_state_pcgoneseq128xslrr64 *s = state;
49
50
0
  step(s);
51
52
0
  return (php_random_result){
53
0
    .size = sizeof(uint64_t),
54
0
    .result = php_random_pcgoneseq128xslrr64_rotr64(s->state),
55
0
  };
56
0
}
57
58
static zend_long range(void *state, zend_long min, zend_long max)
59
0
{
60
0
  return php_random_range((php_random_algo_with_state){
61
0
    .algo = &php_random_algo_pcgoneseq128xslrr64,
62
0
    .state = state,
63
0
  }, min, max);
64
0
}
65
66
static bool serialize(void *state, HashTable *data)
67
0
{
68
0
  php_random_status_state_pcgoneseq128xslrr64 *s = state;
69
0
  uint64_t u;
70
0
  zval z;
71
72
0
  u = php_random_uint128_hi(s->state);
73
0
  ZVAL_STR(&z, php_random_bin2hex_le(&u, sizeof(uint64_t)));
74
0
  zend_hash_next_index_insert(data, &z);
75
76
0
  u = php_random_uint128_lo(s->state);
77
0
  ZVAL_STR(&z, php_random_bin2hex_le(&u, sizeof(uint64_t)));
78
0
  zend_hash_next_index_insert(data, &z);
79
80
0
  return true;
81
0
}
82
83
static bool unserialize(void *state, HashTable *data)
84
0
{
85
0
  php_random_status_state_pcgoneseq128xslrr64 *s = state;
86
0
  uint64_t u[2];
87
0
  zval *t;
88
89
  /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */
90
0
  if (zend_hash_num_elements(data) != 2) {
91
0
    return false;
92
0
  }
93
94
0
  for (uint32_t i = 0; i < 2; i++) {
95
0
    t = zend_hash_index_find(data, i);
96
0
    if (!t || Z_TYPE_P(t) != IS_STRING || Z_STRLEN_P(t) != (2 * sizeof(uint64_t))) {
97
0
      return false;
98
0
    }
99
0
    if (!php_random_hex2bin_le(Z_STR_P(t), &u[i])) {
100
0
      return false;
101
0
    }
102
0
  }
103
0
  s->state = php_random_uint128_constant(u[0], u[1]);
104
105
0
  return true;
106
0
}
107
108
PHPAPI const php_random_algo php_random_algo_pcgoneseq128xslrr64 = {
109
  sizeof(php_random_status_state_pcgoneseq128xslrr64),
110
  generate,
111
  range,
112
  serialize,
113
  unserialize
114
};
115
116
/* {{{ php_random_pcgoneseq128xslrr64_advance */
117
PHPAPI void php_random_pcgoneseq128xslrr64_advance(php_random_status_state_pcgoneseq128xslrr64 *state, uint64_t advance)
118
0
{
119
0
  php_random_uint128_t
120
0
    cur_mult = php_random_uint128_constant(2549297995355413924ULL,4865540595714422341ULL),
121
0
    cur_plus = php_random_uint128_constant(6364136223846793005ULL,1442695040888963407ULL),
122
0
    acc_mult = php_random_uint128_constant(0ULL, 1ULL),
123
0
    acc_plus = php_random_uint128_constant(0ULL, 0ULL);
124
125
0
  while (advance > 0) {
126
0
    if (advance & 1) {
127
0
      acc_mult = php_random_uint128_multiply(acc_mult, cur_mult);
128
0
      acc_plus = php_random_uint128_add(php_random_uint128_multiply(acc_plus, cur_mult), cur_plus);
129
0
    }
130
0
    cur_plus = php_random_uint128_multiply(php_random_uint128_add(cur_mult, php_random_uint128_constant(0ULL, 1ULL)), cur_plus);
131
0
    cur_mult = php_random_uint128_multiply(cur_mult, cur_mult);
132
0
    advance /= 2;
133
0
  }
134
135
0
  state->state = php_random_uint128_add(php_random_uint128_multiply(acc_mult, state->state), acc_plus);
136
0
}
137
/* }}} */
138
139
/* {{{ Random\Engine\PcgOneseq128XslRr64::__construct */
140
PHP_METHOD(Random_Engine_PcgOneseq128XslRr64, __construct)
141
5
{
142
5
  php_random_algo_with_state engine = Z_RANDOM_ENGINE_P(ZEND_THIS)->engine;
143
5
  php_random_status_state_pcgoneseq128xslrr64 *state = engine.state;
144
5
  zend_string *str_seed = NULL;
145
5
  zend_long int_seed = 0;
146
5
  bool seed_is_null = true;
147
148
15
  ZEND_PARSE_PARAMETERS_START(0, 1)
149
15
    Z_PARAM_OPTIONAL;
150
15
    Z_PARAM_STR_OR_LONG_OR_NULL(str_seed, int_seed, seed_is_null);
151
10
  ZEND_PARSE_PARAMETERS_END();
152
153
5
  if (seed_is_null) {
154
5
    php_random_uint128_t s;
155
156
5
    if (php_random_bytes_throw(&s, sizeof(s)) == FAILURE) {
157
0
      zend_throw_exception(random_ce_Random_RandomException, "Failed to generate a random seed", 0);
158
0
      RETURN_THROWS();
159
0
    }
160
161
5
    php_random_pcgoneseq128xslrr64_seed128(state, s);
162
5
  } else {
163
0
    if (str_seed) {
164
      /* char (byte: 8 bit) * 16 = 128 bits */
165
0
      if (ZSTR_LEN(str_seed) == 16) {
166
0
        uint64_t t[2];
167
168
        /* Endianness safe copy */
169
0
        for (uint32_t i = 0; i < 2; i++) {
170
0
          t[i] = 0;
171
0
          for (uint32_t j = 0; j < 8; j++) {
172
0
            t[i] += ((uint64_t) (unsigned char) ZSTR_VAL(str_seed)[(i * 8) + j]) << (j * 8);
173
0
          }
174
0
        }
175
176
0
        php_random_pcgoneseq128xslrr64_seed128(state, php_random_uint128_constant(t[0], t[1]));
177
0
      } else {
178
0
        zend_argument_value_error(1, "must be a 16 byte (128 bit) string");
179
0
        RETURN_THROWS();
180
0
      }
181
0
    } else {
182
0
      php_random_pcgoneseq128xslrr64_seed128(state, php_random_uint128_constant(0ULL, (uint64_t) int_seed));
183
0
    }
184
0
  }
185
5
}
186
/* }}} */
187
188
/* {{{ Random\Engine\PcgOneseq128XslRr64::jump() */
189
PHP_METHOD(Random_Engine_PcgOneseq128XslRr64, jump)
190
0
{
191
0
  php_random_algo_with_state engine = Z_RANDOM_ENGINE_P(ZEND_THIS)->engine;
192
0
  php_random_status_state_pcgoneseq128xslrr64 *state = engine.state;
193
0
  zend_long advance = 0;
194
195
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
196
0
    Z_PARAM_LONG(advance);
197
0
  ZEND_PARSE_PARAMETERS_END();
198
199
0
  if (UNEXPECTED(advance < 0)) {
200
0
    zend_argument_value_error(1, "must be greater than or equal to 0");
201
0
    RETURN_THROWS();
202
0
  }
203
204
0
  php_random_pcgoneseq128xslrr64_advance(state, advance);
205
0
}
206
/* }}} */