mirror of
https://github.com/php/php-src.git
synced 2024-11-25 10:54:15 +08:00
Optimized array_map() and array_combine()
This commit is contained in:
parent
de306e7088
commit
8f229b2855
@ -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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
/* We iterate through all the arrays at once. */
|
||||
for (k = 0; k < maxlen; k++) {
|
||||
if (n_arrays == 1) {
|
||||
ulong num_key;
|
||||
zend_string *str_key;
|
||||
int key_type = 0;
|
||||
zval *zv;
|
||||
|
||||
/* If no callback, the result will be an array, consisting of current
|
||||
* entries from all arrays. */
|
||||
if (Z_TYPE(arrays[0]) != IS_ARRAY) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Argument #%d should be an array", 2);
|
||||
return;
|
||||
}
|
||||
maxlen = zend_hash_num_elements(Z_ARRVAL(arrays[0]));
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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]);
|
||||
} else {
|
||||
ZVAL_NULL(¶ms[i]);
|
||||
}
|
||||
|
||||
if (!ZEND_FCI_INITIALIZED(fci)) {
|
||||
add_next_index_zval(&result, ¶ms[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (ZEND_FCI_INITIALIZED(fci)) {
|
||||
ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(arrays[0]), num_key, str_key, zv) {
|
||||
fci.retval = &result;
|
||||
fci.param_count = n_arrays;
|
||||
fci.params = params;
|
||||
fci.param_count = 1;
|
||||
fci.params = zv;
|
||||
fci.no_separation = 0;
|
||||
|
||||
if (Z_REFCOUNTED_P(zv)) {
|
||||
Z_ADDREF_P(zv);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
efree(params);
|
||||
zval_ptr_dtor(zv);
|
||||
RETURN_NULL();
|
||||
} else {
|
||||
for (i = 0; i < n_arrays; i++) {
|
||||
zval_ptr_dtor(¶ms[i]);
|
||||
}
|
||||
zval_ptr_dtor(zv);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
if (str_key) {
|
||||
zend_hash_add_new(Z_ARRVAL_P(return_value), str_key, &result);
|
||||
} else {
|
||||
add_index_zval(return_value, num_key, &result);
|
||||
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));
|
||||
|
||||
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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
efree(params);
|
||||
efree(array_len);
|
||||
efree(array_pos);
|
||||
efree(args);
|
||||
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);
|
||||
|
||||
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. */
|
||||
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);
|
||||
}
|
||||
|
||||
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++) {
|
||||
/* 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++;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -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);
|
||||
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);
|
||||
|
||||
zval_add_ref(entry_values);
|
||||
zend_symtable_update(Z_ARRVAL_P(return_value), key, entry_values);
|
||||
|
||||
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();
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user