mirror of
https://github.com/php/php-src.git
synced 2025-08-15 21:48:51 +02:00
random: Use branchless implementation for mask generation in Randomizer::getBytesFromString() (#10522)
* random: Add `max_offset` local to Randomizer::getBytesFromString() * random: Use branchless implementation for mask generation in Randomizer::getBytesFromString() This was benchmarked against clzl with a standalone script with random inputs and is slightly faster. clzl requires an additional branch to handle the source_length = 1 / max_offset = 0 case. * Improve comment for masking in Randomizer::getBytesFromString()
This commit is contained in:
parent
eabb9b7dea
commit
0cfc45b667
2 changed files with 62 additions and 23 deletions
|
@ -388,6 +388,7 @@ PHP_METHOD(Random_Randomizer, getBytesFromString)
|
|||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
const size_t source_length = ZSTR_LEN(source);
|
||||
const size_t max_offset = source_length - 1;
|
||||
|
||||
if (source_length < 1) {
|
||||
zend_argument_value_error(1, "cannot be empty");
|
||||
|
@ -401,9 +402,9 @@ PHP_METHOD(Random_Randomizer, getBytesFromString)
|
|||
|
||||
retval = zend_string_alloc(length, 0);
|
||||
|
||||
if (source_length > 0x100) {
|
||||
if (max_offset > 0xff) {
|
||||
while (total_size < length) {
|
||||
uint64_t offset = randomizer->algo->range(randomizer->status, 0, source_length - 1);
|
||||
uint64_t offset = randomizer->algo->range(randomizer->status, 0, max_offset);
|
||||
|
||||
if (EG(exception)) {
|
||||
zend_string_free(retval);
|
||||
|
@ -413,26 +414,14 @@ PHP_METHOD(Random_Randomizer, getBytesFromString)
|
|||
ZSTR_VAL(retval)[total_size++] = ZSTR_VAL(source)[offset];
|
||||
}
|
||||
} else {
|
||||
uint64_t mask;
|
||||
if (source_length <= 0x1) {
|
||||
mask = 0x0;
|
||||
} else if (source_length <= 0x2) {
|
||||
mask = 0x1;
|
||||
} else if (source_length <= 0x4) {
|
||||
mask = 0x3;
|
||||
} else if (source_length <= 0x8) {
|
||||
mask = 0x7;
|
||||
} else if (source_length <= 0x10) {
|
||||
mask = 0xF;
|
||||
} else if (source_length <= 0x20) {
|
||||
mask = 0x1F;
|
||||
} else if (source_length <= 0x40) {
|
||||
mask = 0x3F;
|
||||
} else if (source_length <= 0x80) {
|
||||
mask = 0x7F;
|
||||
} else {
|
||||
mask = 0xFF;
|
||||
}
|
||||
uint64_t mask = max_offset;
|
||||
// Copy the top-most bit into all lower bits.
|
||||
// Shifting by 4 is sufficient, because max_offset
|
||||
// is guaranteed to fit in an 8-bit integer at this
|
||||
// point.
|
||||
mask |= mask >> 1;
|
||||
mask |= mask >> 2;
|
||||
mask |= mask >> 4;
|
||||
|
||||
int failures = 0;
|
||||
while (total_size < length) {
|
||||
|
@ -445,7 +434,7 @@ PHP_METHOD(Random_Randomizer, getBytesFromString)
|
|||
for (size_t i = 0; i < randomizer->status->last_generated_size; i++) {
|
||||
uint64_t offset = (result >> (i * 8)) & mask;
|
||||
|
||||
if (offset >= source_length) {
|
||||
if (offset > max_offset) {
|
||||
if (++failures > PHP_RANDOM_RANGE_ATTEMPTS) {
|
||||
zend_string_free(retval);
|
||||
zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue