Skip to content

Commit b32e0d0

Browse files
committed
Got random_int() seemingly working thanks to @ircmaxell
1 parent aa0ca69 commit b32e0d0

File tree

1 file changed

+26
-34
lines changed

1 file changed

+26
-34
lines changed

ext/standard/random.c

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,20 @@
2121
#include <stdlib.h>
2222
#include <sys/stat.h>
2323
#include <fcntl.h>
24+
#include <math.h>
2425

2526
#include "php.h"
2627

2728
#if PHP_WIN32
2829
# include "win32/winutil.h"
2930
#endif
3031

32+
// Big thanks to @ircmaxell for the help on this bit
33+
union rand_long_buffer {
34+
char buffer[8];
35+
long number;
36+
};
37+
3138
/*
3239
// Copy/pasted from string.c
3340
static char hexconvtab[] = "0123456789abcdef";
@@ -62,6 +69,7 @@ static int php_random_bytes(char *bytes, zend_long size)
6269
}
6370
n = (int)size;
6471
#else
72+
// @todo Need to cache the fd for random_int() call within loop
6573
int fd;
6674
size_t read_bytes = 0;
6775

@@ -152,56 +160,40 @@ PHP_FUNCTION(random_hex)
152160
}
153161
/* }}} */
154162

155-
/* {{{ proto int random_int(int max)
163+
/* {{{ proto int random_int(int maximum)
156164
Return an arbitrary pseudo-random integer */
157165
PHP_FUNCTION(random_int)
158166
{
159-
zend_long max;
167+
zend_long maximum;
160168
zend_long size;
161-
zend_long number = 0;
162-
zend_string *bytes;
163169
size_t i;
164170

165-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &max) == FAILURE) {
171+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &maximum) == FAILURE) {
166172
return;
167173
}
168174

169-
if (max >= INT_MAX) {
170-
php_error_docref(NULL, E_WARNING, "Cannot use max greater than %d", INT_MAX);
175+
if (maximum <= 0 || maximum >= INT_MAX) {
176+
php_error_docref(NULL, E_WARNING, "Cannot use maximum less than 1 or greater than %d", INT_MAX);
171177
RETURN_FALSE;
172178
}
173179

174-
size = sizeof(max);
175-
176-
bytes = zend_string_alloc(size, 0);
180+
long range = (long) maximum; // @todo Support min?
177181

178-
if (php_random_bytes(bytes->val, size) == FAILURE) {
179-
zend_string_release(bytes);
180-
return;
181-
}
182+
// Big thanks to @ircmaxell for the help on this bit
183+
union rand_long_buffer value;
184+
long result;
185+
int bits = (int) (log((double) range) / log(2.0)) + 1;
186+
int bytes = MAX(ceil(bits / 8), 1);
187+
long mask = (long) pow(2.0, (double) bits) - 1;
182188

183-
// @todo bin-to-int: I know this is wrong but don't know how to fix
184-
for (i = 0; i < size; i++) {
185-
unsigned char c = bytes->val[i++];
186-
unsigned char d;
187-
188-
if (c >= '0' && c <= '9') {
189-
d = c - '0';
190-
} else if (c >= 'a' && c <= 'f') {
191-
d = c - 'a' - 10;
192-
} else if (c >= 'A' && c <= 'F') {
193-
d = c - 'A' - 10;
194-
} else {
195-
continue;
189+
do {
190+
if (php_random_bytes(&value.buffer, 8) == FAILURE) {
191+
return;
196192
}
193+
result = value.number & mask;
194+
} while (result > maximum);
197195

198-
// Binary = base-2
199-
number = number * 2 + d;
200-
}
201-
202-
zend_string_release(bytes);
203-
204-
RETURN_LONG(number);
196+
RETURN_LONG(result);
205197
}
206198
/* }}} */
207199

0 commit comments

Comments
 (0)