Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions builtin-functions/kphp-light/stdlib/math-functions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ function lcg_value() ::: float;

function random_bytes($length ::: int) ::: string | false;

function random_int($l ::: int, $r ::: int) ::: int | false;

/** @kphp-extern-func-info interruptible */
function uniqid ($prefix ::: string = '', $more_entropy ::: bool = false) ::: string;

Expand All @@ -185,5 +187,3 @@ define('PHP_ROUND_HALF_DOWN', 123423144);
define('PHP_ROUND_HALF_EVEN', 123423145);
define('PHP_ROUND_HALF_ODD', 123423146);

/** @kphp-extern-func-info stub generation-required */
function random_int($l ::: int, $r ::: int) ::: int | false;
44 changes: 44 additions & 0 deletions runtime-light/stdlib/math/random-functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <algorithm>
#include <array>
#include <bit>
#include <chrono>
#include <cstddef>
#include <cstdint>
Expand All @@ -25,6 +26,7 @@
#include "runtime-common/stdlib/math/random-functions.h"
#include "runtime-light/coroutine/task.h"
#include "runtime-light/k2-platform/k2-api.h"
#include "runtime-light/stdlib/diagnostics/logs.h"
#include "runtime-light/stdlib/math/random-state.h"
#include "runtime-light/stdlib/system/system-functions.h"

Expand Down Expand Up @@ -157,6 +159,48 @@ inline Optional<string> f$random_bytes(int64_t length) noexcept {
return str;
}

inline Optional<int64_t> f$random_int(int64_t min, int64_t max) noexcept {
if (min > max) [[unlikely]] {
kphp::log::warning("argument #1 ($min) must be less than or equal to argument #2 ($max)");
return false;
}

if (min == max) {
return min;
}

auto umax{static_cast<uint64_t>(max) - static_cast<uint64_t>(min)};

uint64_t trial{};
if (random_impl_::secure_rand_buf(std::addressof(trial), sizeof(trial)) == -1) [[unlikely]] {
kphp::log::warning("source of randomness cannot be found");
return false;
}

// special case where no modulus is required
if (umax == std::numeric_limits<uint64_t>::max()) {
return static_cast<int64_t>(trial);
}

++umax; // increment the max so the range is inclusive of max

// powers of two are not biased
if (!std::has_single_bit(umax)) {
// ceiling under which UINT64_MAX % max == 0
const auto limit{std::numeric_limits<uint64_t>::max() - (std::numeric_limits<uint64_t>::max() % umax) - 1};

// discard numbers over the limit to avoid modulo bias
while (trial > limit) {
if (random_impl_::secure_rand_buf(std::addressof(trial), sizeof(trial)) == -1) [[unlikely]] {
kphp::log::warning("source of randomness cannot be found");
return false;
}
}
}

return min + static_cast<int64_t>(trial % umax);
}

inline kphp::coro::task<string> f$uniqid(string prefix = string{}, bool more_entropy = false) noexcept {
if (!more_entropy) {
co_await f$usleep(1);
Expand Down
9 changes: 9 additions & 0 deletions tests/phpt/dl/386_random_int.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,15 @@
<?php

function test_random_int() {
$x = random_int(-1, 0);
var_dump($x >= -1 && $x <= 0);

$x = random_int(-1, 1);
var_dump($x >= -1 && $x <= 1);

$x = random_int(0, 1);
var_dump($x >= 0 && $x <= 1);

$x = random_int(100, 500);
var_dump($x >= 100 && $x <= 500);

Expand Down
Loading