From c74bc87c74f48bc55541b3bf2fc67d595f58a3b5 Mon Sep 17 00:00:00 2001 From: Sara Golemon Date: Tue, 14 Mar 2017 10:30:49 -0700 Subject: [PATCH] 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) --- NEWS | 1 + ext/standard/array.c | 61 +++++++++++------ ext/standard/tests/array/packed_001.phpt | 84 ++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 19 deletions(-) create mode 100644 ext/standard/tests/array/packed_001.phpt diff --git a/NEWS b/NEWS index 7aa4ccfe93..84cf31d401 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,7 @@ PHP NEWS bugs #53838, #61655, #66173, #70925, #72254, etc. (Andrea) . Raised minimum supported Windows versions to Windows 7/Server 2008 R2. (Anatol) + . Implemented minor optimization in array_keys/array_values(). (Sara) . Fixed bug #73969 (segfault in debug_print_backtrace). (andrewnester) . Added PHP_OS_FAMILY constant to determine on which OS we are. (Jan Altensen) . Fixed bug #73994 (arginfo incorrect for unpack). (krakjoe) diff --git a/ext/standard/array.c b/ext/standard/array.c index 8b9ad196e4..6e94ed6759 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -3938,6 +3938,8 @@ PHP_FUNCTION(array_keys) zend_bool strict = 0; /* do strict comparison */ zend_ulong num_idx; zend_string *str_idx; + zend_array *arrval; + zend_ulong elem_count; ZEND_PARSE_PARAMETERS_START(1, 3) Z_PARAM_ARRAY(input) @@ -3945,13 +3947,20 @@ PHP_FUNCTION(array_keys) Z_PARAM_ZVAL(search_value) Z_PARAM_BOOL(strict) ZEND_PARSE_PARAMETERS_END(); + arrval = Z_ARRVAL_P(input); + elem_count = zend_hash_num_elements(arrval); + + /* Base case: empty input */ + if (!elem_count) { + RETURN_ZVAL(input, 1, 0) + } /* Initialize return array */ if (search_value != NULL) { array_init(return_value); if (strict) { - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) { + ZEND_HASH_FOREACH_KEY_VAL_IND(arrval, num_idx, str_idx, entry) { ZVAL_DEREF(entry); if (fast_is_identical_function(search_value, entry)) { if (str_idx) { @@ -3963,7 +3972,7 @@ PHP_FUNCTION(array_keys) } } ZEND_HASH_FOREACH_END(); } else { - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) { + ZEND_HASH_FOREACH_KEY_VAL_IND(arrval, num_idx, str_idx, entry) { if (fast_equal_check_function(search_value, entry)) { if (str_idx) { ZVAL_STR_COPY(&new_val, str_idx); @@ -3975,21 +3984,27 @@ PHP_FUNCTION(array_keys) } ZEND_HASH_FOREACH_END(); } } else { - array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input))); - if (!zend_hash_num_elements(Z_ARRVAL_P(input))) { - return; - } + array_init_size(return_value, elem_count); zend_hash_real_init(Z_ARRVAL_P(return_value), 1); ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { - /* Go through input array and add keys to the return array */ - ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) { - if (str_idx) { - ZVAL_STR_COPY(&new_val, str_idx); - } else { - ZVAL_LONG(&new_val, num_idx); + if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) { + /* Optimistic case: range(0..n-1) for vector-like packed array */ + zval new_val; + ZVAL_LONG(&new_val, 0); + for (; Z_LVAL(new_val) < elem_count; ++Z_LVAL(new_val)) { + ZEND_HASH_FILL_ADD(&new_val); } - ZEND_HASH_FILL_ADD(&new_val); - } ZEND_HASH_FOREACH_END(); + } else { + /* Go through input array and add keys to the return array */ + ZEND_HASH_FOREACH_KEY_VAL_IND(Z_ARRVAL_P(input), num_idx, str_idx, entry) { + if (str_idx) { + ZVAL_STR_COPY(&new_val, str_idx); + } else { + ZVAL_LONG(&new_val, num_idx); + } + ZEND_HASH_FILL_ADD(&new_val); + } ZEND_HASH_FOREACH_END(); + } } ZEND_HASH_FILL_END(); } } @@ -4001,23 +4016,31 @@ PHP_FUNCTION(array_values) { zval *input, /* Input array */ *entry; /* An entry in the input array */ + zend_array *arrval; ZEND_PARSE_PARAMETERS_START(1, 1) Z_PARAM_ARRAY(input) ZEND_PARSE_PARAMETERS_END(); - /* Initialize return array */ - array_init_size(return_value, zend_hash_num_elements(Z_ARRVAL_P(input))); + arrval = Z_ARRVAL_P(input); - if (!zend_hash_num_elements(Z_ARRVAL_P(input))) { - return; + /* Return empty input as is */ + if (!zend_hash_num_elements(arrval)) { + RETURN_ZVAL(input, 1, 0); } + /* Return vector-like packed arrays as-is */ + if (HT_IS_PACKED(arrval) && HT_IS_WITHOUT_HOLES(arrval)) { + RETURN_ZVAL(input, 1, 0); + } + + /* Initialize return array */ + array_init_size(return_value, zend_hash_num_elements(arrval)); zend_hash_real_init(Z_ARRVAL_P(return_value), 1); /* Go through input array and add values to the return array */ ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { - ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(input), entry) { + ZEND_HASH_FOREACH_VAL(arrval, entry) { if (UNEXPECTED(Z_ISREF_P(entry) && Z_REFCOUNT_P(entry) == 1)) { entry = Z_REFVAL_P(entry); } diff --git a/ext/standard/tests/array/packed_001.phpt b/ext/standard/tests/array/packed_001.phpt new file mode 100644 index 0000000000..81973c7e0e --- /dev/null +++ b/ext/standard/tests/array/packed_001.phpt @@ -0,0 +1,84 @@ +--TEST-- +array_keys() and array_values() w/ packed optimization +--FILE-- +1, 1=>2, 2=>3], + [1=>1, 2=>2, 3=>3], + [0=>1, 2=>3], + $x, +]; + +foreach ($inputs as $input) { + print_r(array_keys($input)); + print_r(array_values($input)); +} +--EXPECT-- +Array +( +) +Array +( +) +Array +( + [0] => 0 + [1] => 1 + [2] => 2 +) +Array +( + [0] => 1 + [1] => 2 + [2] => 3 +) +Array +( + [0] => 0 + [1] => 1 + [2] => 2 +) +Array +( + [0] => 1 + [1] => 2 + [2] => 3 +) +Array +( + [0] => 1 + [1] => 2 + [2] => 3 +) +Array +( + [0] => 1 + [1] => 2 + [2] => 3 +) +Array +( + [0] => 0 + [1] => 2 +) +Array +( + [0] => 1 + [1] => 3 +) +Array +( + [0] => 0 + [1] => 2 +) +Array +( + [0] => 1 + [1] => 3 +) -- 2.50.1