|
79 | 79 | #define INTERSECT_COMP_DATA_USER 1 |
80 | 80 | #define INTERSECT_COMP_KEY_INTERNAL 0 |
81 | 81 | #define INTERSECT_COMP_KEY_USER 1 |
| 82 | + |
| 83 | +// For array_rand even distribution |
| 84 | +#define BITFIELD_BYTES(bits) ((bits + 7) >> 3) |
| 85 | +#define BITFIELD_SETBIT(field, bit) field[bit >> 3] |= 1 << (bit & 7) |
| 86 | +#define BITFIELD_BITSET(field, bit) ((field[bit >> 3] & (1 << (bit & 7))) != 0) |
| 87 | + |
82 | 88 | /* }}} */ |
83 | 89 |
|
84 | 90 | ZEND_DECLARE_MODULE_GLOBALS(array) |
@@ -5033,56 +5039,76 @@ PHP_FUNCTION(array_rand) |
5033 | 5039 | { |
5034 | 5040 | zval *input; |
5035 | 5041 | zend_long randval, num_req = 1; |
5036 | | -int num_avail; |
5037 | 5042 | zend_string *string_key; |
5038 | 5043 | zend_ulong num_key; |
| 5044 | +int i; |
| 5045 | +int num_avail; |
| 5046 | +char *bitfield; |
| 5047 | +int negative_bitfield = 0; |
5039 | 5048 |
|
5040 | 5049 | if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &input, &num_req) == FAILURE) { |
5041 | 5050 | return; |
5042 | 5051 | } |
5043 | 5052 |
|
5044 | 5053 | num_avail = zend_hash_num_elements(Z_ARRVAL_P(input)); |
5045 | 5054 |
|
5046 | | -if (ZEND_NUM_ARGS() > 1) { |
5047 | | -if (num_req <= 0 || num_req > num_avail) { |
5048 | | -php_error_docref(NULL, E_WARNING, "Second argument has to be between 1 and the number of elements in the array"); |
5049 | | -return; |
5050 | | -} |
5051 | | -} |
5052 | | - |
5053 | | -/* Make the return value an array only if we need to pass back more than one result. */ |
5054 | | -if (num_req > 1) { |
5055 | | -array_init_size(return_value, (uint32_t)num_req); |
| 5055 | +if (num_avail == 0) { |
| 5056 | +php_error_docref(NULL, E_WARNING, "Array is empty"); |
| 5057 | +return; |
5056 | 5058 | } |
5057 | 5059 |
|
5058 | | -/* We can't use zend_hash_index_find() because the array may have string keys or gaps. */ |
5059 | | -ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) { |
5060 | | -if (!num_req) { |
5061 | | -break; |
5062 | | -} |
5063 | | - |
5064 | | -randval = php_rand(); |
| 5060 | +if (num_req == 1) { |
| 5061 | +randval = php_mt_rand_range(0, num_avail - 1); |
5065 | 5062 |
|
5066 | | -if ((double) (randval / PHP_RAND_MAX) <= (double) num_req / (double) num_avail) { |
5067 | | -/* If we are returning a single result, just do it. */ |
5068 | | -if (Z_TYPE_P(return_value) != IS_ARRAY) { |
| 5063 | +ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) { |
| 5064 | +if (randval-- == 0) { |
5069 | 5065 | if (string_key) { |
5070 | 5066 | RETURN_STR_COPY(string_key); |
5071 | 5067 | } else { |
5072 | 5068 | RETURN_LONG(num_key); |
5073 | 5069 | } |
| 5070 | +} |
| 5071 | +} ZEND_HASH_FOREACH_END(); |
| 5072 | +} |
| 5073 | + |
| 5074 | +if (num_req <= 0 || num_req > num_avail) { |
| 5075 | +php_error_docref(NULL, E_WARNING, "Second argument has to be between 1 and the number of elements in the array"); |
| 5076 | +return; |
| 5077 | +} |
| 5078 | + |
| 5079 | +/* Make the return value an array only if we need to pass back more than one result. */ |
| 5080 | +array_init_size(return_value, (uint32_t)num_req); |
| 5081 | +if (num_req > (num_avail >> 1)) { |
| 5082 | +negative_bitfield = 1; |
| 5083 | +num_req = num_avail - num_req; |
| 5084 | +} |
| 5085 | + |
| 5086 | +bitfield = emalloc(BITFIELD_BYTES(num_avail)); |
| 5087 | +memset(bitfield, 0, BITFIELD_BYTES(num_avail)); |
| 5088 | + |
| 5089 | +i = num_req; |
| 5090 | +while (i) { |
| 5091 | +randval = php_mt_rand_range(0, num_avail - 1); |
| 5092 | +if (!BITFIELD_BITSET(bitfield, randval)) { |
| 5093 | +BITFIELD_SETBIT(bitfield, randval); |
| 5094 | +i--; |
| 5095 | +} |
| 5096 | +} |
| 5097 | + |
| 5098 | +/* We can't use zend_hash_index_find() because the array may have string keys or gaps. */ |
| 5099 | +i = 0; |
| 5100 | +ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(input), num_key, string_key) { |
| 5101 | +if (BITFIELD_BITSET(bitfield, i) ^ negative_bitfield) { |
| 5102 | +if (string_key) { |
| 5103 | +add_next_index_str(return_value, zend_string_copy(string_key)); |
5074 | 5104 | } else { |
5075 | | -/* Append the result to the return value. */ |
5076 | | -if (string_key) { |
5077 | | -add_next_index_str(return_value, zend_string_copy(string_key)); |
5078 | | -} else { |
5079 | | -add_next_index_long(return_value, num_key); |
5080 | | -} |
| 5105 | +add_next_index_long(return_value, num_key); |
5081 | 5106 | } |
5082 | | -num_req--; |
5083 | 5107 | } |
5084 | | -num_avail--; |
| 5108 | +i++; |
5085 | 5109 | } ZEND_HASH_FOREACH_END(); |
| 5110 | + |
| 5111 | +efree(bitfield); |
5086 | 5112 | } |
5087 | 5113 | /* }}} */ |
5088 | 5114 |
|
|
0 commit comments