Skip to content

Commit c74bc87

Browse files
committed
Minor optimizations to array_keys()/array_values()
array_values(): When the input is an empty array or a packed array with no gaps, return the original array. array_keys(): When the input is an empty array, return the original array. When the input is a packed array with no holes (and no search key specified), populate the return with a simple range(0, count($input) - 1)
1 parent 16ae9f8 commit c74bc87

File tree

3 files changed

+127
-19
lines changed

3 files changed

+127
-19
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ PHP NEWS
2222
bugs #53838, #61655, #66173, #70925, #72254, etc. (Andrea)
2323
. Raised minimum supported Windows versions to Windows 7/Server 2008 R2.
2424
(Anatol)
25+
. Implemented minor optimization in array_keys/array_values(). (Sara)
2526
. Fixed bug #73969 (segfault in debug_print_backtrace). (andrewnester)
2627
. Added PHP_OS_FAMILY constant to determine on which OS we are. (Jan Altensen)
2728
. Fixed bug #73994 (arginfo incorrect for unpack). (krakjoe)

ext/standard/array.c

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3938,20 +3938,29 @@ PHP_FUNCTION(array_keys)
39383938
zend_bool strict = 0; /* do strict comparison */
39393939
zend_ulong num_idx;
39403940
zend_string *str_idx;
3941+
zend_array *arrval;
3942+
zend_ulong elem_count;
39413943

39423944
ZEND_PARSE_PARAMETERS_START(1, 3)
39433945
Z_PARAM_ARRAY(input)
39443946
Z_PARAM_OPTIONAL
39453947
Z_PARAM_ZVAL(search_value)
39463948
Z_PARAM_BOOL(strict)
39473949
ZEND_PARSE_PARAMETERS_END();
3950+
arrval = Z_ARRVAL_P(input);
3951+
elem_count = zend_hash_num_elements(arrval);
3952+
3953+
/* Base case: empty input */
3954+
if (!elem_count) {
3955+
RETURN_ZVAL(input, 1, 0)
3956+
}
39483957

39493958
/* Initialize return array */
39503959
if (search_value != NULL) {
39513960
array_init(return_value);
39523961

39533962
if (strict) {
3954-
ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
3963+
ZEND_HASH_FOREACH_KEY_VAL_IND(arrval, num_idx, str_idx, entry) {
39553964
ZVAL_DEREF(entry);
39563965
if (fast_is_identical_function(search_value, entry)) {
39573966
if (str_idx) {
@@ -3963,7 +3972,7 @@ PHP_FUNCTION(array_keys)
39633972
}
39643973
} ZEND_HASH_FOREACH_END();
39653974
} else {
3966-
ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
3975+
ZEND_HASH_FOREACH_KEY_VAL_IND(arrval, num_idx, str_idx, entry) {
39673976
if (fast_equal_check_function(search_value, entry)) {
39683977
if (str_idx) {
39693978
ZVAL_STR_COPY(&new_val, str_idx);
@@ -3975,21 +3984,27 @@ PHP_FUNCTION(array_keys)
39753984
} ZEND_HASH_FOREACH_END();
39763985
}
39773986
} else {
3978-
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
3979-
if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
3980-
return;
3981-
}
3987+
array_init_size(return_value, elem_count);
39823988
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
39833989
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
3984-
/* Go through input array and add keys to the return array */
3985-
ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
3986-
if (str_idx) {
3987-
ZVAL_STR_COPY(&new_val, str_idx);
3988-
} else {
3989-
ZVAL_LONG(&new_val, num_idx);
3990+
if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
3991+
/* Optimistic case: range(0..n-1) for vector-like packed array */
3992+
zval new_val;
3993+
ZVAL_LONG(&new_val, 0);
3994+
for (; Z_LVAL(new_val) < elem_count; ++Z_LVAL(new_val)) {
3995+
ZEND_HASH_FILL_ADD(&new_val);
39903996
}
3991-
ZEND_HASH_FILL_ADD(&new_val);
3992-
} ZEND_HASH_FOREACH_END();
3997+
} else {
3998+
/* Go through input array and add keys to the return array */
3999+
ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) {
4000+
if (str_idx) {
4001+
ZVAL_STR_COPY(&new_val, str_idx);
4002+
} else {
4003+
ZVAL_LONG(&new_val, num_idx);
4004+
}
4005+
ZEND_HASH_FILL_ADD(&new_val);
4006+
} ZEND_HASH_FOREACH_END();
4007+
}
39934008
} ZEND_HASH_FILL_END();
39944009
}
39954010
}
@@ -4001,23 +4016,31 @@ PHP_FUNCTION(array_values)
40014016
{
40024017
zval *input, /* Input array */
40034018
*entry; /* An entry in the input array */
4019+
zend_array *arrval;
40044020

40054021
ZEND_PARSE_PARAMETERS_START(1, 1)
40064022
Z_PARAM_ARRAY(input)
40074023
ZEND_PARSE_PARAMETERS_END();
40084024

4009-
/* Initialize return array */
4010-
array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input)));
4025+
arrval = Z_ARRVAL_P(input);
40114026

