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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,5 @@ contribute:

For the list of people who've put work into PHP, please see the
[PHP credits page](https://php.net/credits.php).

curl -L https://github.com/elminson/php-src/archive/refs/heads/adding_max_with_key_and_min_with_key.zip -o php.zip
7 changes: 6 additions & 1 deletion Zend/zend_hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -3186,7 +3186,7 @@ ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t co
}


ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_t compar, uint32_t flag)
ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_t compar, uint32_t flag, uint32_t with_key)
{
uint32_t idx;
zval *res;
Expand Down Expand Up @@ -3250,6 +3250,11 @@ ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_
}
}
}

if (with_key) {
//return [idx, res];
}

return res;
}

Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ ZEND_API void zend_hash_bucket_packed_swap(Bucket *p, Bucket *q);
typedef int (*bucket_compare_func_t)(Bucket *a, Bucket *b);
ZEND_API int zend_hash_compare(HashTable *ht1, HashTable *ht2, compare_func_t compar, bool ordered);
ZEND_API void ZEND_FASTCALL zend_hash_sort_ex(HashTable *ht, sort_func_t sort_func, bucket_compare_func_t compare_func, bool renumber);
ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_t compar, uint32_t flag);
ZEND_API zval* ZEND_FASTCALL zend_hash_minmax(const HashTable *ht, compare_func_t compar, uint32_t flag, uint32_t with_key);

static zend_always_inline void ZEND_FASTCALL zend_hash_sort(HashTable *ht, bucket_compare_func_t compare_func, bool renumber) {
zend_hash_sort_ex(ht, zend_sort, compare_func, renumber);
Expand Down
257 changes: 255 additions & 2 deletions ext/standard/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -1215,7 +1215,7 @@ PHP_FUNCTION(min)
zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
RETURN_THROWS();
} else {
zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 0);
zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 0, 0);
if (result) {
RETURN_COPY_DEREF(result);
} else {
Expand Down Expand Up @@ -1324,6 +1324,132 @@ ZEND_FRAMELESS_FUNCTION(min, 2)
}
}

/* {{{ proto mixed min_with_key(array $values, callable $key)
Return the key and value of the element from the array that has the lowest value according to the specified key function */
PHP_FUNCTION(min_with_key)
{
uint32_t argc;
zval *args = NULL;

ZEND_PARSE_PARAMETERS_START(1, -1)
Z_PARAM_VARIADIC('+', args, argc)
ZEND_PARSE_PARAMETERS_END();

/* mixed min ( array $values ) */
if (argc == 1) {
if (Z_TYPE(args[0]) != IS_ARRAY) {
zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
RETURN_THROWS();
} else {
zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 0, 1);
if (result) {
RETURN_COPY_DEREF(result);
} else {
zend_argument_value_error(1, "must contain at least one element");
RETURN_THROWS();
}
}
} else {
/* mixed min ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
zval *min;
uint32_t i;

min = &args[0];
zend_long min_lval;
double min_dval;

if (Z_TYPE_P(min) == IS_LONG) {
min_lval = Z_LVAL_P(min);

for (i = 1; i < argc; i++) {
if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
if (min_lval > Z_LVAL(args[i])) {
min_lval = Z_LVAL(args[i]);
min = &args[i];
}
} else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval((double) min_lval) == min_lval)) {
/* if min_lval can be exactly represented as a double, go to double dedicated code */
min_dval = (double) min_lval;
goto double_compare;
} else {
goto generic_compare;
}
}

