Skip to content

Commit 360fae2

Browse files
committed
optimize generate size detection
1 parent b74c88d commit 360fae2

File tree

9 files changed

+248
-105
lines changed

9 files changed

+248
-105
lines changed

ext/random/php_random.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@
8383
# define php_random_int_silent(min, max, result) \
8484
php_random_int((min), (max), (result), 0)
8585

86+
# define RANDOM_ENGINE_GENERATE_SIZE(algo, state, value, size) \
87+
do { \
88+
value = algo->generate(state); \
89+
size = algo->static_generate_size != 0 \
90+
? algo->static_generate_size \
91+
: algo->dynamic_generate_size(state) \
92+
;\
93+
} while(0);
94+
8695
PHPAPI double php_combined_lcg(void);
8796

8897
PHPAPI void php_srand(zend_long seed);
@@ -97,8 +106,9 @@ PHPAPI int php_random_bytes(void *bytes, size_t size, bool should_throw);
97106
PHPAPI int php_random_int(zend_long min, zend_long max, zend_long *result, bool should_throw);
98107

99108
typedef struct _php_random_engine_algo {
109+
const size_t static_generate_size;
110+
size_t (*dynamic_generate_size)(void *state);
100111
const size_t state_size;
101-
size_t (*size)(void *state);
102112
uint64_t (*generate)(void *state);
103113
void (*seed)(void *state, const uint64_t seed); /* nullable */
104114
int (*serialize)(void *state, HashTable *data); /* nullable */
@@ -159,6 +169,7 @@ typedef struct _php_random_engine_state_user {
159169
zend_object *object;
160170
zend_function *size_method;
161171
zend_function *generate_method;
172+
size_t last_generate_size;
162173
} php_random_engine_state_user;
163174

164175
static inline php_random_engine *php_random_engine_from_obj(zend_object *obj) {

ext/random/random.c

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -157,13 +157,16 @@ static zend_object_handlers random_randomizer_object_handlers;
157157

158158
static uint32_t rand_range32(const php_random_engine_algo *algo, void *state, uint32_t umax) {
159159
uint32_t result, limit;
160-
size_t generated_size = algo->size(state);
160+
size_t generated_size;
161+
162+
RANDOM_ENGINE_GENERATE_SIZE(algo, state, result, generated_size);
161163

162-
result = algo->generate(state);
163164
while (generated_size < sizeof(uint32_t)) {
164-
size_t generate_size = algo->size(state);
165+
uint32_t ret;
166+
size_t generate_size;
165167

166-
result = (result << generate_size) | algo->generate(state);
168+
RANDOM_ENGINE_GENERATE_SIZE(algo, state, ret, generate_size);
169+
result = (result << generate_size) | ret;
167170

168171
generated_size += generate_size;
169172
}
@@ -186,12 +189,14 @@ static uint32_t rand_range32(const php_random_engine_algo *algo, void *state, ui
186189

187190
/* Discard numbers over the limit to avoid modulo bias */
188191
while (UNEXPECTED(result > limit)) {
189-
generated_size = algo->size(state);
190-
result = algo->generate(state);
192+
RANDOM_ENGINE_GENERATE_SIZE(algo, state, result, generated_size);
191193
while (generated_size < sizeof(uint32_t)) {
192-
size_t generate_size = algo->size(state);
194+
uint32_t ret;
195+
size_t generate_size;
196+
197+
RANDOM_ENGINE_GENERATE_SIZE(algo, state, ret, generate_size);
193198

194-
result = (result << generate_size) | algo->generate(state);
199+
result = (result << generate_size) | ret;
195200

196201
generated_size += generate_size;
197202
}
@@ -203,13 +208,17 @@ static uint32_t rand_range32(const php_random_engine_algo *algo, void *state, ui
203208
#if ZEND_ULONG_MAX > UINT32_MAX
204209
static uint64_t rand_range64(const php_random_engine_algo *algo, void *state, uint64_t umax) {
205210
uint64_t result, limit;
206-
size_t generated_size = algo->size(state);
211+
size_t generated_size;
212+
213+
RANDOM_ENGINE_GENERATE_SIZE(algo, state, result, generated_size);
207214

208-
result = algo->generate(state);
209215
while (generated_size < sizeof(uint64_t)) {
210-
size_t generate_size = algo->size(state);
216+
uint64_t ret;
217+
size_t generate_size;
218+
219+
RANDOM_ENGINE_GENERATE_SIZE(algo, state, ret, generate_size);
211220

212-
result = (result << generate_size) | algo->generate(state);
221+
result = (result << generate_size) | ret;
213222

214223
generated_size += generate_size;
215224
}
@@ -232,13 +241,15 @@ static uint64_t rand_range64(const php_random_engine_algo *algo, void *state, ui
232241

233242
/* Discard numbers over the limit to avoid modulo bias */
234243
while (UNEXPECTED(result > limit)) {
235-
generated_size = algo->size(state);
236-
result = algo->generate(state);
244+
RANDOM_ENGINE_GENERATE_SIZE(algo, state, result, generated_size);
237245

238246
while (generated_size < sizeof(uint64_t)) {
239-
size_t generate_size = algo->size(state);
247+
uint64_t ret;
248+
size_t generate_size;
240249

241-
result = (result << generate_size) | algo->generate(state);
250+
RANDOM_ENGINE_GENERATE_SIZE(algo, state, ret, generate_size);
251+
252+
result = (result << generate_size) | ret;
242253

243254
generated_size += generate_size;
244255
}
@@ -289,7 +300,7 @@ static zend_object *php_random_engine_common_clone_obj(zend_object *old_object)
289300

290301
/* XorShift128Plus begin */
291302

292-
static inline size_t xorshift128plus_size(void *state) {
303+
static inline size_t xorshift128plus_dynamic_generate_size(void *state) {
293304
return sizeof(uint64_t);
294305
}
295306

@@ -362,7 +373,7 @@ static zend_object *php_random_engine_xorshift128plus_new(zend_class_entry *ce)
362373

363374
/* MersenneTwister begin */
364375

365-
static inline size_t mersennetwister_size(void *state) {
376+
static inline size_t mersennetwister_dynamic_generate__size(void *state) {
366377
return sizeof(uint32_t);
367378
}
368379

@@ -484,7 +495,7 @@ static zend_object *php_random_engine_mersennetwister_new(zend_class_entry *ce)
484495

485496
/* CombinedLCG begin */
486497

487-
static inline size_t combinedlcg_size(void *state) {
498+
static inline size_t combinedlcg_dynamic_generate__size(void *state) {
488499
return sizeof(uint32_t);
489500
}
490501

@@ -581,7 +592,7 @@ static zend_object *php_random_engine_combinedlcg_new(zend_class_entry *ce) {
581592

582593
/* Secure begin */
583594

584-
static inline size_t secure_size(void *state) {
595+
static inline size_t secure_dynamic_generate_size(void *state) {
585596
return sizeof(uint64_t);
586597
}
587598

@@ -601,13 +612,10 @@ static zend_object *php_random_engine_secure_new(zend_class_entry *ce) {
601612

602613
/* User begin */
603614

604-
static inline size_t user_size(void *state) {
615+
static inline size_t user_dynamic_generate_size(void *state) {
605616
php_random_engine_state_user *s = (php_random_engine_state_user *) state;
606-
zval retval;
607617

608-
zend_call_known_instance_method_with_0_params(s->size_method, s->object, &retval);
609-
610-
return (size_t) zval_get_long(&retval);
618+
return s->last_generate_size;
611619
}
612620

613621
static uint64_t user_generate(void *state) {
@@ -619,13 +627,11 @@ static uint64_t user_generate(void *state) {
619627

620628
zend_call_known_instance_method_with_0_params(s->generate_method, s->object, &retval);
621629

622-
/* not supported over 64-bit wide currently */
623-
size = user_size(state);
624-
if (size > sizeof(uint64_t)) {
625-
size = sizeof(uint64_t);
626-
}
630+
/* Store generated size in a state */
631+
size = Z_STR(retval)->len;
632+
s->last_generate_size = size;
627633

628-
/* Endianness safe */
634+
/* Endianness safe copy */
629635
char *ptr = (char *) &result;
630636
for (i = 0; i < size; i++) {
631637
ptr[i] = Z_STR(retval)->val[i];
@@ -639,44 +645,49 @@ static uint64_t user_generate(void *state) {
639645
/* User end */
640646

641647
const php_random_engine_algo php_random_engine_algo_xorshift128plus = {
648+
sizeof(uint64_t),
649+
xorshift128plus_dynamic_generate_size,
642650
sizeof(php_random_engine_state_xorshift128plus),
643-
xorshift128plus_size,
644651
xorshift128plus_generate,
645652
xorshift128plus_seed,
646653
xorshift128plus_serialize,
647654
xorshift128plus_unserialize
648655
};
649656

650657
const php_random_engine_algo php_random_engine_algo_mersennetwister = {
658+
sizeof(uint32_t),
659+
mersennetwister_dynamic_generate__size,
651660
sizeof(php_random_engine_state_mersennetwister),
652-
mersennetwister_size,
653661
mersennetwister_generate,
654662
mersennetwister_seed,
655663
mersennetwister_serialize,
656664
mersennetwister_unserialize
657665
};
658666

659667
const php_random_engine_algo php_random_engine_algo_combinedlcg = {
668+
sizeof(uint32_t),
669+
combinedlcg_dynamic_generate__size,
660670
sizeof(php_random_engine_state_combinedlcg),
661-
combinedlcg_size,
662671
combinedlcg_generate,
663672
combinedlcg_seed,
664673
combinedlcg_serialize,
665674
combinedlcg_unserialize
666675
};
667676

668677
const php_random_engine_algo php_random_engine_algo_secure = {
678+
sizeof(uint64_t),
679+
secure_dynamic_generate_size,
669680
0,
670-
secure_size,
671681
secure_generate,
672682
NULL,
673683
NULL,
674684
NULL
675685
};
676686

677687
const php_random_engine_algo php_random_engine_algo_user = {
688+
0, /* does not support static generate size */
689+
user_dynamic_generate_size, /* always use dynamic_generate_size */
678690
sizeof(php_random_engine_state_user),
679-
user_size,
680691
user_generate,
681692
NULL,
682693
NULL,
@@ -988,7 +999,7 @@ PHPAPI zend_long php_random_engine_range(const php_random_engine_algo *algo, voi
988999
{
9891000
zend_ulong umax = max - min;
9901001

991-
if (algo->size(state) >= sizeof(uint64_t)) {
1002+
if (algo->dynamic_generate_size(state) >= sizeof(uint64_t)) {
9921003
return (zend_long) rand_range64(algo, state, umax) + min;
9931004
}
9941005

@@ -1169,7 +1180,7 @@ PHP_METHOD(Random_Engine_XorShift128Plus, __construct)
11691180
if (str_seed) {
11701181
/* char (8 bit) * 16 = 128 bits */
11711182
if (str_seed->len == 16) {
1172-
/* Endianness safe */
1183+
/* Endianness safe copy */
11731184
int i;
11741185
char *ptr = (char *) &state->s;
11751186
for (i = 0; i < 16; i++) {
@@ -1185,17 +1196,6 @@ PHP_METHOD(Random_Engine_XorShift128Plus, __construct)
11851196
}
11861197
/* }}} */
11871198

1188-
/* {{{ Get a generate random number byte size */
1189-
PHP_METHOD(Random_Engine_XorShift128Plus, nextByteSize)
1190-
{
1191-
php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
1192-
1193-
ZEND_PARSE_PARAMETERS_NONE();
1194-
1195-
RETURN_LONG((zend_long) engine->algo->size(engine->state));
1196-
}
1197-
/* }}} */
1198-
11991199
/* {{{ Generate a random number */
12001200
PHP_METHOD(Random_Engine_XorShift128Plus, generate)
12011201
{
@@ -1208,10 +1208,10 @@ PHP_METHOD(Random_Engine_XorShift128Plus, generate)
12081208
ZEND_PARSE_PARAMETERS_NONE();
12091209

12101210
generated = engine->algo->generate(engine->state);
1211-
size = engine->algo->size(engine->state);
1211+
size = engine->algo->dynamic_generate_size(engine->state);
12121212
bytes = zend_string_alloc(size, 0);
12131213

1214-
/* Endianness safe */
1214+
/* Endianness safe copy */
12151215
for (i = 0; i < size; i++) {
12161216
bytes->val[i] = (generated >> (i * 8)) & 0xff;
12171217
}
@@ -1435,11 +1435,11 @@ PHP_METHOD(Random_Randomizer, getBytes)
14351435
ret = zend_string_alloc(length, 0);
14361436

14371437
while (generated_bytes <= length) {
1438-
size_t generated_size = randomizer->algo->size(randomizer->state);
1438+
size_t generated_size = randomizer->algo->dynamic_generate_size(randomizer->state);
14391439

14401440
buf = randomizer->algo->generate(randomizer->state);
14411441
while (generated_size < sizeof(uint64_t)) {
1442-
size_t generate_size = randomizer->algo->size(randomizer->state);
1442+
size_t generate_size = randomizer->algo->dynamic_generate_size(randomizer->state);
14431443

14441444
buf = (buf << generate_size) | randomizer->algo->generate(randomizer->state);
14451445

ext/random/random.stub.php

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ class XorShift128Plus implements Random\Engine
3131
{
3232
public function __construct(string|int $seed) {}
3333

34-
public function nextByteSize(): int {}
35-
3634
public function generate(): string {}
3735

3836
public function __serialize(): array {}
@@ -46,9 +44,6 @@ class MersenneTwister implements Random\Engine
4644
{
4745
public function __construct(int $seed, int $mode = MT_RAND_MT19937) {}
4846

49-
/** @implementation-alias Random\Engine\XorShift128Plus::nextByteSize */
50-
public function nextByteSize(): int {}
51-
5247
/** @implementation-alias Random\Engine\XorShift128Plus::generate */
5348
public function generate(): string {}
5449

@@ -66,9 +61,6 @@ class CombinedLCG implements Random\Engine
6661
{
6762
public function __construct(int $seed) {}
6863

69-
/** @implementation-alias Random\Engine\XorShift128Plus::nextByteSize */
70-
public function nextByteSize(): int {}
71-
7264
/** @implementation-alias Random\Engine\XorShift128Plus::generate */
7365
public function generate(): string {}
7466

@@ -87,9 +79,6 @@ class Secure implements Random\Engine
8779
{
8880
public function __construct() {}
8981

90-
/** @implementation-alias Random\Engine\XorShift128Plus::nextByteSize */
91-
public function nextByteSize(): int {}
92-
9382
/** @implementation-alias Random\Engine\XorShift128Plus::generate */
9483
public function generate(): string {}
9584
}
@@ -99,8 +88,6 @@ public function generate(): string {}
9988
{
10089
interface Engine
10190
{
102-
public function nextByteSize(): int;
103-
10491
public function generate(): string;
10592
}
10693

0 commit comments

Comments
 (0)