Skip to content

Commit ab61a1b

Browse files
committed
PCG64: algorithm fix and add support inc (sequence) parameter in constructor
1 parent 8976f80 commit ab61a1b

File tree

5 files changed

+68
-18
lines changed

5 files changed

+68
-18
lines changed

ext/random/random.c

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -467,15 +467,26 @@ static uint64_t pcg64_generate(void *state, bool *engine_unsafe) {
467467
return result;
468468
}
469469

470+
static void pcg64_seed_full(php_random_engine_state_pcg64 *state) {
471+
random_uint128_t initstate, initseq;
472+
473+
initstate = state->s;
474+
initseq = state->inc;
475+
476+
UINT128_CON(0ULL, 0ULL, state->s);
477+
PCG64_ROTL1OR1(initseq, state->inc);
478+
pcg64_step(state);
479+
UINT128_ADD(initstate, state->s, state->s);
480+
pcg64_step(state);
481+
}
482+
470483
static void pcg64_seed(void *state, const uint64_t seed) {
471484
php_random_engine_state_pcg64 *s = state;
472-
random_uint128_t sd;
473485

474-
UINT128_CON(0ULL, seed, sd);
475-
PCG64_ROTL1OR1(s->s, s->inc);
476-
pcg64_step(s);
477-
UINT128_ADD(sd, s->s, s->s);
478-
pcg64_step(s);
486+
UINT128_CON(0ULL, seed, s->s);
487+
UINT128_CON(0ULL, 0ULL, s->inc);
488+
489+
pcg64_seed_full(s);
479490
}
480491

481492
static int pcg64_serialize(void *state, HashTable *data) {
@@ -1413,17 +1424,37 @@ PHP_METHOD(Random_Engine_PCG64, __construct)
14131424
{
14141425
php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
14151426
php_random_engine_state_pcg64 *state = engine->state;
1416-
zend_string *str_seed = NULL;
1417-
zend_long int_seed = 0;
1427+
zend_string *str_seed = NULL, *str_seq = NULL;
1428+
zend_long int_seed = 0, int_seq = 0;
14181429
bool seed_is_null = true;
14191430
int i, j;
14201431
uint64_t t[2];
14211432

1422-
ZEND_PARSE_PARAMETERS_START(0, 1)
1433+
ZEND_PARSE_PARAMETERS_START(0, 2)
14231434
Z_PARAM_OPTIONAL;
14241435
Z_PARAM_STR_OR_LONG_OR_NULL(str_seed, int_seed, seed_is_null);
1436+
Z_PARAM_STR_OR_LONG(str_seq, int_seq);
14251437
ZEND_PARSE_PARAMETERS_END();
14261438

1439+
if (str_seq) {
1440+
/* char (8 bit) * 16 = 128 bits */
1441+
if (ZSTR_LEN(str_seq) == 16) {
1442+
/* Endianness safe copy */
1443+
for (i = 0; i < 2; i++) {
1444+
t[i] = 0;
1445+
for (j = 0; j < 8; j++) {
1446+
t[i] += ((uint64_t) (unsigned char) ZSTR_VAL(str_seq)[(i * 8) + j]) << (j * 8);
1447+
}
1448+
}
1449+
UINT128_CON(t[0], t[1], state->inc);
1450+
} else {
1451+
zend_argument_value_error(2, "sequence strings must be 16 bytes");
1452+
RETURN_THROWS();
1453+
}
1454+
} else {
1455+
UINT128_CON(0ULL, int_seq, state->inc);
1456+
}
1457+
14271458
if (seed_is_null) {
14281459
if (php_random_bytes_silent(&state->s, sizeof(random_uint128_t)) == FAILURE) {
14291460
zend_throw_exception(spl_ce_RuntimeException, "Random number generate failed", 0);
@@ -1441,14 +1472,15 @@ PHP_METHOD(Random_Engine_PCG64, __construct)
14411472
}
14421473
}
14431474
UINT128_CON(t[0], t[1], state->s);
1444-
UINT128_CON(0ULL, 0ULL, state->inc);
14451475
} else {
14461476
zend_argument_value_error(1, "state strings must be 16 bytes");
14471477
RETURN_THROWS();
14481478
}
14491479
} else {
1450-
engine->algo->seed(state, int_seed);
1480+
UINT128_CON(0ULL, int_seed, state->s);
14511481
}
1482+
1483+
pcg64_seed_full(state);
14521484
}
14531485
}
14541486
/* }}} */

ext/random/random.stub.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function __debugInfo(): array {}
5959

6060
class PCG64 implements Random\SeedableEngine, Random\SerializableEngine
6161
{
62-
public function __construct(string|int|null $seed = null) {}
62+
public function __construct(string|int|null $seed = null, string|int $seequence = 0) {}
6363

6464
/** @implementation-alias Random\Engine\CombinedLCG::generate */
6565
public function generate(): string {}

ext/random/random_arginfo.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: 08434169eadaff969e4cd3b0c3c66b909f45fe81 */
2+
* Stub hash: c1d50aee843582e2bebea3a515f47c28978a2a85 */
33

44
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_lcg_value, 0, 0, IS_DOUBLE, 0)
55
ZEND_END_ARG_INFO()
@@ -63,6 +63,7 @@ ZEND_END_ARG_INFO()
6363

6464
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Random_Engine_PCG64___construct, 0, 0, 0)
6565
ZEND_ARG_TYPE_MASK(0, seed, MAY_BE_STRING|MAY_BE_LONG|MAY_BE_NULL, "null")
66+
ZEND_ARG_TYPE_MASK(0, seequence, MAY_BE_STRING|MAY_BE_LONG, "0")
6667
ZEND_END_ARG_INFO()
6768

6869
#define arginfo_class_Random_Engine_PCG64_generate arginfo_class_Random_Engine_CombinedLCG_generate

ext/random/tests/02_engine/pcg64_seed.phpt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,17 +31,17 @@ for ($i = 0; $i < 1000; $i++) {
3131
--EXPECTF--
3232
Random\Engine\PCG64::__construct(): Argument #1 ($seed) must be of type string|int|null, float given
3333
Random\Engine\PCG64::__construct(): Argument #1 ($seed) state strings must be 16 bytes
34-
object(Random\Engine\PCG64)#%d (%d) {
34+
object(Random\Engine\PCG64)#%d (1) {
3535
["__states"]=>
3636
array(4) {
3737
[0]=>
38-
string(18) "578437695752307201"
38+
string(19) "3130348909074048294"
3939
[1]=>
40-
string(18) "578437695752307201"
40+
string(20) "10673822082922804875"
4141
[2]=>
4242
string(1) "0"
4343
[3]=>
44-
string(1) "0"
44+
string(1) "1"
4545
}
4646
}
47-
string(16) "e68b42d3478fa786"
47+
string(16) "c372f38083fbb0bb"
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
Random: Engine: PCG64: value with inc
3+
--FILE--
4+
<?php
5+
6+
$engine = new \Random\Engine\PCG64(1234, 114514);
7+
8+
for ($i = 0; $i < 10000; $i++) {
9+
$engine->generate();
10+
}
11+
12+
$engine->jump(1234567);
13+
14+
echo \bin2hex($engine->generate());
15+
?>
16+
--EXPECT--
17+
9eac7778c37b81f0

0 commit comments

Comments
 (0)