diff options
author | Dmitry Stogov <dmitry@zend.com> | 2014-07-11 12:28:36 +0400 |
---|---|---|
committer | Dmitry Stogov <dmitry@zend.com> | 2014-07-11 12:28:36 +0400 |
commit | 8f229b285527a403d46be047546384032a0f6bb3 (patch) | |
tree | 52b0f886c4fad779f4f5a1bd59984bd17d452f15 /ext | |
parent | de306e70882dd7dd04ea952ef3c665304837c3be (diff) | |
download | php-git-8f229b285527a403d46be047546384032a0f6bb3.tar.gz |
Optimized array_map() and array_combine()
Diffstat (limited to 'ext')
-rw-r--r-- | ext/standard/array.c | 243 |
1 files changed, 136 insertions, 107 deletions
diff --git a/ext/standard/array.c b/ext/standard/array.c index 06f04aed2c..9c2176652f 100644 --- a/ext/standard/array.c +++ b/ext/standard/array.c @@ -4313,14 +4313,10 @@ PHP_FUNCTION(array_map) { zval *arrays = NULL; int n_arrays = 0; - zval *params; zval result; - HashPosition *array_pos; - zval **args; zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fci_cache = empty_fcall_info_cache; int i, k, maxlen = 0; - int *array_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!+", &fci, &fci_cache, &arrays, &n_arrays) == FAILURE) { return; @@ -4328,114 +4324,145 @@ PHP_FUNCTION(array_map) RETVAL_NULL(); - args = (zval **)safe_emalloc(n_arrays, sizeof(zval *), 0); - array_len = (int *)safe_emalloc(n_arrays, sizeof(int), 0); - array_pos = (HashPosition *)safe_emalloc(n_arrays, sizeof(HashPosition), 0); + if (n_arrays == 1) { + ulong num_key; + zend_string *str_key; + zval *zv; - for (i = 0; i < n_arrays; i++) { - if (Z_TYPE(arrays[i]) != IS_ARRAY) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d should be an array", i + 2); - efree(args); - efree(array_len); - efree(array_pos); + if (Z_TYPE(arrays[0]) != IS_ARRAY) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d should be an array", 2); return; } - SEPARATE_ZVAL_IF_NOT_REF(&arrays[i]); - args[i] = &arrays[i]; - array_len[i] = zend_hash_num_elements(Z_ARRVAL(arrays[i])); - if (array_len[i] > maxlen) { - maxlen = array_len[i]; - } - zend_hash_internal_pointer_reset_ex(Z_ARRVAL(arrays[i]), &array_pos[i]); - } - - - /* Short-circuit: if no callback and only one array, just return it. */ - if (!ZEND_FCI_INITIALIZED(fci) && n_arrays == 1) { - RETVAL_ZVAL(args[0], 1, 0); - efree(array_len); - efree(array_pos); - efree(args); - return; - } - - array_init_size(return_value, maxlen); - params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0); + maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0])); - /* We iterate through all the arrays at once. */ - for (k = 0; k < maxlen; k++) { - ulong num_key; - zend_string *str_key; - int key_type = 0; - - /* If no callback, the result will be an array, consisting of current - * entries from all arrays. */ + /* Short-circuit: if no callback and only one array, just return it. */ if (!ZEND_FCI_INITIALIZED(fci)) { - array_init_size(&result, n_arrays); + RETVAL_ZVAL(&arrays[0], 1, 0); + return; } - for (i = 0; i < n_arrays; i++) { - /* If this array still has elements, add the current one to the - * parameter list, otherwise use null value. */ - if (k < array_len[i]) { - zval *zv = zend_hash_get_current_data_ex(Z_ARRVAL_P(args[i]), &array_pos[i]); + array_init_size(return_value, maxlen); - ZVAL_COPY(¶ms[i], zv); + ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(arrays[0]), num_key, str_key, zv) { + fci.retval = &result; + fci.param_count = 1; + fci.params = zv; + fci.no_separation = 0; + + if (Z_REFCOUNTED_P(zv)) { + Z_ADDREF_P(zv); + } - /* It is safe to store only last value of key type, because - * this loop will run just once if there is only 1 array. */ - if (n_arrays == 1) { - key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(args[0]), &str_key, &num_key, 0, &array_pos[i]); - } - zend_hash_move_forward_ex(Z_ARRVAL_P(args[i]), &array_pos[i]); + if (zend_call_function(&fci, &fci_cache TSRMLS_CC) != SUCCESS || Z_TYPE(result) == IS_UNDEF) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the map callback"); + zval_dtor(return_value); + zval_ptr_dtor(zv); + RETURN_NULL(); + } else { + zval_ptr_dtor(zv); + } + if (str_key) { + zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, &result); } else { - ZVAL_NULL(¶ms[i]); + zend_hash_index_add_new(Z_ARRVAL_P(return_value), num_key, &result); } + } ZEND_HASH_FOREACH_END(); + } else { + zend_uint *array_pos = (HashPosition *)ecalloc(n_arrays, sizeof(HashPosition)); - if (!ZEND_FCI_INITIALIZED(fci)) { - add_next_index_zval(&result, ¶ms[i]); + for (i = 0; i < n_arrays; i++) { + if (Z_TYPE(arrays[i]) != IS_ARRAY) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d should be an array", i + 2); + efree(array_pos); + return; + } + if (zend_hash_num_elements(Z_ARRVAL(arrays[i])) > maxlen) { + maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[i])); } } - if (ZEND_FCI_INITIALIZED(fci)) { - fci.retval = &result; - fci.param_count = n_arrays; - fci.params = params; - fci.no_separation = 0; + array_init_size(return_value, maxlen); + + if (!ZEND_FCI_INITIALIZED(fci)) { + zval zv; + + /* We iterate through all the arrays at once. */ + for (k = 0; k < maxlen; k++) { + + /* If no callback, the result will be an array, consisting of current + * entries from all arrays. */ + array_init_size(&result, n_arrays); - if (zend_call_function(&fci, &fci_cache TSRMLS_CC) != SUCCESS || Z_TYPE(result) == IS_UNDEF) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the map callback"); - efree(array_len); - efree(args); - efree(array_pos); - zval_dtor(return_value); for (i = 0; i < n_arrays; i++) { - zval_ptr_dtor(¶ms[i]); + /* If this array still has elements, add the current one to the + * parameter list, otherwise use null value. */ + zend_uint pos = array_pos[i]; + while (1) { + if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) { + ZVAL_NULL(&zv); + break; + } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) { + ZVAL_COPY(&zv, &Z_ARRVAL(arrays[i])->arData[pos].val); + array_pos[i] = pos + 1; + break; + } + pos++; + } + + zend_hash_next_index_insert_new(Z_ARRVAL(result), &zv); } - efree(params); - RETURN_NULL(); - } else { + + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result); + } + } else { + zval *params = (zval *)safe_emalloc(n_arrays, sizeof(zval), 0); + + /* We iterate through all the arrays at once. */ + for (k = 0; k < maxlen; k++) { for (i = 0; i < n_arrays; i++) { - zval_ptr_dtor(¶ms[i]); + /* If this array still has elements, add the current one to the + * parameter list, otherwise use null value. */ + zend_uint pos = array_pos[i]; + while (1) { + if (pos >= Z_ARRVAL(arrays[i])->nNumUsed) { + ZVAL_NULL(¶ms[i]); + break; + } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) { + ZVAL_COPY(¶ms[i], &Z_ARRVAL(arrays[i])->arData[pos].val); + array_pos[i] = pos + 1; + break; + } + pos++; + } } - } - } - if (n_arrays > 1) { - add_next_index_zval(return_value, &result); - } else { - if (key_type == HASH_KEY_IS_STRING) { - zend_hash_update(Z_ARRVAL_P(return_value), str_key, &result); - } else { - add_index_zval(return_value, num_key, &result); + fci.retval = &result; + fci.param_count = n_arrays; + fci.params = params; + fci.no_separation = 0; + + if (zend_call_function(&fci, &fci_cache TSRMLS_CC) != SUCCESS || Z_TYPE(result) == IS_UNDEF) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the map callback"); + efree(array_pos); + zval_dtor(return_value); + for (i = 0; i < n_arrays; i++) { + zval_ptr_dtor(¶ms[i]); + } + efree(params); + RETURN_NULL(); + } else { + for (i = 0; i < n_arrays; i++) { + zval_ptr_dtor(¶ms[i]); + } + } + + zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &result); } + + efree(params); } + efree(array_pos); } - - efree(params); - efree(array_len); - efree(array_pos); - efree(args); } /* }}} */ @@ -4545,7 +4572,7 @@ PHP_FUNCTION(array_chunk) PHP_FUNCTION(array_combine) { zval *values, *keys; - HashPosition pos_values, pos_keys; + zend_uint pos_values = 0; zval *entry_keys, *entry_values; int num_keys, num_values; @@ -4567,26 +4594,28 @@ PHP_FUNCTION(array_combine) return; } - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos_keys); - zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(values), &pos_values); - while ((entry_keys = zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), &pos_keys)) != NULL && - (entry_values = zend_hash_get_current_data_ex(Z_ARRVAL_P(values), &pos_values)) != NULL - ) { - if (Z_TYPE_P(entry_keys) == IS_LONG) { - zval_add_ref(entry_values); - add_index_zval(return_value, Z_LVAL_P(entry_keys), entry_values); - } else { - zend_string *key = zval_get_string(entry_keys); - - zval_add_ref(entry_values); - zend_symtable_update(Z_ARRVAL_P(return_value), key, entry_values); + ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(keys), entry_keys) { + while (1) { + if (pos_values >= Z_ARRVAL_P(values)->nNumUsed) { + break; + } else if (Z_TYPE(Z_ARRVAL_P(values)->arData[pos_values].val) != IS_UNDEF) { + entry_values = &Z_ARRVAL_P(values)->arData[pos_values].val; + if (Z_TYPE_P(entry_keys) == IS_LONG) { + zval_add_ref(entry_values); + add_index_zval(return_value, Z_LVAL_P(entry_keys), entry_values); + } else { + zend_string *key = zval_get_string(entry_keys); - STR_RELEASE(key); + zval_add_ref(entry_values); + zend_symtable_update(Z_ARRVAL_P(return_value), key, entry_values); + STR_RELEASE(key); + } + pos_values++; + break; + } + pos_values++; } - - zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos_keys); - zend_hash_move_forward_ex(Z_ARRVAL_P(values), &pos_values); - } + } ZEND_HASH_FOREACH_END(); } /* }}} */ |