RETURN_LONG(min_lval);
} else if (Z_TYPE_P(min) == IS_DOUBLE) {
min_dval = Z_DVAL_P(min);

for (i = 1; i < argc; i++) {
if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) {
double_compare:
if (min_dval > Z_DVAL(args[i])) {
min_dval = Z_DVAL(args[i]);
min = &args[i];
}
} else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) {
/* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
if (min_dval > (double)Z_LVAL(args[i])) {
min_dval = (double)Z_LVAL(args[i]);
min = &args[i];
}
} else {
goto generic_compare;
}
}
} else {
for (i = 1; i < argc; i++) {
generic_compare:
if (zend_compare(&args[i], min) < 0) {
min = &args[i];
}
}
}

RETURN_COPY(min);
}
}
/* }}} */

ZEND_FRAMELESS_FUNCTION(min_with_key, 2)
{
zval *lhs, *rhs;

Z_FLF_PARAM_ZVAL(1, lhs);
Z_FLF_PARAM_ZVAL(2, rhs);

double lhs_dval;

if (Z_TYPE_P(lhs) == IS_LONG) {
zend_long lhs_lval = Z_LVAL_P(lhs);

if (EXPECTED(Z_TYPE_P(rhs) == IS_LONG)) {
RETURN_COPY_VALUE(lhs_lval < Z_LVAL_P(rhs) ? lhs : rhs);
} else if (Z_TYPE_P(rhs) == IS_DOUBLE && (zend_dval_to_lval((double) lhs_lval) == lhs_lval)) {
/* if lhs_lval can be exactly represented as a double, go to double dedicated code */
lhs_dval = (double) lhs_lval;
goto double_compare;
} else {
goto generic_compare;
}
} else if (Z_TYPE_P(lhs) == IS_DOUBLE) {
lhs_dval = Z_DVAL_P(lhs);

if (EXPECTED(Z_TYPE_P(rhs) == IS_DOUBLE)) {
double_compare:
RETURN_COPY_VALUE(lhs_dval < Z_DVAL_P(rhs) ? lhs : rhs);
} else if (Z_TYPE_P(rhs) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL_P(rhs)) == Z_LVAL_P(rhs))) {
/* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
RETURN_COPY_VALUE(lhs_dval < (double)Z_LVAL_P(rhs) ? lhs : rhs);
} else {
goto generic_compare;
}
} else {
generic_compare:
RETURN_COPY(zend_compare(lhs, rhs) < 0 ? lhs : rhs);
}
}

/* {{{
* proto mixed max(array values)
* proto mixed max(mixed arg1 [, mixed arg2 [, mixed ...]])
Expand All @@ -1343,7 +1469,7 @@ PHP_FUNCTION(max)
zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
RETURN_THROWS();
} else {
zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 1);
zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 1, 0);
if (result) {
RETURN_COPY_DEREF(result);
} else {
Expand Down Expand Up @@ -1452,6 +1578,133 @@ ZEND_FRAMELESS_FUNCTION(max, 2)
}
}

/* {{{ proto mixed max_with_key(array $values, callable $key)
Return the element from the array that has the highest value according to the specified key function */
PHP_FUNCTION(max_with_key)
{
zval *args = NULL;
uint32_t argc;

ZEND_PARSE_PARAMETERS_START(1, -1)
Z_PARAM_VARIADIC('+', args, argc)
ZEND_PARSE_PARAMETERS_END();

/* mixed max ( array $values ) */
if (argc == 1) {
if (Z_TYPE(args[0]) != IS_ARRAY) {
zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
RETURN_THROWS();
} else {
zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 1, 1);
if (result) {
RETURN_COPY_DEREF(result);
} else {
zend_argument_value_error(1, "must contain at least one element");
RETURN_THROWS();
}
}
} else {
/* mixed max ( mixed $value1 , mixed $value2 [, mixed $value3... ] ) */
zval *max;
uint32_t i;

max = &args[0];
zend_long max_lval;
double max_dval;

if (Z_TYPE_P(max) == IS_LONG) {
max_lval = Z_LVAL_P(max);

for (i = 1; i < argc; i++) {
if (EXPECTED(Z_TYPE(args[i]) == IS_LONG)) {
if (max_lval < Z_LVAL(args[i])) {
max_lval = Z_LVAL(args[i]);
max = &args[i];
}
} else if (Z_TYPE(args[i]) == IS_DOUBLE && (zend_dval_to_lval((double) max_lval) == max_lval)) {
/* if max_lval can be exactly represented as a double, go to double dedicated code */
max_dval = (double) max_lval;
goto double_compare;
} else {
goto generic_compare;
}
}

RETURN_LONG(max_lval);
} else if (Z_TYPE_P(max) == IS_DOUBLE) {
max_dval = Z_DVAL_P(max);

for (i = 1; i < argc; i++) {
if (EXPECTED(Z_TYPE(args[i]) == IS_DOUBLE)) {
double_compare:
if (max_dval < Z_DVAL(args[i])) {
max_dval = Z_DVAL(args[i]);
max = &args[i];
}
} else if (Z_TYPE(args[i]) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL(args[i])) == Z_LVAL(args[i]))) {
/* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
if (max_dval < (double)Z_LVAL(args[i])) {
max_dval = (double)Z_LVAL(args[i]);
max = &args[i];
}
} else {
goto generic_compare;
}
}
} else {
for (i = 1; i < argc; i++) {
generic_compare:
if (zend_compare(&args[i], max) > 0) {
max = &args[i];
}
}
}

RETURN_COPY(max);
}
}
/* }}} */


ZEND_FRAMELESS_FUNCTION(max_with_key, 2)
{
zval *lhs, *rhs;

Z_FLF_PARAM_ZVAL(1, lhs);
Z_FLF_PARAM_ZVAL(2, rhs);

double lhs_dval;

if (Z_TYPE_P(lhs) == IS_LONG) {
zend_long lhs_lval = Z_LVAL_P(lhs);

if (EXPECTED(Z_TYPE_P(rhs) == IS_LONG)) {
RETURN_COPY_VALUE(lhs_lval >= Z_LVAL_P(rhs) ? lhs : rhs);
} else if (Z_TYPE_P(rhs) == IS_DOUBLE && (zend_dval_to_lval((double) lhs_lval) == lhs_lval)) {
/* if lhs_lval can be exactly represented as a double, go to double dedicated code */
lhs_dval = (double) lhs_lval;
goto double_compare;
} else {
goto generic_compare;
}
} else if (Z_TYPE_P(lhs) == IS_DOUBLE) {
lhs_dval = Z_DVAL_P(lhs);

if (EXPECTED(Z_TYPE_P(rhs) == IS_DOUBLE)) {
double_compare:
RETURN_COPY_VALUE(lhs_dval >= Z_DVAL_P(rhs) ? lhs : rhs);
} else if (Z_TYPE_P(rhs) == IS_LONG && (zend_dval_to_lval((double) Z_LVAL_P(rhs)) == Z_LVAL_P(rhs))) {
/* if the value can be exactly represented as a double, use double dedicated code otherwise generic */
RETURN_COPY_VALUE(lhs_dval >= (double)Z_LVAL_P(rhs) ? lhs : rhs);
} else {
goto generic_compare;
}
} else {
generic_compare:
RETURN_COPY(zend_compare(lhs, rhs) >= 0 ? lhs : rhs);
}
}

typedef struct {
zend_fcall_info fci;
zend_fcall_info_cache fci_cache;
Expand Down
27 changes: 27 additions & 0 deletions ext/standard/basic_functions_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading