summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorDmitry Stogov <dmitry@zend.com>2014-07-11 12:28:36 +0400
committerDmitry Stogov <dmitry@zend.com>2014-07-11 12:28:36 +0400
commit8f229b285527a403d46be047546384032a0f6bb3 (patch)
tree52b0f886c4fad779f4f5a1bd59984bd17d452f15 /ext
parentde306e70882dd7dd04ea952ef3c665304837c3be (diff)
downloadphp-git-8f229b285527a403d46be047546384032a0f6bb3.tar.gz
Optimized array_map() and array_combine()
Diffstat (limited to 'ext')
-rw-r--r--ext/standard/array.c243
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(&params[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(&params[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, &params[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(&params[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(&params[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(&params[i]);
+ break;
+ } else if (Z_TYPE(Z_ARRVAL(arrays[i])->arData[pos].val) != IS_UNDEF) {
+ ZVAL_COPY(&params[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(&params[i]);
+ }
+ efree(params);
+ RETURN_NULL();
+ } else {
+ for (i = 0; i < n_arrays; i++) {
+ zval_ptr_dtor(&params[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();
}
/* }}} */