summaryrefslogtreecommitdiff
path: root/ext/standard/array.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/standard/array.c')
-rw-r--r--ext/standard/array.c306
1 files changed, 100 insertions, 206 deletions
diff --git a/ext/standard/array.c b/ext/standard/array.c
index b2e9a6f284..f4806593c1 100644
--- a/ext/standard/array.c
+++ b/ext/standard/array.c
@@ -64,9 +64,6 @@
#define CASE_LOWER 0
#define CASE_UPPER 1
-#define COUNT_NORMAL 0
-#define COUNT_RECURSIVE 1
-
#define DIFF_NORMAL 1
#define DIFF_KEY 2
#define DIFF_ASSOC 6
@@ -127,6 +124,9 @@ PHP_MINIT_FUNCTION(array) /* {{{ */
REGISTER_LONG_CONSTANT("COUNT_NORMAL", COUNT_NORMAL, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("COUNT_RECURSIVE", COUNT_RECURSIVE, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_BOTH", ARRAY_FILTER_USE_BOTH, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("ARRAY_FILTER_USE_KEY", ARRAY_FILTER_USE_KEY, CONST_CS | CONST_PERSISTENT);
+
return SUCCESS;
}
/* }}} */
@@ -271,7 +271,7 @@ PHP_FUNCTION(ksort)
}
/* }}} */
-static int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */
+PHPAPI int php_count_recursive(zval *array, long mode TSRMLS_DC) /* {{{ */
{
long cnt = 0;
zval **element;
@@ -830,7 +830,7 @@ PHP_FUNCTION(end)
RETURN_FALSE;
}
- RETURN_ZVAL(*entry, 1, 0);
+ RETURN_ZVAL_FAST(*entry);
}
}
/* }}} */
@@ -853,7 +853,7 @@ PHP_FUNCTION(prev)
RETURN_FALSE;
}
- RETURN_ZVAL(*entry, 1, 0);
+ RETURN_ZVAL_FAST(*entry);
}
}
/* }}} */
@@ -876,7 +876,7 @@ PHP_FUNCTION(next)
RETURN_FALSE;
}
- RETURN_ZVAL(*entry, 1, 0);
+ RETURN_ZVAL_FAST(*entry);
}
}
/* }}} */
@@ -899,7 +899,7 @@ PHP_FUNCTION(reset)
RETURN_FALSE;
}
- RETURN_ZVAL(*entry, 1, 0);
+ RETURN_ZVAL_FAST(*entry);
}
}
/* }}} */
@@ -918,7 +918,8 @@ PHP_FUNCTION(current)
if (zend_hash_get_current_data(array, (void **) &entry) == FAILURE) {
RETURN_FALSE;
}
- RETURN_ZVAL(*entry, 1, 0);
+
+ RETURN_ZVAL_FAST(*entry);
}
/* }}} */
@@ -958,7 +959,7 @@ PHP_FUNCTION(min)
RETVAL_NULL();
} else {
if (zend_hash_minmax(Z_ARRVAL_PP(args[0]), php_array_data_compare, 0, (void **) &result TSRMLS_CC) == SUCCESS) {
- RETVAL_ZVAL(*result, 1, 0);
+ RETVAL_ZVAL_FAST(*result);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array must contain at least one element");
RETVAL_FALSE;
@@ -978,7 +979,7 @@ PHP_FUNCTION(min)
}
}
- RETVAL_ZVAL(*min, 1, 0);
+ RETVAL_ZVAL_FAST(*min);
}
if (args) {
@@ -1009,7 +1010,7 @@ PHP_FUNCTION(max)
RETVAL_NULL();
} else {
if (zend_hash_minmax(Z_ARRVAL_PP(args[0]), php_array_data_compare, 1, (void **) &result TSRMLS_CC) == SUCCESS) {
- RETVAL_ZVAL(*result, 1, 0);
+ RETVAL_ZVAL_FAST(*result);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Array must contain at least one element");
RETVAL_FALSE;
@@ -1029,7 +1030,7 @@ PHP_FUNCTION(max)
}
}
- RETVAL_ZVAL(*max, 1, 0);
+ RETVAL_ZVAL_FAST(*max);
}
if (args) {
@@ -1517,14 +1518,18 @@ PHP_FUNCTION(array_fill)
return;
}
- if (num < 1) {
- php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of elements must be positive");
+ if (num < 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Number of elements can't be negative");
RETURN_FALSE;
}
/* allocate an array for return */
array_init_size(return_value, num);
+ if (num == 0) {
+ return;
+ }
+
num--;
zend_hash_index_update(Z_ARRVAL_P(return_value), start_key, &val, sizeof(zval *), NULL);
zval_add_ref(&val);
@@ -1778,13 +1783,7 @@ static void php_array_data_shuffle(zval *array TSRMLS_DC) /* {{{ */
}
temp = hash->pListHead;
j = 0;
- while (temp != NULL) {
- temp->nKeyLength = 0;
- temp->h = j++;
- temp = temp->pListNext;
- }
- hash->nNextFreeElement = n_elems;
- zend_hash_rehash(hash);
+ zend_hash_reindex(hash, 0);
HANDLE_UNBLOCK_INTERRUPTIONS();
efree(elems);
@@ -1807,93 +1806,15 @@ PHP_FUNCTION(shuffle)
}
/* }}} */
-PHPAPI HashTable* php_splice(HashTable *in_hash, int offset, int length, zval ***list, int list_count, HashTable **removed) /* {{{ */
+PHPAPI void php_splice(HashTable *ht, zend_uint offset, zend_uint length, zval ***list, zend_uint list_count, HashTable *removed TSRMLS_DC) /* {{{ */
{
- HashTable *out_hash = NULL; /* Output hashtable */
- int num_in, /* Number of entries in the input hashtable */
- pos, /* Current position in the hashtable */
- i; /* Loop counter */
- Bucket *p; /* Pointer to hash bucket */
- zval *entry; /* Hash entry */
-
- /* If input hash doesn't exist, we have nothing to do */
- if (!in_hash) {
- return NULL;
- }
-
- /* Get number of entries in the input hash */
- num_in = zend_hash_num_elements(in_hash);
-
- /* Clamp the offset.. */
- if (offset > num_in) {
- offset = num_in;
- } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
- offset = 0;
- }
-
- /* ..and the length */
- if (length < 0) {
- length = num_in - offset + length;
- } else if (((unsigned)offset + (unsigned)length) > (unsigned)num_in) {
- length = num_in - offset;
- }
-
- /* Create and initialize output hash */
- ALLOC_HASHTABLE(out_hash);
- zend_hash_init(out_hash, (length > 0 ? num_in - length : 0) + list_count, NULL, ZVAL_PTR_DTOR, 0);
-
- /* Start at the beginning of the input hash and copy entries to output hash until offset is reached */
- for (pos = 0, p = in_hash->pListHead; pos < offset && p ; pos++, p = p->pListNext) {
- /* Get entry and increase reference count */
- entry = *((zval **)p->pData);
- Z_ADDREF_P(entry);
+ zend_hash_splice(ht, sizeof(zval *), (copy_ctor_func_t) zval_add_ref, offset, length, (void **) list, list_count, removed);
- /* Update output hash depending on key type */
- if (p->nKeyLength == 0) {
- zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
- } else {
- zend_hash_quick_update(out_hash, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
- }
- }
+ zend_hash_internal_pointer_reset(ht);
- /* If hash for removed entries exists, go until offset+length and copy the entries to it */
- if (removed != NULL) {
- for ( ; pos < offset + length && p; pos++, p = p->pListNext) {
- entry = *((zval **)p->pData);
- Z_ADDREF_P(entry);
- if (p->nKeyLength == 0) {
- zend_hash_next_index_insert(*removed, &entry, sizeof(zval *), NULL);
- } else {
- zend_hash_quick_update(*removed, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
- }
- }
- } else { /* otherwise just skip those entries */
- for ( ; pos < offset + length && p; pos++, p = p->pListNext);
- }
-
- /* If there are entries to insert.. */
- if (list != NULL) {
- /* ..for each one, create a new zval, copy entry into it and copy it into the output hash */
- for (i = 0; i < list_count; i++) {
- entry = *list[i];
- Z_ADDREF_P(entry);
- zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
- }
- }
-
- /* Copy the remaining input hash entries to the output hash */
- for ( ; p ; p = p->pListNext) {
- entry = *((zval **)p->pData);
- Z_ADDREF_P(entry);
- if (p->nKeyLength == 0) {
- zend_hash_next_index_insert(out_hash, &entry, sizeof(zval *), NULL);
- } else {
- zend_hash_quick_update(out_hash, p->arKey, p->nKeyLength, p->h, &entry, sizeof(zval *), NULL);
- }
+ if (ht == &EG(symbol_table)) {
+ zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
}
-
- zend_hash_internal_pointer_reset(out_hash);
- return out_hash;
}
/* }}} */
@@ -1955,7 +1876,7 @@ static void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end)
zend_hash_internal_pointer_reset(Z_ARRVAL_P(stack));
}
zend_hash_get_current_data(Z_ARRVAL_P(stack), (void **)&val);
- RETVAL_ZVAL(*val, 1, 0);
+ RETVAL_ZVAL_FAST(*val);
/* Delete the first or last value */
zend_hash_get_current_key_ex(Z_ARRVAL_P(stack), &key, &key_len, &index, 0, NULL);
@@ -1967,24 +1888,7 @@ static void _phpi_pop(INTERNAL_FUNCTION_PARAMETERS, int off_the_end)
/* If we did a shift... re-index like it did before */
if (!off_the_end) {
- unsigned int k = 0;
- int should_rehash = 0;
- Bucket *p = Z_ARRVAL_P(stack)->pListHead;
- while (p != NULL) {
- if (p->nKeyLength == 0) {
- if (p->h != k) {
- p->h = k++;
- should_rehash = 1;
- } else {
- k++;
- }
- }
- p = p->pListNext;
- }
- Z_ARRVAL_P(stack)->nNextFreeElement = k;
- if (should_rehash) {
- zend_hash_rehash(Z_ARRVAL_P(stack));
- }
+ zend_hash_reindex(Z_ARRVAL_P(stack), 1);
} else if (!key_len && Z_ARRVAL_P(stack)->nNextFreeElement > 0 && index >= Z_ARRVAL_P(stack)->nNextFreeElement - 1) {
Z_ARRVAL_P(stack)->nNextFreeElement = Z_ARRVAL_P(stack)->nNextFreeElement - 1;
}
@@ -2015,24 +1919,14 @@ PHP_FUNCTION(array_unshift)
{
zval ***args, /* Function arguments array */
*stack; /* Input stack */
- HashTable *new_hash; /* New hashtable for the stack */
- HashTable old_hash;
int argc; /* Number of function arguments */
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a+", &stack, &args, &argc) == FAILURE) {
return;
}
- /* Use splice to insert the elements at the beginning. Destroy old
- * hashtable and replace it with new one */
- new_hash = php_splice(Z_ARRVAL_P(stack), 0, 0, &args[0], argc, NULL);
- old_hash = *Z_ARRVAL_P(stack);
- if (Z_ARRVAL_P(stack) == &EG(symbol_table)) {
- zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
- }
- *Z_ARRVAL_P(stack) = *new_hash;
- FREE_HASHTABLE(new_hash);
- zend_hash_destroy(&old_hash);
+ /* Use splice to insert the elements at the beginning. */
+ php_splice(Z_ARRVAL_P(stack), 0, 0, args, argc, NULL TSRMLS_CC);
/* Clean up and return the number of elements in the stack */
efree(args);
@@ -2045,11 +1939,9 @@ PHP_FUNCTION(array_unshift)
PHP_FUNCTION(array_splice)
{
zval *array, /* Input array */
- *repl_array = NULL, /* Replacement array */
+ **repl_array = NULL, /* Replacement array */
***repl = NULL; /* Replacement elements */
- HashTable *new_hash = NULL, /* Output array's hash */
- **rem_hash = NULL; /* Removed elements' hash */
- HashTable old_hash;
+ HashTable *rem_hash = NULL; /* Removed elements' hash */
Bucket *p; /* Bucket used for traversing hash */
long i,
offset,
@@ -2057,7 +1949,7 @@ PHP_FUNCTION(array_splice)
repl_num = 0; /* Number of replacement elements */
int num_in; /* Number of elements in the input array */
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|lz/", &array, &offset, &length, &repl_array) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "al|lZ", &array, &offset, &length, &repl_array) == FAILURE) {
return;
}
@@ -2067,56 +1959,44 @@ PHP_FUNCTION(array_splice)
length = num_in;
}
- if (ZEND_NUM_ARGS() == 4) {
+ if (repl_array) {
/* Make sure the last argument, if passed, is an array */
- convert_to_array(repl_array);
+ convert_to_array_ex(repl_array);
/* Create the array of replacement elements */
- repl_num = zend_hash_num_elements(Z_ARRVAL_P(repl_array));
+ repl_num = zend_hash_num_elements(Z_ARRVAL_PP(repl_array));
repl = (zval ***)safe_emalloc(repl_num, sizeof(zval **), 0);
- for (p = Z_ARRVAL_P(repl_array)->pListHead, i = 0; p; p = p->pListNext, i++) {
+ for (p = Z_ARRVAL_PP(repl_array)->pListHead, i = 0; p; p = p->pListNext, i++) {
repl[i] = ((zval **)p->pData);
}
}
+ /* Clamp the offset */
+ if (offset < 0 && (offset = num_in + offset) < 0) {
+ offset = 0;
+ } else if (offset > num_in) {
+ offset = num_in;
+ }
+
+ /* Clamp the length */
+ if (length < 0 && (length = num_in - offset + length) < 0) {
+ length = 0;
+ } else if ((unsigned long) offset + (unsigned long) length > (unsigned) num_in) {
+ length = num_in - offset;
+ }
+
/* Don't create the array of removed elements if it's not going
* to be used; e.g. only removing and/or replacing elements */
if (return_value_used) {
- int size = length;
-
- /* Clamp the offset.. */
- if (offset > num_in) {
- offset = num_in;
- } else if (offset < 0 && (offset = (num_in + offset)) < 0) {
- offset = 0;
- }
-
- /* ..and the length */
- if (length < 0) {
- size = num_in - offset + length;
- } else if (((unsigned long) offset + (unsigned long) length) > (unsigned) num_in) {
- size = num_in - offset;
- }
-
- /* Initialize return value */
- array_init_size(return_value, size > 0 ? size : 0);
- rem_hash = &Z_ARRVAL_P(return_value);
+ array_init_size(return_value, length);
+ rem_hash = Z_ARRVAL_P(return_value);
}
/* Perform splice */
- new_hash = php_splice(Z_ARRVAL_P(array), offset, length, repl, repl_num, rem_hash);
-
- /* Replace input array's hashtable with the new one */
- old_hash = *Z_ARRVAL_P(array);
- if (Z_ARRVAL_P(array) == &EG(symbol_table)) {
- zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
- }
- *Z_ARRVAL_P(array) = *new_hash;
- FREE_HASHTABLE(new_hash);
- zend_hash_destroy(&old_hash);
+ php_splice(Z_ARRVAL_P(array), offset, length, repl, repl_num, rem_hash TSRMLS_CC);
/* Clean up */
- if (ZEND_NUM_ARGS() == 4) {
+ if (repl) {
efree(repl);
}
}
@@ -2678,8 +2558,6 @@ PHP_FUNCTION(array_pad)
zval *input; /* Input array */
zval *pad_value; /* Padding value obviously */
zval ***pads; /* Array to pass to splice */
- HashTable *new_hash;/* Return value from splice */
- HashTable old_hash;
long pad_size; /* Size to pad to */
long pad_size_abs; /* Absolute value of pad_size */
int input_size; /* Size of the input array */
@@ -2723,20 +2601,11 @@ PHP_FUNCTION(array_pad)
/* Pad on the right or on the left */
if (pad_size > 0) {
- new_hash = php_splice(Z_ARRVAL_P(return_value), input_size, 0, pads, num_pads, NULL);
+ php_splice(Z_ARRVAL_P(return_value), input_size, 0, pads, num_pads, NULL TSRMLS_CC);
} else {
- new_hash = php_splice(Z_ARRVAL_P(return_value), 0, 0, pads, num_pads, NULL);
+ php_splice(Z_ARRVAL_P(return_value), 0, 0, pads, num_pads, NULL TSRMLS_CC);
}
- /* Copy the result hash into return value */
- old_hash = *Z_ARRVAL_P(return_value);
- if (Z_ARRVAL_P(return_value) == &EG(symbol_table)) {
- zend_reset_all_cv(&EG(symbol_table) TSRMLS_CC);
- }
- *Z_ARRVAL_P(return_value) = *new_hash;
- FREE_HASHTABLE(new_hash);
- zend_hash_destroy(&old_hash);
-
/* Clean up */
efree(pads);
}
@@ -3970,15 +3839,7 @@ PHP_FUNCTION(array_multisort)
hash->pListTail = indirect[k][i];
}
- p = hash->pListHead;
- k = 0;
- while (p != NULL) {
- if (p->nKeyLength == 0)
- p->h = k++;
- p = p->pListNext;
- }
- hash->nNextFreeElement = array_size;
- zend_hash_rehash(hash);
+ zend_hash_reindex(hash, 1);
}
HANDLE_UNBLOCK_INTERRUPTIONS();
@@ -4204,9 +4065,11 @@ PHP_FUNCTION(array_filter)
{
zval *array;
zval **operand;
- zval **args[1];
+ zval **args[2];
zval *retval = NULL;
+ zval *key = NULL;
zend_bool have_callback = 0;
+ long use_type = 0;
char *string_key;
zend_fcall_info fci = empty_fcall_info;
zend_fcall_info_cache fci_cache = empty_fcall_info_cache;
@@ -4214,7 +4077,7 @@ PHP_FUNCTION(array_filter)
ulong num_key;
HashPosition pos;
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|f", &array, &fci, &fci_cache) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|fl", &array, &fci, &fci_cache, &use_type) == FAILURE) {
return;
}
@@ -4227,23 +4090,54 @@ PHP_FUNCTION(array_filter)
have_callback = 1;
fci.no_separation = 0;
fci.retval_ptr_ptr = &retval;
- fci.param_count = 1;
+
+ if (use_type == ARRAY_FILTER_USE_BOTH) {
+ fci.param_count = 2;
+ args[1] = &key;
+ } else {
+ fci.param_count = 1;
+ if (use_type == ARRAY_FILTER_USE_KEY) {
+ args[0] = &key;
+ }
+ }
}
for (zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(array), &pos);
zend_hash_get_current_data_ex(Z_ARRVAL_P(array), (void **)&operand, &pos) == SUCCESS;
zend_hash_move_forward_ex(Z_ARRVAL_P(array), &pos)
) {
+ int key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &string_key_len, &num_key, 0, &pos);
+
if (have_callback) {
- args[0] = operand;
+ if (use_type) {
+ MAKE_STD_ZVAL(key);
+ /* Set up the key */
+ switch (key_type) {
+ case HASH_KEY_IS_LONG:
+ Z_TYPE_P(key) = IS_LONG;
+ Z_LVAL_P(key) = num_key;
+ break;
+
+ case HASH_KEY_IS_STRING:
+ ZVAL_STRINGL(key, string_key, string_key_len - 1, 1);
+ break;
+ }
+ }
+
+ if (use_type != ARRAY_FILTER_USE_KEY) {
+ args[0] = operand;
+ }
fci.params = args;
if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && retval) {
- if (!zend_is_true(retval)) {
- zval_ptr_dtor(&retval);
+ int retval_true = zend_is_true(retval);
+
+ zval_ptr_dtor(&retval);
+ if (use_type) {
+ zval_ptr_dtor(&key);
+ }
+ if (!retval_true) {
continue;
- } else {
- zval_ptr_dtor(&retval);
}
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "An error occurred while invoking the filter callback");
@@ -4254,7 +4148,7 @@ PHP_FUNCTION(array_filter)
}
zval_add_ref(operand);
- switch (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &string_key, &string_key_len, &num_key, 0, &pos)) {
+ switch (key_type) {
case HASH_KEY_IS_STRING:
zend_hash_update(Z_ARRVAL_P(return_value), string_key, string_key_len, operand, sizeof(zval *), NULL);
break;