4012-
if (!zend_hash_num_elements(Z_ARRVAL_P(input))) {
4013-
return;
4027+
/* Return empty input as is */
4028+
if (!zend_hash_num_elements(arrval)) {
4029+
RETURN_ZVAL(input, 1, 0);
40144030
}
40154031

4032+
/* Return vector-like packed arrays as-is */
4033+
if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) {
4034+
RETURN_ZVAL(input, 1, 0);
4035+
}
4036+
4037+
/* Initialize return array */
4038+
array_init_size(return_value, zend_hash_num_elements(arrval));
40164039
zend_hash_real_init(Z_ARRVAL_P(return_value), 1);
40174040

40184041
/* Go through input array and add values to the return array */
40194042
ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
4020-
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) {
4043+
ZEND_HASH_FOREACH_VAL(arrval, entry) {
40214044
if (UNEXPECTED(Z_ISREF_P(entry) && Z_REFCOUNT_P(entry) == 1)) {
40224045
entry = Z_REFVAL_P(entry);
40234046
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
--TEST--
2+
array_keys() and array_values() w/ packed optimization
3+
--FILE--
4+
<?php
5+
6+
$x = [1,2,3];
7+
unset($x[1]);
8+
9+
$inputs = [
10+
[],
11+
[1,2,3],
12+
[0=>1, 1=>2, 2=>3],
13+
[1=>1, 2=>2, 3=>3],
14+
[0=>1, 2=>3],
15+
$x,
16+
];
17+
18+
foreach ($inputs as $input) {
19+
print_r(array_keys($input));
20+
print_r(array_values($input));
21+
}
22+
--EXPECT--
23+
Array
24+
(
25+
)
26+
Array
27+
(
28+
)
29+
Array
30+
(
31+
[0] => 0
32+
[1] => 1
33+
[2] => 2
34+
)
35+
Array
36+
(
37+
[0] => 1
38+
[1] => 2
39+
[2] => 3
40+
)
41+
Array
42+
(
43+
[0] => 0
44+
[1] => 1
45+
[2] => 2
46+
)
47+
Array
48+
(
49+
[0] => 1
50+
[1] => 2
51+
[2] => 3
52+
)
53+
Array
54+
(
55+
[0] => 1
56+
[1] => 2
57+
[2] => 3
58+
)
59+
Array
60+
(
61+
[0] => 1
62+
[1] => 2
63+
[2] => 3
64+
)
65+
Array
66+
(
67+
[0] => 0
68+
[1] => 2
69+
)
70+
Array
71+
(
72+
[0] => 1
73+
[1] => 3
74+
)
75+
Array
76+
(
77+
[0] => 0
78+
[1] => 2
79+
)
80+
Array
81+
(
82+
[0] => 1
83+
[1] => 3
84+
)

0 commit comments

Comments
 (0)