]> granicus.if.org Git - php/commitdiff
Minor optimizations to array_keys()/array_values()
authorSara Golemon <pollita@php.net>
Tue, 14 Mar 2017 17:30:49 +0000 (10:30 -0700)
committerSara Golemon <pollita@php.net>
Tue, 14 Mar 2017 18:23:02 +0000 (11:23 -0700)
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
ext/standard/array.c
ext/standard/tests/array/packed_001.phpt [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 7aa4ccfe930006348389e911cbf51e8043dd5121..84cf31d401672cabc91cda2e2c66788fee2342db 100644 (file)
--- 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)
index 8b9ad196e40ce5e938250c018985e703ef48f143..6e94ed67596998a9bc2394319a3e8ecc2325b241 100644 (file)
@@ -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 (file)
index 0000000..81973c7
--- /dev/null
@@ -0,0 +1,84 @@
+--TEST--
+array_keys() and array_values() w/ packed optimization
+--FILE--
+<?php
+
+$x = [1,2,3];
+unset($x[1]);
+
+$inputs = [
+  [],
+  [1,2,3],
+  [0=>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
+)