Skip to content

Commit 0f5c8a6

Browse files
Specialize min and max for long arrays
1 parent 0a03d10 commit 0f5c8a6

2 files changed

Lines changed: 119 additions & 2 deletions

File tree

ext/standard/array.c

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,6 +1095,69 @@ static int php_data_compare(const void *f, const void *s) /* {{{ */
10951095
}
10961096
/* }}} */
10971097

1098+
static zend_always_inline bool php_array_minmax_long(HashTable *array, zval *return_value, bool max) /* {{{ */
1099+
{
1100+
zval *entry, *result = NULL;
1101+
zend_long result_lval;
1102+
bool long_mode = true;
1103+
1104+
ZEND_HASH_FOREACH_VAL(array, entry) {
1105+
zval *value = entry;
1106+
zend_long value_lval;
1107+
1108+
ZVAL_DEREF(value);
1109+
if (!result) {
1110+
if (Z_TYPE_P(value) != IS_LONG) {
1111+
return false;
1112+
}
1113+
1114+
result = value;
1115+
result_lval = Z_LVAL_P(value);
1116+
continue;
1117+
}
1118+
1119+
if (long_mode && EXPECTED(Z_TYPE_P(value) == IS_LONG)) {
1120+
value_lval = Z_LVAL_P(value);
1121+
if (max) {
1122+
if (result_lval < value_lval) {
1123+
result = value;
1124+
result_lval = value_lval;
1125+
}
1126+
} else {
1127+
if (result_lval > value_lval) {
1128+
result = value;
1129+
result_lval = value_lval;
1130+
}
1131+
}
1132+
continue;
1133+
}
1134+
1135+
long_mode = false;
1136+
1137+
if (max) {
1138+
if (php_data_compare(result, value) < 0) {
1139+
result = value;
1140+
}
1141+
} else {
1142+
if (php_data_compare(result, value) > 0) {
1143+
result = value;
1144+
}
1145+
}
1146+
} ZEND_HASH_FOREACH_END();
1147+
1148+
if (!result) {
1149+
return false;
1150+
}
1151+
1152+
if (long_mode) {
1153+
ZVAL_LONG(return_value, result_lval);
1154+
} else {
1155+
ZVAL_COPY_DEREF(return_value, result);
1156+
}
1157+
return true;
1158+
}
1159+
/* }}} */
1160+
10981161
/* {{{
10991162
* proto mixed min(array values)
11001163
* proto mixed min(mixed arg1 [, mixed arg2 [, mixed ...]])
@@ -1114,7 +1177,12 @@ PHP_FUNCTION(min)
11141177
zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
11151178
RETURN_THROWS();
11161179
} else {
1117-
zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 0);
1180+
HashTable *array = Z_ARRVAL(args[0]);
1181+
if (php_array_minmax_long(array, return_value, false)) {
1182+
return;
1183+
}
1184+
1185+
zval *result = zend_hash_minmax(array, php_data_compare, 0);
11181186
if (result) {
11191187
RETURN_COPY_DEREF(result);
11201188
} else {
@@ -1242,7 +1310,12 @@ PHP_FUNCTION(max)
12421310
zend_argument_type_error(1, "must be of type array, %s given", zend_zval_value_name(&args[0]));
12431311
RETURN_THROWS();
12441312
} else {
1245-
zval *result = zend_hash_minmax(Z_ARRVAL(args[0]), php_data_compare, 1);
1313+
HashTable *array = Z_ARRVAL(args[0]);
1314+
if (php_array_minmax_long(array, return_value, true)) {
1315+
return;
1316+
}
1317+
1318+
zval *result = zend_hash_minmax(array, php_data_compare, 1);
12461319
if (result) {
12471320
RETURN_COPY_DEREF(result);
12481321
} else {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
--TEST--
2+
min() and max() array long fast path preserves comparison behavior
3+
--FILE--
4+
<?php
5+
6+
$cases = [];
7+
8+
$cases["packed"] = [4, 1, 7, -3, 2];
9+
10+
$sparse = [4, 1, 7];
11+
unset($sparse[1]);
12+
$sparse[5] = -2;
13+
$cases["sparse"] = $sparse;
14+
15+
$a = 4;
16+
$b = 9;
17+
$cases["refs"] = [&$a, &$b, 3];
18+
19+
$cases["first non-long"] = ["5", 3, 4];
20+
$cases["fallback after longs"] = [5, 4, "3", 2];
21+
22+
foreach ($cases as $name => $values) {
23+
echo "-- $name --\n";
24+
var_dump(min($values));
25+
var_dump(max($values));
26+
}
27+
28+
?>
29+
--EXPECT--
30+
-- packed --
31+
int(-3)
32+
int(7)
33+
-- sparse --
34+
int(-2)
35+
int(7)
36+
-- refs --
37+
int(3)
38+
int(9)
39+
-- first non-long --
40+
int(3)
41+
string(1) "5"
42+
-- fallback after longs --
43+
int(2)
44+
int(5)

0 commit comments

Comments
 (0)